Internationalize "n/a" library versions
[libisds.git] / src / isds.c
blob85f4c84be5361809fe105a692cdca0039a8ac602
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 /* Send log-in request */
1429 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1431 if (context->otp) {
1432 /* Revert context URL from OTP authentication service URL to OTP web
1433 * service base URL for subsequent calls. Potenial isds_login() retry
1434 * will re-set context URL again. */
1435 zfree(context->url);
1436 context->url = _isds_astrcat(url, "apps/");
1437 if (context->url == NULL) {
1438 soap_err = IE_NOMEM;
1440 /* Detach pointer to OTP credentials from context */
1441 context->otp_credentials = NULL;
1444 /* Remove credentials */
1445 _isds_discard_credentials(context, 0);
1447 /* Destroy log-in request */
1448 xmlFreeNode(request);
1450 if (soap_err) {
1451 xmlFreeNodeList(response);
1452 _isds_close_connection(context);
1453 return soap_err;
1456 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1457 * authentication succeeded if soap_err == IE_SUCCESS */
1458 err = IE_SUCCESS;
1460 xmlFreeNodeList(response);
1462 if (!err)
1463 isds_log(ILF_ISDS, ILL_DEBUG,
1464 _("User %s has been logged into server %s successfully\n"),
1465 username, url);
1466 return err;
1467 #else /* not HAVE_LIBCURL */
1468 return IE_NOTSUP;
1469 #endif
1473 /* Log out from ISDS server discards credentials and connection configuration. */
1474 isds_error isds_logout(struct isds_ctx *context) {
1475 if (!context) return IE_INVALID_CONTEXT;
1476 zfree(context->long_message);
1478 #if HAVE_LIBCURL
1479 if (context->curl) {
1480 if (context->otp) {
1481 isds_error err = _isds_invalidate_otp_cookie(context);
1482 if (err) return err;
1485 /* Close connection */
1486 _isds_close_connection(context);
1488 /* Discard credentials for sure. They should not survive isds_login(),
1489 * even successful .*/
1490 _isds_discard_credentials(context, 1);
1491 zfree(context->url);
1493 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1494 } else {
1495 _isds_discard_credentials(context, 1);
1497 return IE_SUCCESS;
1498 #else /* not HAVE_LIBCURL */
1499 return IE_NOTSUP;
1500 #endif
1504 /* Verify connection to ISDS is alive and server is responding.
1505 * Send dummy request to ISDS and expect dummy response. */
1506 isds_error isds_ping(struct isds_ctx *context) {
1507 #if HAVE_LIBCURL
1508 isds_error soap_err;
1509 xmlNsPtr isds_ns = NULL;
1510 xmlNodePtr request = NULL;
1511 xmlNodePtr response = NULL;
1512 #endif /* HAVE_LIBCURL */
1514 if (!context) return IE_INVALID_CONTEXT;
1515 zfree(context->long_message);
1517 #if HAVE_LIBCURL
1518 /* Check if connection is established */
1519 if (!context->curl) return IE_CONNECTION_CLOSED;
1522 /* Build dummy request */
1523 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1524 if (!request) {
1525 isds_log_message(context, _("Could build ISDS dummy request"));
1526 return IE_ERROR;
1528 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1529 if(!isds_ns) {
1530 isds_log_message(context, _("Could not create ISDS name space"));
1531 xmlFreeNode(request);
1532 return IE_ERROR;
1534 xmlSetNs(request, isds_ns);
1536 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1538 /* Sent dummy request */
1539 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1541 /* Destroy log-in request */
1542 xmlFreeNode(request);
1544 if (soap_err) {
1545 isds_log(ILF_ISDS, ILL_DEBUG,
1546 _("ISDS server could not be contacted\n"));
1547 xmlFreeNodeList(response);
1548 return soap_err;
1551 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1552 * authentication succeeded if soap_err == IE_SUCCESS */
1553 /* TODO: ISDS documentation does not specify response body.
1554 * However real server sends back DummyOperationResponse */
1557 xmlFreeNodeList(response);
1559 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1561 return IE_SUCCESS;
1562 #else /* not HAVE_LIBCURL */
1563 return IE_NOTSUP;
1564 #endif
1568 /* Send bogus request to ISDS.
1569 * Just for test purposes */
1570 isds_error isds_bogus_request(struct isds_ctx *context) {
1571 #if HAVE_LIBCURL
1572 isds_error err;
1573 xmlNsPtr isds_ns = NULL;
1574 xmlNodePtr request = NULL;
1575 xmlDocPtr response = NULL;
1576 xmlChar *code = NULL, *message = NULL;
1577 #endif
1579 if (!context) return IE_INVALID_CONTEXT;
1580 zfree(context->long_message);
1582 #if HAVE_LIBCURL
1583 /* Check if connection is established */
1584 if (!context->curl) {
1585 /* Testing printf message */
1586 isds_printf_message(context, "%s", _("I said connection closed"));
1587 return IE_CONNECTION_CLOSED;
1591 /* Build dummy request */
1592 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1593 if (!request) {
1594 isds_log_message(context, _("Could build ISDS bogus request"));
1595 return IE_ERROR;
1597 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1598 if(!isds_ns) {
1599 isds_log_message(context, _("Could not create ISDS name space"));
1600 xmlFreeNode(request);
1601 return IE_ERROR;
1603 xmlSetNs(request, isds_ns);
1605 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1607 /* Sent bogus request */
1608 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1610 /* Destroy request */
1611 xmlFreeNode(request);
1613 if (err) {
1614 isds_log(ILF_ISDS, ILL_DEBUG,
1615 _("Processing ISDS response on bogus request failed\n"));
1616 xmlFreeDoc(response);
1617 return err;
1620 /* Check for response status */
1621 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1622 &code, &message, NULL);
1623 if (err) {
1624 isds_log(ILF_ISDS, ILL_DEBUG,
1625 _("ISDS response on bogus request is missing status\n"));
1626 free(code);
1627 free(message);
1628 xmlFreeDoc(response);
1629 return err;
1631 if (xmlStrcmp(code, BAD_CAST "0000")) {
1632 char *code_locale = _isds_utf82locale((char*)code);
1633 char *message_locale = _isds_utf82locale((char*)message);
1634 isds_log(ILF_ISDS, ILL_DEBUG,
1635 _("Server refused bogus request (code=%s, message=%s)\n"),
1636 code_locale, message_locale);
1637 /* XXX: Literal error messages from ISDS are Czech messages
1638 * (English sometimes) in UTF-8. It's hard to catch them for
1639 * translation. Successfully gettextized would return in locale
1640 * encoding, unsuccessfully translated would pass in UTF-8. */
1641 isds_log_message(context, message_locale);
1642 free(code_locale);
1643 free(message_locale);
1644 free(code);
1645 free(message);
1646 xmlFreeDoc(response);
1647 return IE_ISDS;
1651 free(code);
1652 free(message);
1653 xmlFreeDoc(response);
1655 isds_log(ILF_ISDS, ILL_DEBUG,
1656 _("Bogus message accepted by server. This should not happen.\n"));
1658 return IE_SUCCESS;
1659 #else /* not HAVE_LIBCURL */
1660 return IE_NOTSUP;
1661 #endif
1665 #if HAVE_LIBCURL
1666 /* Serialize XML subtree to buffer preserving XML indentation.
1667 * @context is session context
1668 * @subtree is XML element to be serialized (with children)
1669 * @buffer is automatically reallocated buffer where serialize to
1670 * @length is size of serialized stream in bytes
1671 * @return standard error code, free @buffer in case of error */
1672 static isds_error serialize_subtree(struct isds_ctx *context,
1673 xmlNodePtr subtree, void **buffer, size_t *length) {
1674 isds_error err = IE_SUCCESS;
1675 xmlBufferPtr xml_buffer = NULL;
1676 xmlSaveCtxtPtr save_ctx = NULL;
1677 xmlDocPtr subtree_doc = NULL;
1678 xmlNodePtr subtree_copy;
1679 xmlNsPtr isds_ns;
1680 void *new_buffer;
1682 if (!context) return IE_INVALID_CONTEXT;
1683 if (!buffer) return IE_INVAL;
1684 zfree(*buffer);
1685 if (!subtree || !length) return IE_INVAL;
1687 /* Make temporary XML document with @subtree root element */
1688 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1689 * It can result in not well-formed on invalid XML tree (e.g. name space
1690 * prefix definition can miss. */
1691 /*FIXME */
1693 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1694 if (!subtree_doc) {
1695 isds_log_message(context, _("Could not build temporary document"));
1696 err = IE_ERROR;
1697 goto leave;
1700 /* XXX: Copy subtree and attach the copy to document.
1701 * One node can not bee attached into more document at the same time.
1702 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1703 * automatically.
1704 * XXX: Check xmlSaveTree() too. */
1705 subtree_copy = xmlCopyNodeList(subtree);
1706 if (!subtree_copy) {
1707 isds_log_message(context, _("Could not copy subtree"));
1708 err = IE_ERROR;
1709 goto leave;
1711 xmlDocSetRootElement(subtree_doc, subtree_copy);
1713 /* Only this way we get namespace definition as @xmlns:isds,
1714 * otherwise we get namespace prefix without definition */
1715 /* FIXME: Don't overwrite original default namespace */
1716 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1717 if(!isds_ns) {
1718 isds_log_message(context, _("Could not create ISDS name space"));
1719 err = IE_ERROR;
1720 goto leave;
1722 xmlSetNs(subtree_copy, isds_ns);
1725 /* Serialize the document into buffer */
1726 xml_buffer = xmlBufferCreate();
1727 if (!xml_buffer) {
1728 isds_log_message(context, _("Could not create xmlBuffer"));
1729 err = IE_ERROR;
1730 goto leave;
1732 /* Last argument 0 means to not format the XML tree */
1733 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1734 if (!save_ctx) {
1735 isds_log_message(context, _("Could not create XML serializer"));
1736 err = IE_ERROR;
1737 goto leave;
1739 /* XXX: According LibXML documentation, this function does not return
1740 * meaningful value yet */
1741 xmlSaveDoc(save_ctx, subtree_doc);
1742 if (-1 == xmlSaveFlush(save_ctx)) {
1743 isds_log_message(context,
1744 _("Could not serialize XML subtree"));
1745 err = IE_ERROR;
1746 goto leave;
1748 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1749 * even after xmlSaveFlush(). Thus close it here */
1750 xmlSaveClose(save_ctx); save_ctx = NULL;
1753 /* Store and detach buffer from xml_buffer */
1754 *buffer = xml_buffer->content;
1755 *length = xml_buffer->use;
1756 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1758 /* Shrink buffer */
1759 new_buffer = realloc(*buffer, *length);
1760 if (new_buffer) *buffer = new_buffer;
1762 leave:
1763 if (err) {
1764 zfree(*buffer);
1765 *length = 0;
1768 xmlSaveClose(save_ctx);
1769 xmlBufferFree(xml_buffer);
1770 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1771 return err;
1773 #endif /* HAVE_LIBCURL */
1776 #if 0
1777 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1778 * @context is session context
1779 * @document is original document where @nodeset points to
1780 * @nodeset is XPath node set to dump (recursively)
1781 * @buffer is automatically reallocated buffer where serialize to
1782 * @length is size of serialized stream in bytes
1783 * @return standard error code, free @buffer in case of error */
1784 static isds_error dump_nodeset(struct isds_ctx *context,
1785 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1786 void **buffer, size_t *length) {
1787 isds_error err = IE_SUCCESS;
1788 xmlBufferPtr xml_buffer = NULL;
1789 void *new_buffer;
1791 if (!context) return IE_INVALID_CONTEXT;
1792 if (!buffer) return IE_INVAL;
1793 zfree(*buffer);
1794 if (!document || !nodeset || !length) return IE_INVAL;
1795 *length = 0;
1797 /* Empty node set results into NULL buffer */
1798 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1799 goto leave;
1802 /* Resulting the document into buffer */
1803 xml_buffer = xmlBufferCreate();
1804 if (!xml_buffer) {
1805 isds_log_message(context, _("Could not create xmlBuffer"));
1806 err = IE_ERROR;
1807 goto leave;
1810 /* Iterate over all nodes */
1811 for (int i = 0; i < nodeset->nodeNr; i++) {
1812 /* Serialize node.
1813 * XXX: xmlNodeDump() appends to xml_buffer. */
1814 if (-1 ==
1815 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1816 isds_log_message(context, _("Could not dump XML node"));
1817 err = IE_ERROR;
1818 goto leave;
1822 /* Store and detach buffer from xml_buffer */
1823 *buffer = xml_buffer->content;
1824 *length = xml_buffer->use;
1825 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1827 /* Shrink buffer */
1828 new_buffer = realloc(*buffer, *length);
1829 if (new_buffer) *buffer = new_buffer;
1832 leave:
1833 if (err) {
1834 zfree(*buffer);
1835 *length = 0;
1838 xmlBufferFree(xml_buffer);
1839 return err;
1841 #endif
1843 #if 0
1844 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1845 * @context is session context
1846 * @document is original document where @nodeset points to
1847 * @nodeset is XPath node set to dump (recursively)
1848 * @buffer is automatically reallocated buffer where serialize to
1849 * @length is size of serialized stream in bytes
1850 * @return standard error code, free @buffer in case of error */
1851 static isds_error dump_nodeset(struct isds_ctx *context,
1852 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1853 void **buffer, size_t *length) {
1854 isds_error err = IE_SUCCESS;
1855 xmlBufferPtr xml_buffer = NULL;
1856 xmlSaveCtxtPtr save_ctx = NULL;
1857 void *new_buffer;
1859 if (!context) return IE_INVALID_CONTEXT;
1860 if (!buffer) return IE_INVAL;
1861 zfree(*buffer);
1862 if (!document || !nodeset || !length) return IE_INVAL;
1863 *length = 0;
1865 /* Empty node set results into NULL buffer */
1866 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1867 goto leave;
1870 /* Resulting the document into buffer */
1871 xml_buffer = xmlBufferCreate();
1872 if (!xml_buffer) {
1873 isds_log_message(context, _("Could not create xmlBuffer"));
1874 err = IE_ERROR;
1875 goto leave;
1877 if (xmlSubstituteEntitiesDefault(1)) {
1878 isds_log_message(context, _("Could not disable attribute escaping"));
1879 err = IE_ERROR;
1880 goto leave;
1882 /* Last argument means:
1883 * 0 to not format the XML tree
1884 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1885 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1886 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1887 if (!save_ctx) {
1888 isds_log_message(context, _("Could not create XML serializer"));
1889 err = IE_ERROR;
1890 goto leave;
1892 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1893 isds_log_message(context, _("Could not disable attribute escaping"));
1894 err = IE_ERROR;
1895 goto leave;
1899 /* Iterate over all nodes */
1900 for (int i = 0; i < nodeset->nodeNr; i++) {
1901 /* Serialize node.
1902 * XXX: xmlNodeDump() appends to xml_buffer. */
1903 /*if (-1 ==
1904 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1906 /* XXX: According LibXML documentation, this function does not return
1907 * meaningful value yet */
1908 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1909 if (-1 == xmlSaveFlush(save_ctx)) {
1910 isds_log_message(context,
1911 _("Could not serialize XML subtree"));
1912 err = IE_ERROR;
1913 goto leave;
1917 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1918 * even after xmlSaveFlush(). Thus close it here */
1919 xmlSaveClose(save_ctx); save_ctx = NULL;
1921 /* Store and detach buffer from xml_buffer */
1922 *buffer = xml_buffer->content;
1923 *length = xml_buffer->use;
1924 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1926 /* Shrink buffer */
1927 new_buffer = realloc(*buffer, *length);
1928 if (new_buffer) *buffer = new_buffer;
1930 leave:
1931 if (err) {
1932 zfree(*buffer);
1933 *length = 0;
1936 xmlSaveClose(save_ctx);
1937 xmlBufferFree(xml_buffer);
1938 return err;
1940 #endif
1943 #if HAVE_LIBCURL
1944 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1945 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1946 if (!string || !type) return IE_INVAL;
1948 if (!xmlStrcmp(string, BAD_CAST "FO"))
1949 *type = DBTYPE_FO;
1950 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1951 *type = DBTYPE_PFO;
1952 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1953 *type = DBTYPE_PFO_ADVOK;
1954 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1955 *type = DBTYPE_PFO_DANPOR;
1956 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1957 *type = DBTYPE_PFO_INSSPR;
1958 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1959 *type = DBTYPE_PO;
1960 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1961 *type = DBTYPE_PO_ZAK;
1962 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1963 *type = DBTYPE_PO_REQ;
1964 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1965 *type = DBTYPE_OVM;
1966 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1967 *type = DBTYPE_OVM_NOTAR;
1968 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1969 *type = DBTYPE_OVM_EXEKUT;
1970 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1971 *type = DBTYPE_OVM_REQ;
1972 else
1973 return IE_ENUM;
1974 return IE_SUCCESS;
1978 /* Convert ISDS dbType enum @type to UTF-8 string.
1979 * @Return pointer to static string, or NULL if unknown enum value */
1980 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1981 switch(type) {
1982 /* DBTYPE_SYSTEM is invalid value from point of view of public
1983 * SOAP interface. */
1984 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1985 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1986 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1987 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1988 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1989 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1990 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1991 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1992 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1993 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1994 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1995 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1996 default: return NULL; break;
2001 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2002 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
2003 if (!string || !type) return IE_INVAL;
2005 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2006 *type = USERTYPE_PRIMARY;
2007 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2008 *type = USERTYPE_ENTRUSTED;
2009 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2010 *type = USERTYPE_ADMINISTRATOR;
2011 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2012 *type = USERTYPE_OFFICIAL;
2013 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2014 *type = USERTYPE_OFFICIAL_CERT;
2015 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2016 *type = USERTYPE_LIQUIDATOR;
2017 else
2018 return IE_ENUM;
2019 return IE_SUCCESS;
2023 /* Convert ISDS userType enum @type to UTF-8 string.
2024 * @Return pointer to static string, or NULL if unknown enum value */
2025 static const xmlChar *isds_UserType2string(const isds_UserType type) {
2026 switch(type) {
2027 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
2028 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
2029 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
2030 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
2031 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
2032 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
2033 default: return NULL; break;
2038 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2039 static isds_error string2isds_sender_type(const xmlChar *string,
2040 isds_sender_type *type) {
2041 if (!string || !type) return IE_INVAL;
2043 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2044 *type = SENDERTYPE_PRIMARY;
2045 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2046 *type = SENDERTYPE_ENTRUSTED;
2047 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2048 *type = SENDERTYPE_ADMINISTRATOR;
2049 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2050 *type = SENDERTYPE_OFFICIAL;
2051 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2052 *type = SENDERTYPE_VIRTUAL;
2053 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2054 *type = SENDERTYPE_OFFICIAL_CERT;
2055 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2056 *type = SENDERTYPE_LIQUIDATOR;
2057 else
2058 return IE_ENUM;
2059 return IE_SUCCESS;
2063 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2064 static isds_error string2isds_payment_type(const xmlChar *string,
2065 isds_payment_type *type) {
2066 if (!string || !type) return IE_INVAL;
2068 if (!xmlStrcmp(string, BAD_CAST "K"))
2069 *type = PAYMENT_SENDER;
2070 else if (!xmlStrcmp(string, BAD_CAST "O"))
2071 *type = PAYMENT_RESPONSE;
2072 else if (!xmlStrcmp(string, BAD_CAST "G"))
2073 *type = PAYMENT_SPONSOR;
2074 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2075 *type = PAYMENT_SPONSOR_LIMITED;
2076 else if (!xmlStrcmp(string, BAD_CAST "D"))
2077 *type = PAYMENT_SPONSOR_EXTERNAL;
2078 else if (!xmlStrcmp(string, BAD_CAST "E"))
2079 *type = PAYMENT_STAMP;
2080 else
2081 return IE_ENUM;
2082 return IE_SUCCESS;
2086 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2087 * ciEventType is integer but we convert it from string representation
2088 * directly. */
2089 static isds_error string2isds_credit_event_type(const xmlChar *string,
2090 isds_credit_event_type *type) {
2091 if (!string || !type) return IE_INVAL;
2093 if (!xmlStrcmp(string, BAD_CAST "1"))
2094 *type = ISDS_CREDIT_CHARGED;
2095 else if (!xmlStrcmp(string, BAD_CAST "2"))
2096 *type = ISDS_CREDIT_DISCHARGED;
2097 else if (!xmlStrcmp(string, BAD_CAST "3"))
2098 *type = ISDS_CREDIT_MESSAGE_SENT;
2099 else if (!xmlStrcmp(string, BAD_CAST "4"))
2100 *type = ISDS_CREDIT_STORAGE_SET;
2101 else if (!xmlStrcmp(string, BAD_CAST "5"))
2102 *type = ISDS_CREDIT_EXPIRED;
2103 else
2104 return IE_ENUM;
2105 return IE_SUCCESS;
2109 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2110 * @Return pointer to static string, or NULL if unknown enum value */
2111 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2112 switch(type) {
2113 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2114 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2115 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2116 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2117 default: return NULL; break;
2120 #endif /* HAVE_LIBCURL */
2123 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2124 * @Return IE_ENUM if @string is not valid enum member */
2125 static isds_error string2isds_FileMetaType(const xmlChar *string,
2126 isds_FileMetaType *type) {
2127 if (!string || !type) return IE_INVAL;
2129 if (!xmlStrcmp(string, BAD_CAST "main"))
2130 *type = FILEMETATYPE_MAIN;
2131 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2132 *type = FILEMETATYPE_ENCLOSURE;
2133 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2134 *type = FILEMETATYPE_SIGNATURE;
2135 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2136 *type = FILEMETATYPE_META;
2137 else
2138 return IE_ENUM;
2139 return IE_SUCCESS;
2143 /* Convert UTF-8 @string to ISDS hash @algorithm.
2144 * @Return IE_ENUM if @string is not valid enum member */
2145 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2146 isds_hash_algorithm *algorithm) {
2147 if (!string || !algorithm) return IE_INVAL;
2149 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2150 *algorithm = HASH_ALGORITHM_MD5;
2151 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2152 *algorithm = HASH_ALGORITHM_SHA_1;
2153 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2154 *algorithm = HASH_ALGORITHM_SHA_224;
2155 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2156 *algorithm = HASH_ALGORITHM_SHA_256;
2157 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2158 *algorithm = HASH_ALGORITHM_SHA_384;
2159 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2160 *algorithm = HASH_ALGORITHM_SHA_512;
2161 else
2162 return IE_ENUM;
2163 return IE_SUCCESS;
2167 #if HAVE_LIBCURL
2168 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2169 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2170 if (!time || !string) return IE_INVAL;
2172 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2173 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2174 return IE_ERROR;
2176 return IE_SUCCESS;
2180 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2181 * respects the @time microseconds too. */
2182 static isds_error timeval2timestring(const struct timeval *time,
2183 xmlChar **string) {
2184 struct tm broken;
2186 if (!time || !string) return IE_INVAL;
2188 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
2189 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2191 /* TODO: small negative year should be formatted as "-0012". This is not
2192 * true for glibc "%04d". We should implement it.
2193 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
2194 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2195 if (-1 == isds_asprintf((char **) string,
2196 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
2197 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2198 broken.tm_hour, broken.tm_min, broken.tm_sec,
2199 time->tv_usec))
2200 return IE_ERROR;
2202 return IE_SUCCESS;
2204 #endif /* HAVE_LIBCURL */
2207 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2208 * It respects microseconds too.
2209 * In case of error, @time will be freed. */
2210 static isds_error timestring2timeval(const xmlChar *string,
2211 struct timeval **time) {
2212 struct tm broken;
2213 char *offset, *delim, *endptr;
2214 char subseconds[7];
2215 int offset_hours, offset_minutes;
2216 int i;
2217 #ifdef _WIN32
2218 int tmp;
2219 #endif
2221 if (!time) return IE_INVAL;
2222 if (!string) {
2223 zfree(*time);
2224 return IE_INVAL;
2227 memset(&broken, 0, sizeof(broken));
2229 if (!*time) {
2230 *time = calloc(1, sizeof(**time));
2231 if (!*time) return IE_NOMEM;
2232 } else {
2233 memset(*time, 0, sizeof(**time));
2237 /* xsd:date is ISO 8601 string, thus ASCII */
2238 /*TODO: negative year */
2240 #ifdef _WIN32
2241 i = 0;
2242 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2243 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2244 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2245 &i)) < 6) {
2246 zfree(*time);
2247 return IE_DATE;
2250 broken.tm_year -= 1900;
2251 broken.tm_mon--;
2252 offset = (char*)string + i;
2253 #else
2254 /* Parse date and time without subseconds and offset */
2255 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2256 if (!offset) {
2257 zfree(*time);
2258 return IE_DATE;
2260 #endif
2262 /* Get subseconds */
2263 if (*offset == '.' ) {
2264 offset++;
2266 /* Copy first 6 digits, pad it with zeros.
2267 * XXX: It truncates longer number, no round.
2268 * Current server implementation uses only millisecond resolution. */
2269 /* TODO: isdigit() is locale sensitive */
2270 for (i = 0;
2271 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
2272 i++, offset++) {
2273 subseconds[i] = *offset;
2275 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
2276 subseconds[i] = '0';
2278 subseconds[6] = '\0';
2280 /* Convert it into integer */
2281 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
2282 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
2283 (*time)->tv_usec == LONG_MAX) {
2284 zfree(*time);
2285 return IE_DATE;
2288 /* move to the zone offset delimiter or signal NULL*/
2289 delim = strchr(offset, '-');
2290 if (!delim)
2291 delim = strchr(offset, '+');
2292 if (!delim)
2293 delim = strchr(offset, 'Z');
2294 offset = delim;
2297 /* Get zone offset */
2298 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2299 * "" equals to "Z" and it means UTC zone. */
2300 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2301 * colon separator */
2302 if (offset && (*offset == '-' || *offset == '+')) {
2303 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2304 zfree(*time);
2305 return IE_DATE;
2307 if (*offset == '+') {
2308 broken.tm_hour -= offset_hours;
2309 broken.tm_min -= offset_minutes;
2310 } else {
2311 broken.tm_hour += offset_hours;
2312 broken.tm_min += offset_minutes;
2316 /* Convert to time_t */
2317 (*time)->tv_sec = _isds_timegm(&broken);
2318 if ((*time)->tv_sec == (time_t) -1) {
2319 zfree(*time);
2320 return IE_DATE;
2323 return IE_SUCCESS;
2327 /* Convert unsigned int into isds_message_status.
2328 * @context is session context
2329 * @number is pointer to number value. NULL will be treated as invalid value.
2330 * @status is automatically reallocated status
2331 * @return IE_SUCCESS, or error code and free status */
2332 static isds_error uint2isds_message_status(struct isds_ctx *context,
2333 const unsigned long int *number, isds_message_status **status) {
2334 if (!context) return IE_INVALID_CONTEXT;
2335 if (!status) return IE_INVAL;
2337 free(*status); *status = NULL;
2338 if (!number) return IE_INVAL;
2340 if (*number < 1 || *number > 10) {
2341 isds_printf_message(context, _("Invalid message status value: %lu"),
2342 *number);
2343 return IE_ENUM;
2346 *status = malloc(sizeof(**status));
2347 if (!*status) return IE_NOMEM;
2349 **status = 1 << *number;
2350 return IE_SUCCESS;
2354 /* Convert event description string into isds_event members type and
2355 * description
2356 * @string is raw event description starting with event prefix
2357 * @event is structure where to store type and stripped description to
2358 * @return standard error code, unknown prefix is not classified as an error.
2359 * */
2360 static isds_error eventstring2event(const xmlChar *string,
2361 struct isds_event* event) {
2362 const xmlChar *known_prefixes[] = {
2363 BAD_CAST "EV0:",
2364 BAD_CAST "EV1:",
2365 BAD_CAST "EV2:",
2366 BAD_CAST "EV3:",
2367 BAD_CAST "EV4:",
2368 BAD_CAST "EV5:",
2369 BAD_CAST "EV11:",
2370 BAD_CAST "EV12:",
2371 BAD_CAST "EV13:"
2373 const isds_event_type types[] = {
2374 EVENT_ENTERED_SYSTEM,
2375 EVENT_ACCEPTED_BY_RECIPIENT,
2376 EVENT_ACCEPTED_BY_FICTION,
2377 EVENT_UNDELIVERABLE,
2378 EVENT_COMMERCIAL_ACCEPTED,
2379 EVENT_DELIVERED,
2380 EVENT_PRIMARY_LOGIN,
2381 EVENT_ENTRUSTED_LOGIN,
2382 EVENT_SYSCERT_LOGIN
2384 unsigned int index;
2385 size_t length;
2387 if (!string || !event) return IE_INVAL;
2389 if (!event->type) {
2390 event->type = malloc(sizeof(*event->type));
2391 if (!(event->type)) return IE_NOMEM;
2393 zfree(event->description);
2395 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2396 index++) {
2397 length = xmlUTF8Strlen(known_prefixes[index]);
2399 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2400 /* Prefix is known */
2401 *event->type = types[index];
2403 /* Strip prefix from description and spaces */
2404 /* TODO: Recognize all white spaces from UCS blank class and
2405 * operate on UTF-8 chars. */
2406 for (; string[length] != '\0' && string[length] == ' '; length++);
2407 event->description = strdup((char *) (string + length));
2408 if (!(event->description)) return IE_NOMEM;
2410 return IE_SUCCESS;
2414 /* Unknown event prefix.
2415 * XSD allows any string */
2416 char *string_locale = _isds_utf82locale((char *) string);
2417 isds_log(ILF_ISDS, ILL_WARNING,
2418 _("Unknown delivery info event prefix: %s\n"), string_locale);
2419 free(string_locale);
2421 *event->type = EVENT_UKNOWN;
2422 event->description = strdup((char *) string);
2423 if (!(event->description)) return IE_NOMEM;
2425 return IE_SUCCESS;
2429 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2430 * and leave label */
2431 #define EXTRACT_STRING(element, string) { \
2432 xmlXPathFreeObject(result); \
2433 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2434 if (NULL == (result)) { \
2435 err = IE_ERROR; \
2436 goto leave; \
2438 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2439 if (result->nodesetval->nodeNr > 1) { \
2440 isds_printf_message(context, _("Multiple %s element"), element); \
2441 err = IE_ERROR; \
2442 goto leave; \
2444 (string) = (char *) \
2445 xmlXPathCastNodeSetToString(result->nodesetval); \
2446 if (NULL == (string)) { \
2447 err = IE_ERROR; \
2448 goto leave; \
2453 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2455 char *string = NULL; \
2456 EXTRACT_STRING(element, string); \
2458 if (string) { \
2459 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2460 if (!(booleanPtr)) { \
2461 free(string); \
2462 err = IE_NOMEM; \
2463 goto leave; \
2466 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2467 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2468 *(booleanPtr) = 1; \
2469 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2470 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2471 *(booleanPtr) = 0; \
2472 else { \
2473 char *string_locale = _isds_utf82locale((char*)string); \
2474 isds_printf_message(context, \
2475 _("%s value is not valid boolean: %s"), \
2476 element, string_locale); \
2477 free(string_locale); \
2478 free(string); \
2479 err = IE_ERROR; \
2480 goto leave; \
2483 free(string); \
2487 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2489 char *string = NULL; \
2490 EXTRACT_STRING(element, string); \
2491 if (string) { \
2492 long int number; \
2493 char *endptr; \
2495 number = strtol((char*)string, &endptr, 10); \
2497 if (*endptr != '\0') { \
2498 char *string_locale = _isds_utf82locale((char *)string); \
2499 isds_printf_message(context, \
2500 _("%s is not valid integer: %s"), \
2501 element, string_locale); \
2502 free(string_locale); \
2503 free(string); \
2504 err = IE_ISDS; \
2505 goto leave; \
2508 if (number == LONG_MIN || number == LONG_MAX) { \
2509 char *string_locale = _isds_utf82locale((char *)string); \
2510 isds_printf_message(context, \
2511 _("%s value out of range of long int: %s"), \
2512 element, string_locale); \
2513 free(string_locale); \
2514 free(string); \
2515 err = IE_ERROR; \
2516 goto leave; \
2519 free(string); string = NULL; \
2521 if (!(preallocated)) { \
2522 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2523 if (!(longintPtr)) { \
2524 err = IE_NOMEM; \
2525 goto leave; \
2528 *(longintPtr) = number; \
2532 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2534 char *string = NULL; \
2535 EXTRACT_STRING(element, string); \
2536 if (string) { \
2537 long int number; \
2538 char *endptr; \
2540 number = strtol((char*)string, &endptr, 10); \
2542 if (*endptr != '\0') { \
2543 char *string_locale = _isds_utf82locale((char *)string); \
2544 isds_printf_message(context, \
2545 _("%s is not valid integer: %s"), \
2546 element, string_locale); \
2547 free(string_locale); \
2548 free(string); \
2549 err = IE_ISDS; \
2550 goto leave; \
2553 if (number == LONG_MIN || number == LONG_MAX) { \
2554 char *string_locale = _isds_utf82locale((char *)string); \
2555 isds_printf_message(context, \
2556 _("%s value out of range of long int: %s"), \
2557 element, string_locale); \
2558 free(string_locale); \
2559 free(string); \
2560 err = IE_ERROR; \
2561 goto leave; \
2564 free(string); string = NULL; \
2565 if (number < 0) { \
2566 isds_printf_message(context, \
2567 _("%s value is negative: %ld"), element, number); \
2568 err = IE_ERROR; \
2569 goto leave; \
2572 if (!(preallocated)) { \
2573 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2574 if (!(ulongintPtr)) { \
2575 err = IE_NOMEM; \
2576 goto leave; \
2579 *(ulongintPtr) = number; \
2583 #define EXTRACT_DATE(element, tmPtr) { \
2584 char *string = NULL; \
2585 EXTRACT_STRING(element, string); \
2586 if (NULL != string) { \
2587 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2588 if (NULL == (tmPtr)) { \
2589 free(string); \
2590 err = IE_NOMEM; \
2591 goto leave; \
2593 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2594 if (err) { \
2595 if (err == IE_NOTSUP) { \
2596 err = IE_ISDS; \
2597 char *string_locale = _isds_utf82locale(string); \
2598 char *element_locale = _isds_utf82locale(element); \
2599 isds_printf_message(context, _("Invalid %s value: %s"), \
2600 element_locale, string_locale); \
2601 free(string_locale); \
2602 free(element_locale); \
2604 free(string); \
2605 goto leave; \
2607 free(string); \
2611 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2612 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2613 NULL); \
2614 if ((required) && (!string)) { \
2615 char *attribute_locale = _isds_utf82locale(attribute); \
2616 char *element_locale = \
2617 _isds_utf82locale((char *)xpath_ctx->node->name); \
2618 isds_printf_message(context, \
2619 _("Could not extract required %s attribute value from " \
2620 "%s element"), attribute_locale, element_locale); \
2621 free(element_locale); \
2622 free(attribute_locale); \
2623 err = IE_ERROR; \
2624 goto leave; \
2629 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2631 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2632 (xmlChar *) (string)); \
2633 if (!node) { \
2634 isds_printf_message(context, \
2635 _("Could not add %s child to %s element"), \
2636 element, (parent)->name); \
2637 err = IE_ERROR; \
2638 goto leave; \
2642 #define INSERT_STRING(parent, element, string) \
2643 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2645 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2647 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2648 else { INSERT_STRING(parent, element, "false"); } \
2651 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2653 if (booleanPtr) { \
2654 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2655 } else { \
2656 INSERT_STRING(parent, element, NULL); \
2660 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2661 if ((longintPtr)) { \
2662 /* FIXME: locale sensitive */ \
2663 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2664 err = IE_NOMEM; \
2665 goto leave; \
2667 INSERT_STRING(parent, element, buffer) \
2668 free(buffer); (buffer) = NULL; \
2669 } else { INSERT_STRING(parent, element, NULL) } \
2672 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2673 if ((ulongintPtr)) { \
2674 /* FIXME: locale sensitive */ \
2675 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2676 err = IE_NOMEM; \
2677 goto leave; \
2679 INSERT_STRING(parent, element, buffer) \
2680 free(buffer); (buffer) = NULL; \
2681 } else { INSERT_STRING(parent, element, NULL) } \
2684 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2686 /* FIXME: locale sensitive */ \
2687 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2688 err = IE_NOMEM; \
2689 goto leave; \
2691 INSERT_STRING(parent, element, buffer) \
2692 free(buffer); (buffer) = NULL; \
2695 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2696 * new attribute. */
2697 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2699 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2700 (xmlChar *) (string)); \
2701 if (!attribute_node) { \
2702 isds_printf_message(context, _("Could not add %s " \
2703 "attribute to %s element"), \
2704 (attribute), (parent)->name); \
2705 err = IE_ERROR; \
2706 goto leave; \
2710 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2711 if (string) { \
2712 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2713 if (length > (maximum)) { \
2714 isds_printf_message(context, \
2715 ngettext("%s has more than %d characters", \
2716 "%s has more than %d characters", (maximum)), \
2717 (name), (maximum)); \
2718 err = IE_2BIG; \
2719 goto leave; \
2721 if (length < (minimum)) { \
2722 isds_printf_message(context, \
2723 ngettext("%s has less than %d characters", \
2724 "%s has less than %d characters", (minimum)), \
2725 (name), (minimum)); \
2726 err = IE_2SMALL; \
2727 goto leave; \
2732 #define INSERT_ELEMENT(child, parent, element) \
2734 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2735 if (!(child)) { \
2736 isds_printf_message(context, \
2737 _("Could not add %s child to %s element"), \
2738 (element), (parent)->name); \
2739 err = IE_ERROR; \
2740 goto leave; \
2745 /* Find child element by name in given XPath context and switch context onto
2746 * it. The child must be uniq and must exist. Otherwise fails.
2747 * @context is ISDS context
2748 * @child is child element name
2749 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2750 * into it child. In error case, the @xpath_ctx keeps original value. */
2751 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2752 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2753 isds_error err = IE_SUCCESS;
2754 xmlXPathObjectPtr result = NULL;
2756 if (!context) return IE_INVALID_CONTEXT;
2757 if (!child || !xpath_ctx) return IE_INVAL;
2759 /* Find child */
2760 result = xmlXPathEvalExpression(child, xpath_ctx);
2761 if (!result) {
2762 err = IE_XML;
2763 goto leave;
2766 /* No match */
2767 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2768 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2769 char *child_locale = _isds_utf82locale((char*) child);
2770 isds_printf_message(context,
2771 _("%s element does not contain %s child"),
2772 parent_locale, child_locale);
2773 free(child_locale);
2774 free(parent_locale);
2775 err = IE_NOEXIST;
2776 goto leave;
2779 /* More matches */
2780 if (result->nodesetval->nodeNr > 1) {
2781 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2782 char *child_locale = _isds_utf82locale((char*) child);
2783 isds_printf_message(context,
2784 _("%s element contains multiple %s children"),
2785 parent_locale, child_locale);
2786 free(child_locale);
2787 free(parent_locale);
2788 err = IE_NOTUNIQ;
2789 goto leave;
2792 /* Switch context */
2793 xpath_ctx->node = result->nodesetval->nodeTab[0];
2795 leave:
2796 xmlXPathFreeObject(result);
2797 return err;
2802 #if HAVE_LIBCURL
2803 /* Find and convert XSD:gPersonName group in current node into structure
2804 * @context is ISDS context
2805 * @personName is automatically reallocated person name structure. If no member
2806 * value is found, will be freed.
2807 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2808 * elements
2809 * In case of error @personName will be freed. */
2810 static isds_error extract_gPersonName(struct isds_ctx *context,
2811 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2812 isds_error err = IE_SUCCESS;
2813 xmlXPathObjectPtr result = NULL;
2815 if (!context) return IE_INVALID_CONTEXT;
2816 if (!personName) return IE_INVAL;
2817 isds_PersonName_free(personName);
2818 if (!xpath_ctx) return IE_INVAL;
2821 *personName = calloc(1, sizeof(**personName));
2822 if (!*personName) {
2823 err = IE_NOMEM;
2824 goto leave;
2827 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2828 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2829 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2830 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2832 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2833 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2834 isds_PersonName_free(personName);
2836 leave:
2837 if (err) isds_PersonName_free(personName);
2838 xmlXPathFreeObject(result);
2839 return err;
2843 /* Find and convert XSD:gAddress group in current node into structure
2844 * @context is ISDS context
2845 * @address is automatically reallocated address structure. If no member
2846 * value is found, will be freed.
2847 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2848 * elements
2849 * In case of error @address will be freed. */
2850 static isds_error extract_gAddress(struct isds_ctx *context,
2851 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2852 isds_error err = IE_SUCCESS;
2853 xmlXPathObjectPtr result = NULL;
2855 if (!context) return IE_INVALID_CONTEXT;
2856 if (!address) return IE_INVAL;
2857 isds_Address_free(address);
2858 if (!xpath_ctx) return IE_INVAL;
2861 *address = calloc(1, sizeof(**address));
2862 if (!*address) {
2863 err = IE_NOMEM;
2864 goto leave;
2867 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2868 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2869 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2870 EXTRACT_STRING("isds:adNumberInMunicipality",
2871 (*address)->adNumberInMunicipality);
2872 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2873 EXTRACT_STRING("isds:adState", (*address)->adState);
2875 if (!(*address)->adCity && !(*address)->adStreet &&
2876 !(*address)->adNumberInStreet &&
2877 !(*address)->adNumberInMunicipality &&
2878 !(*address)->adZipCode && !(*address)->adState)
2879 isds_Address_free(address);
2881 leave:
2882 if (err) isds_Address_free(address);
2883 xmlXPathFreeObject(result);
2884 return err;
2888 /* Find and convert isds:biDate element in current node into structure
2889 * @context is ISDS context
2890 * @biDate is automatically reallocated birth date structure. If no member
2891 * value is found, will be freed.
2892 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2893 * element
2894 * In case of error @biDate will be freed. */
2895 static isds_error extract_BiDate(struct isds_ctx *context,
2896 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2897 isds_error err = IE_SUCCESS;
2898 xmlXPathObjectPtr result = NULL;
2899 char *string = NULL;
2901 if (!context) return IE_INVALID_CONTEXT;
2902 if (!biDate) return IE_INVAL;
2903 zfree(*biDate);
2904 if (!xpath_ctx) return IE_INVAL;
2906 EXTRACT_STRING("isds:biDate", string);
2907 if (string) {
2908 *biDate = calloc(1, sizeof(**biDate));
2909 if (!*biDate) {
2910 err = IE_NOMEM;
2911 goto leave;
2913 err = _isds_datestring2tm((xmlChar *)string, *biDate);
2914 if (err) {
2915 if (err == IE_NOTSUP) {
2916 err = IE_ISDS;
2917 char *string_locale = _isds_utf82locale(string);
2918 isds_printf_message(context,
2919 _("Invalid isds:biDate value: %s"), string_locale);
2920 free(string_locale);
2922 goto leave;
2926 leave:
2927 if (err) zfree(*biDate);
2928 free(string);
2929 xmlXPathFreeObject(result);
2930 return err;
2934 /* Convert isds:dBOwnerInfo XML tree into structure
2935 * @context is ISDS context
2936 * @db_owner_info is automatically reallocated box owner info structure
2937 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2938 * In case of error @db_owner_info will be freed. */
2939 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2940 struct isds_DbOwnerInfo **db_owner_info,
2941 xmlXPathContextPtr xpath_ctx) {
2942 isds_error err = IE_SUCCESS;
2943 xmlXPathObjectPtr result = NULL;
2944 char *string = NULL;
2946 if (!context) return IE_INVALID_CONTEXT;
2947 if (!db_owner_info) return IE_INVAL;
2948 isds_DbOwnerInfo_free(db_owner_info);
2949 if (!xpath_ctx) return IE_INVAL;
2952 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2953 if (!*db_owner_info) {
2954 err = IE_NOMEM;
2955 goto leave;
2958 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2960 EXTRACT_STRING("isds:dbType", string);
2961 if (string) {
2962 (*db_owner_info)->dbType =
2963 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2964 if (!(*db_owner_info)->dbType) {
2965 err = IE_NOMEM;
2966 goto leave;
2968 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2969 if (err) {
2970 zfree((*db_owner_info)->dbType);
2971 if (err == IE_ENUM) {
2972 err = IE_ISDS;
2973 char *string_locale = _isds_utf82locale(string);
2974 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2975 string_locale);
2976 free(string_locale);
2978 goto leave;
2980 zfree(string);
2983 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2985 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2986 xpath_ctx);
2987 if (err) goto leave;
2989 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2991 (*db_owner_info)->birthInfo =
2992 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2993 if (!(*db_owner_info)->birthInfo) {
2994 err = IE_NOMEM;
2995 goto leave;
2997 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2998 xpath_ctx);
2999 if (err) goto leave;
3000 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3001 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3002 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3003 if (!(*db_owner_info)->birthInfo->biDate &&
3004 !(*db_owner_info)->birthInfo->biCity &&
3005 !(*db_owner_info)->birthInfo->biCounty &&
3006 !(*db_owner_info)->birthInfo->biState)
3007 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3009 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3010 if (err) goto leave;
3012 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3013 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3014 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3015 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3016 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3018 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3020 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3021 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3022 (*db_owner_info)->dbOpenAddressing);
3024 leave:
3025 if (err) isds_DbOwnerInfo_free(db_owner_info);
3026 free(string);
3027 xmlXPathFreeObject(result);
3028 return err;
3032 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3033 * @context is session context
3034 * @owner is libisds structure with box description
3035 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3036 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3037 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
3039 isds_error err = IE_SUCCESS;
3040 xmlNodePtr node;
3041 xmlChar *string = NULL;
3043 if (!context) return IE_INVALID_CONTEXT;
3044 if (!owner || !db_owner_info) return IE_INVAL;
3047 /* Build XSD:tDbOwnerInfo */
3048 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3049 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3051 /* dbType */
3052 if (owner->dbType) {
3053 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
3054 if (!type_string) {
3055 isds_printf_message(context, _("Invalid dbType value: %d"),
3056 *(owner->dbType));
3057 err = IE_ENUM;
3058 goto leave;
3060 INSERT_STRING(db_owner_info, "dbType", type_string);
3062 INSERT_STRING(db_owner_info, "ic", owner->ic);
3063 if (owner->personName) {
3064 INSERT_STRING(db_owner_info, "pnFirstName",
3065 owner->personName->pnFirstName);
3066 INSERT_STRING(db_owner_info, "pnMiddleName",
3067 owner->personName->pnMiddleName);
3068 INSERT_STRING(db_owner_info, "pnLastName",
3069 owner->personName->pnLastName);
3070 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3071 owner->personName->pnLastNameAtBirth);
3073 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3074 if (owner->birthInfo) {
3075 if (owner->birthInfo->biDate) {
3076 if (!tm2datestring(owner->birthInfo->biDate, &string))
3077 INSERT_STRING(db_owner_info, "biDate", string);
3078 free(string); string = NULL;
3080 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
3081 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
3082 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
3084 if (owner->address) {
3085 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
3086 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
3087 INSERT_STRING(db_owner_info, "adNumberInStreet",
3088 owner->address->adNumberInStreet);
3089 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3090 owner->address->adNumberInMunicipality);
3091 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
3092 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
3094 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3095 INSERT_STRING(db_owner_info, "email", owner->email);
3096 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3098 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3099 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3101 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3102 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3104 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3106 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3107 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3108 owner->dbOpenAddressing);
3110 leave:
3111 free(string);
3112 return err;
3116 /* Convert XSD:tDbUserInfo XML tree into structure
3117 * @context is ISDS context
3118 * @db_user_info is automatically reallocated user info structure
3119 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3120 * In case of error @db_user_info will be freed. */
3121 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3122 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3123 isds_error err = IE_SUCCESS;
3124 xmlXPathObjectPtr result = NULL;
3125 char *string = NULL;
3127 if (!context) return IE_INVALID_CONTEXT;
3128 if (!db_user_info) return IE_INVAL;
3129 isds_DbUserInfo_free(db_user_info);
3130 if (!xpath_ctx) return IE_INVAL;
3133 *db_user_info = calloc(1, sizeof(**db_user_info));
3134 if (!*db_user_info) {
3135 err = IE_NOMEM;
3136 goto leave;
3139 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3141 EXTRACT_STRING("isds:userType", string);
3142 if (string) {
3143 (*db_user_info)->userType =
3144 calloc(1, sizeof(*((*db_user_info)->userType)));
3145 if (!(*db_user_info)->userType) {
3146 err = IE_NOMEM;
3147 goto leave;
3149 err = string2isds_UserType((xmlChar *)string,
3150 (*db_user_info)->userType);
3151 if (err) {
3152 zfree((*db_user_info)->userType);
3153 if (err == IE_ENUM) {
3154 err = IE_ISDS;
3155 char *string_locale = _isds_utf82locale(string);
3156 isds_printf_message(context,
3157 _("Unknown isds:userType value: %s"), string_locale);
3158 free(string_locale);
3160 goto leave;
3162 zfree(string);
3165 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3167 (*db_user_info)->personName =
3168 calloc(1, sizeof(*((*db_user_info)->personName)));
3169 if (!(*db_user_info)->personName) {
3170 err = IE_NOMEM;
3171 goto leave;
3174 err = extract_gPersonName(context, &(*db_user_info)->personName,
3175 xpath_ctx);
3176 if (err) goto leave;
3178 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3179 if (err) goto leave;
3181 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3182 if (err) goto leave;
3184 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3185 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3187 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3188 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3189 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3191 /* ???: Default value is "CZ" according specification. Should we provide
3192 * it? */
3193 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3195 leave:
3196 if (err) isds_DbUserInfo_free(db_user_info);
3197 free(string);
3198 xmlXPathFreeObject(result);
3199 return err;
3203 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3204 * @context is session context
3205 * @user is libisds structure with user description
3206 * @db_user_info is XML element of XSD:tDbUserInfo */
3207 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3208 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3210 isds_error err = IE_SUCCESS;
3211 xmlNodePtr node;
3212 xmlChar *string = NULL;
3214 if (!context) return IE_INVALID_CONTEXT;
3215 if (!user || !db_user_info) return IE_INVAL;
3217 /* Build XSD:tDbUserInfo */
3218 if (user->personName) {
3219 INSERT_STRING(db_user_info, "pnFirstName",
3220 user->personName->pnFirstName);
3221 INSERT_STRING(db_user_info, "pnMiddleName",
3222 user->personName->pnMiddleName);
3223 INSERT_STRING(db_user_info, "pnLastName",
3224 user->personName->pnLastName);
3225 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3226 user->personName->pnLastNameAtBirth);
3228 if (user->address) {
3229 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3230 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3231 INSERT_STRING(db_user_info, "adNumberInStreet",
3232 user->address->adNumberInStreet);
3233 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3234 user->address->adNumberInMunicipality);
3235 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3236 INSERT_STRING(db_user_info, "adState", user->address->adState);
3238 if (user->biDate) {
3239 if (!tm2datestring(user->biDate, &string))
3240 INSERT_STRING(db_user_info, "biDate", string);
3241 zfree(string);
3243 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3244 INSERT_STRING(db_user_info, "userID", user->userID);
3246 /* userType */
3247 if (user->userType) {
3248 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3249 if (!type_string) {
3250 isds_printf_message(context, _("Invalid userType value: %d"),
3251 *(user->userType));
3252 err = IE_ENUM;
3253 goto leave;
3255 INSERT_STRING(db_user_info, "userType", type_string);
3258 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3259 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3260 INSERT_STRING(db_user_info, "ic", user->ic);
3261 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3262 INSERT_STRING(db_user_info, "firmName", user->firmName);
3263 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3264 INSERT_STRING(db_user_info, "caCity", user->caCity);
3265 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3266 INSERT_STRING(db_user_info, "caState", user->caState);
3268 leave:
3269 free(string);
3270 return err;
3274 /* Convert XSD:tPDZRec XML tree into structure
3275 * @context is ISDS context
3276 * @permission is automatically reallocated commercial permission structure
3277 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3278 * In case of error @permission will be freed. */
3279 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3280 struct isds_commercial_permission **permission,
3281 xmlXPathContextPtr xpath_ctx) {
3282 isds_error err = IE_SUCCESS;
3283 xmlXPathObjectPtr result = NULL;
3284 char *string = NULL;
3286 if (!context) return IE_INVALID_CONTEXT;
3287 if (!permission) return IE_INVAL;
3288 isds_commercial_permission_free(permission);
3289 if (!xpath_ctx) return IE_INVAL;
3292 *permission = calloc(1, sizeof(**permission));
3293 if (!*permission) {
3294 err = IE_NOMEM;
3295 goto leave;
3298 EXTRACT_STRING("isds:PDZType", string);
3299 if (string) {
3300 err = string2isds_payment_type((xmlChar *)string,
3301 &(*permission)->type);
3302 if (err) {
3303 if (err == IE_ENUM) {
3304 err = IE_ISDS;
3305 char *string_locale = _isds_utf82locale(string);
3306 isds_printf_message(context,
3307 _("Unknown isds:PDZType value: %s"), string_locale);
3308 free(string_locale);
3310 goto leave;
3312 zfree(string);
3315 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3316 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3318 EXTRACT_STRING("isds:PDZExpire", string);
3319 if (string) {
3320 err = timestring2timeval((xmlChar *) string,
3321 &((*permission)->expiration));
3322 if (err) {
3323 char *string_locale = _isds_utf82locale(string);
3324 if (err == IE_DATE) err = IE_ISDS;
3325 isds_printf_message(context,
3326 _("Could not convert PDZExpire as ISO time: %s"),
3327 string_locale);
3328 free(string_locale);
3329 goto leave;
3331 zfree(string);
3334 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3335 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3337 leave:
3338 if (err) isds_commercial_permission_free(permission);
3339 free(string);
3340 xmlXPathFreeObject(result);
3341 return err;
3345 /* Convert XSD:tCiRecord XML tree into structure
3346 * @context is ISDS context
3347 * @event is automatically reallocated commercial credit event structure
3348 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3349 * In case of error @event will be freed. */
3350 static isds_error extract_CiRecord(struct isds_ctx *context,
3351 struct isds_credit_event **event,
3352 xmlXPathContextPtr xpath_ctx) {
3353 isds_error err = IE_SUCCESS;
3354 xmlXPathObjectPtr result = NULL;
3355 char *string = NULL;
3356 long int *number_ptr;
3358 if (!context) return IE_INVALID_CONTEXT;
3359 if (!event) return IE_INVAL;
3360 isds_credit_event_free(event);
3361 if (!xpath_ctx) return IE_INVAL;
3364 *event = calloc(1, sizeof(**event));
3365 if (!*event) {
3366 err = IE_NOMEM;
3367 goto leave;
3370 EXTRACT_STRING("isds:ciEventTime", string);
3371 if (string) {
3372 err = timestring2timeval((xmlChar *) string,
3373 &(*event)->time);
3374 if (err) {
3375 char *string_locale = _isds_utf82locale(string);
3376 if (err == IE_DATE) err = IE_ISDS;
3377 isds_printf_message(context,
3378 _("Could not convert ciEventTime as ISO time: %s"),
3379 string_locale);
3380 free(string_locale);
3381 goto leave;
3383 zfree(string);
3386 EXTRACT_STRING("isds:ciEventType", string);
3387 if (string) {
3388 err = string2isds_credit_event_type((xmlChar *)string,
3389 &(*event)->type);
3390 if (err) {
3391 if (err == IE_ENUM) {
3392 err = IE_ISDS;
3393 char *string_locale = _isds_utf82locale(string);
3394 isds_printf_message(context,
3395 _("Unknown isds:ciEventType value: %s"), string_locale);
3396 free(string_locale);
3398 goto leave;
3400 zfree(string);
3403 number_ptr = &((*event)->credit_change);
3404 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3405 number_ptr = &(*event)->new_credit;
3406 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3408 switch((*event)->type) {
3409 case ISDS_CREDIT_CHARGED:
3410 EXTRACT_STRING("isds:ciTransID",
3411 (*event)->details.charged.transaction);
3412 break;
3413 case ISDS_CREDIT_DISCHARGED:
3414 EXTRACT_STRING("isds:ciTransID",
3415 (*event)->details.discharged.transaction);
3416 break;
3417 case ISDS_CREDIT_MESSAGE_SENT:
3418 EXTRACT_STRING("isds:ciRecipientID",
3419 (*event)->details.message_sent.recipient);
3420 EXTRACT_STRING("isds:ciPDZID",
3421 (*event)->details.message_sent.message_id);
3422 break;
3423 case ISDS_CREDIT_STORAGE_SET:
3424 number_ptr = &((*event)->details.storage_set.new_capacity);
3425 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3426 EXTRACT_DATE("isds:ciNewFrom",
3427 (*event)->details.storage_set.new_valid_from);
3428 EXTRACT_DATE("isds:ciNewTo",
3429 (*event)->details.storage_set.new_valid_to);
3430 EXTRACT_LONGINT("isds:ciOldCapacity",
3431 (*event)->details.storage_set.old_capacity, 0);
3432 EXTRACT_DATE("isds:ciOldFrom",
3433 (*event)->details.storage_set.old_valid_from);
3434 EXTRACT_DATE("isds:ciOldTo",
3435 (*event)->details.storage_set.old_valid_to);
3436 EXTRACT_STRING("isds:ciDoneBy",
3437 (*event)->details.storage_set.initiator);
3438 break;
3439 case ISDS_CREDIT_EXPIRED:
3440 break;
3443 leave:
3444 if (err) isds_credit_event_free(event);
3445 free(string);
3446 xmlXPathFreeObject(result);
3447 return err;
3451 #endif /* HAVE_LIBCURL */
3454 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3455 * isds_envelope structure. The envelope is automatically allocated but not
3456 * reallocated. The date are just appended into envelope structure.
3457 * @context is ISDS context
3458 * @envelope is automatically allocated message envelope structure
3459 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3460 * In case of error @envelope will be freed. */
3461 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3462 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3463 isds_error err = IE_SUCCESS;
3464 xmlXPathObjectPtr result = NULL;
3466 if (!context) return IE_INVALID_CONTEXT;
3467 if (!envelope) return IE_INVAL;
3468 if (!xpath_ctx) return IE_INVAL;
3471 if (!*envelope) {
3472 /* Allocate envelope */
3473 *envelope = calloc(1, sizeof(**envelope));
3474 if (!*envelope) {
3475 err = IE_NOMEM;
3476 goto leave;
3478 } else {
3479 /* Else free former data */
3480 zfree((*envelope)->dmSenderOrgUnit);
3481 zfree((*envelope)->dmSenderOrgUnitNum);
3482 zfree((*envelope)->dbIDRecipient);
3483 zfree((*envelope)->dmRecipientOrgUnit);
3484 zfree((*envelope)->dmRecipientOrgUnitNum);
3485 zfree((*envelope)->dmToHands);
3486 zfree((*envelope)->dmAnnotation);
3487 zfree((*envelope)->dmRecipientRefNumber);
3488 zfree((*envelope)->dmSenderRefNumber);
3489 zfree((*envelope)->dmRecipientIdent);
3490 zfree((*envelope)->dmSenderIdent);
3491 zfree((*envelope)->dmLegalTitleLaw);
3492 zfree((*envelope)->dmLegalTitleYear);
3493 zfree((*envelope)->dmLegalTitleSect);
3494 zfree((*envelope)->dmLegalTitlePar);
3495 zfree((*envelope)->dmLegalTitlePoint);
3496 zfree((*envelope)->dmPersonalDelivery);
3497 zfree((*envelope)->dmAllowSubstDelivery);
3500 /* Extract envelope elements added by sender or ISDS
3501 * (XSD: gMessageEnvelopeSub type) */
3502 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3503 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3504 (*envelope)->dmSenderOrgUnitNum, 0);
3505 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3506 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3507 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3508 (*envelope)->dmRecipientOrgUnitNum, 0);
3509 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3510 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3511 EXTRACT_STRING("isds:dmRecipientRefNumber",
3512 (*envelope)->dmRecipientRefNumber);
3513 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3514 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3515 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3517 /* Extract envelope elements regarding law reference */
3518 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3519 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3520 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3521 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3522 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3524 /* Extract envelope other elements */
3525 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3526 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3527 (*envelope)->dmAllowSubstDelivery);
3529 leave:
3530 if (err) isds_envelope_free(envelope);
3531 xmlXPathFreeObject(result);
3532 return err;
3537 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3538 * isds_envelope structure. The envelope is automatically allocated but not
3539 * reallocated. The date are just appended into envelope structure.
3540 * @context is ISDS context
3541 * @envelope is automatically allocated message envelope structure
3542 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3543 * In case of error @envelope will be freed. */
3544 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3545 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3546 isds_error err = IE_SUCCESS;
3547 xmlXPathObjectPtr result = NULL;
3549 if (!context) return IE_INVALID_CONTEXT;
3550 if (!envelope) return IE_INVAL;
3551 if (!xpath_ctx) return IE_INVAL;
3554 if (!*envelope) {
3555 /* Allocate envelope */
3556 *envelope = calloc(1, sizeof(**envelope));
3557 if (!*envelope) {
3558 err = IE_NOMEM;
3559 goto leave;
3561 } else {
3562 /* Else free former data */
3563 zfree((*envelope)->dmID);
3564 zfree((*envelope)->dbIDSender);
3565 zfree((*envelope)->dmSender);
3566 zfree((*envelope)->dmSenderAddress);
3567 zfree((*envelope)->dmSenderType);
3568 zfree((*envelope)->dmRecipient);
3569 zfree((*envelope)->dmRecipientAddress);
3570 zfree((*envelope)->dmAmbiguousRecipient);
3573 /* Extract envelope elements added by ISDS
3574 * (XSD: gMessageEnvelope type) */
3575 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3576 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3577 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3578 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3579 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3580 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3581 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3582 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3583 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3584 (*envelope)->dmAmbiguousRecipient);
3586 /* Extract envelope elements added by sender and ISDS
3587 * (XSD: gMessageEnvelope type) */
3588 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3589 if (err) goto leave;
3591 leave:
3592 if (err) isds_envelope_free(envelope);
3593 xmlXPathFreeObject(result);
3594 return err;
3598 /* Convert other envelope elements from XML tree into isds_envelope structure:
3599 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3600 * The envelope is automatically allocated but not reallocated.
3601 * The data are just appended into envelope structure.
3602 * @context is ISDS context
3603 * @envelope is automatically allocated message envelope structure
3604 * @xpath_ctx is XPath context with current node as parent desired elements
3605 * In case of error @envelope will be freed. */
3606 static isds_error append_status_size_times(struct isds_ctx *context,
3607 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3608 isds_error err = IE_SUCCESS;
3609 xmlXPathObjectPtr result = NULL;
3610 char *string = NULL;
3611 unsigned long int *unumber = NULL;
3613 if (!context) return IE_INVALID_CONTEXT;
3614 if (!envelope) return IE_INVAL;
3615 if (!xpath_ctx) return IE_INVAL;
3618 if (!*envelope) {
3619 /* Allocate new */
3620 *envelope = calloc(1, sizeof(**envelope));
3621 if (!*envelope) {
3622 err = IE_NOMEM;
3623 goto leave;
3625 } else {
3626 /* Free old data */
3627 zfree((*envelope)->dmMessageStatus);
3628 zfree((*envelope)->dmAttachmentSize);
3629 zfree((*envelope)->dmDeliveryTime);
3630 zfree((*envelope)->dmAcceptanceTime);
3634 /* dmMessageStatus element is mandatory */
3635 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3636 if (!unumber) {
3637 isds_log_message(context,
3638 _("Missing mandatory sisds:dmMessageStatus integer"));
3639 err = IE_ISDS;
3640 goto leave;
3642 err = uint2isds_message_status(context, unumber,
3643 &((*envelope)->dmMessageStatus));
3644 if (err) {
3645 if (err == IE_ENUM) err = IE_ISDS;
3646 goto leave;
3648 free(unumber); unumber = NULL;
3650 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3653 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3654 if (string) {
3655 err = timestring2timeval((xmlChar *) string,
3656 &((*envelope)->dmDeliveryTime));
3657 if (err) {
3658 char *string_locale = _isds_utf82locale(string);
3659 if (err == IE_DATE) err = IE_ISDS;
3660 isds_printf_message(context,
3661 _("Could not convert dmDeliveryTime as ISO time: %s"),
3662 string_locale);
3663 free(string_locale);
3664 goto leave;
3666 zfree(string);
3669 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3670 if (string) {
3671 err = timestring2timeval((xmlChar *) string,
3672 &((*envelope)->dmAcceptanceTime));
3673 if (err) {
3674 char *string_locale = _isds_utf82locale(string);
3675 if (err == IE_DATE) err = IE_ISDS;
3676 isds_printf_message(context,
3677 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3678 string_locale);
3679 free(string_locale);
3680 goto leave;
3682 zfree(string);
3685 leave:
3686 if (err) isds_envelope_free(envelope);
3687 free(unumber);
3688 free(string);
3689 xmlXPathFreeObject(result);
3690 return err;
3694 /* Convert message type attribute of current element into isds_envelope
3695 * structure.
3696 * TODO: This function can be incorporated into append_status_size_times() as
3697 * they are called always together.
3698 * The envelope is automatically allocated but not reallocated.
3699 * The data are just appended into envelope structure.
3700 * @context is ISDS context
3701 * @envelope is automatically allocated message envelope structure
3702 * @xpath_ctx is XPath context with current node as parent of attribute
3703 * carrying message type
3704 * In case of error @envelope will be freed. */
3705 static isds_error append_message_type(struct isds_ctx *context,
3706 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3707 isds_error err = IE_SUCCESS;
3709 if (!context) return IE_INVALID_CONTEXT;
3710 if (!envelope) return IE_INVAL;
3711 if (!xpath_ctx) return IE_INVAL;
3714 if (!*envelope) {
3715 /* Allocate new */
3716 *envelope = calloc(1, sizeof(**envelope));
3717 if (!*envelope) {
3718 err = IE_NOMEM;
3719 goto leave;
3721 } else {
3722 /* Free old data */
3723 zfree((*envelope)->dmType);
3727 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3729 if (!(*envelope)->dmType) {
3730 /* Use default value */
3731 (*envelope)->dmType = strdup("V");
3732 if (!(*envelope)->dmType) {
3733 err = IE_NOMEM;
3734 goto leave;
3736 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3737 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3738 isds_printf_message(context,
3739 _("Message type in dmType attribute is not 1 character long: "
3740 "%s"),
3741 type_locale);
3742 free(type_locale);
3743 err = IE_ISDS;
3744 goto leave;
3747 leave:
3748 if (err) isds_envelope_free(envelope);
3749 return err;
3753 #if HAVE_LIBCURL
3754 /* Convert dmType isds_envelope member into XML attribute and append it to
3755 * current node.
3756 * @context is ISDS context
3757 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3758 * @dm_envelope is XML element the resulting attribute will be appended to.
3759 * @return error code, in case of error context' message is filled. */
3760 static isds_error insert_message_type(struct isds_ctx *context,
3761 const char *type, xmlNodePtr dm_envelope) {
3762 isds_error err = IE_SUCCESS;
3763 xmlAttrPtr attribute_node;
3765 if (!context) return IE_INVALID_CONTEXT;
3766 if (!dm_envelope) return IE_INVAL;
3768 /* Insert optional message type */
3769 if (type) {
3770 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3771 char *type_locale = _isds_utf82locale(type);
3772 isds_printf_message(context,
3773 _("Message type in envelope is not 1 character long: %s"),
3774 type_locale);
3775 free(type_locale);
3776 err = IE_INVAL;
3777 goto leave;
3779 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3782 leave:
3783 return err;
3785 #endif /* HAVE_LIBCURL */
3788 /* Extract message document into reallocated document structure
3789 * @context is ISDS context
3790 * @document is automatically reallocated message documents structure
3791 * @xpath_ctx is XPath context with current node as isds:dmFile
3792 * In case of error @document will be freed. */
3793 static isds_error extract_document(struct isds_ctx *context,
3794 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3795 isds_error err = IE_SUCCESS;
3796 xmlXPathObjectPtr result = NULL;
3797 xmlNodePtr file_node;
3798 char *string = NULL;
3800 if (!context) return IE_INVALID_CONTEXT;
3801 if (!document) return IE_INVAL;
3802 isds_document_free(document);
3803 if (!xpath_ctx) return IE_INVAL;
3804 file_node = xpath_ctx->node;
3806 *document = calloc(1, sizeof(**document));
3807 if (!*document) {
3808 err = IE_NOMEM;
3809 goto leave;
3812 /* Extract document meta data */
3813 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3814 if (context->normalize_mime_type) {
3815 const char *normalized_type =
3816 isds_normalize_mime_type((*document)->dmMimeType);
3817 if (NULL != normalized_type &&
3818 normalized_type != (*document)->dmMimeType) {
3819 char *new_type = strdup(normalized_type);
3820 if (NULL == new_type) {
3821 isds_printf_message(context,
3822 _("Not enough memory to normalize document MIME type"));
3823 err = IE_NOMEM;
3824 goto leave;
3826 free((*document)->dmMimeType);
3827 (*document)->dmMimeType = new_type;
3831 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3832 err = string2isds_FileMetaType((xmlChar*)string,
3833 &((*document)->dmFileMetaType));
3834 if (err) {
3835 char *meta_type_locale = _isds_utf82locale(string);
3836 isds_printf_message(context,
3837 _("Document has invalid dmFileMetaType attribute value: %s"),
3838 meta_type_locale);
3839 free(meta_type_locale);
3840 err = IE_ISDS;
3841 goto leave;
3843 zfree(string);
3845 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3846 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3847 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3848 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3851 /* Extract document data.
3852 * Base64 encoded blob or XML subtree must be presented. */
3854 /* Check for dmEncodedContent */
3855 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3856 xpath_ctx);
3857 if (!result) {
3858 err = IE_XML;
3859 goto leave;
3862 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3863 /* Here we have Base64 blob */
3864 (*document)->is_xml = 0;
3866 if (result->nodesetval->nodeNr > 1) {
3867 isds_printf_message(context,
3868 _("Document has more dmEncodedContent elements"));
3869 err = IE_ISDS;
3870 goto leave;
3873 xmlXPathFreeObject(result); result = NULL;
3874 EXTRACT_STRING("isds:dmEncodedContent", string);
3876 /* Decode non-empty document */
3877 if (string && string[0] != '\0') {
3878 (*document)->data_length =
3879 _isds_b64decode(string, &((*document)->data));
3880 if ((*document)->data_length == (size_t) -1) {
3881 isds_printf_message(context,
3882 _("Error while Base64-decoding document content"));
3883 err = IE_ERROR;
3884 goto leave;
3887 } else {
3888 /* No Base64 blob, try XML document */
3889 xmlXPathFreeObject(result); result = NULL;
3890 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3891 xpath_ctx);
3892 if (!result) {
3893 err = IE_XML;
3894 goto leave;
3897 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3898 /* Here we have XML document */
3899 (*document)->is_xml = 1;
3901 if (result->nodesetval->nodeNr > 1) {
3902 isds_printf_message(context,
3903 _("Document has more dmXMLContent elements"));
3904 err = IE_ISDS;
3905 goto leave;
3908 /* XXX: We cannot serialize the content simply because:
3909 * - XML document may point out of its scope (e.g. to message
3910 * envelope)
3911 * - isds:dmXMLContent can contain more elements, no element,
3912 * a text node only
3913 * - it's not the XML way
3914 * Thus we provide the only right solution: XML DOM. Let's
3915 * application to cope with this hot potato :) */
3916 (*document)->xml_node_list =
3917 result->nodesetval->nodeTab[0]->children;
3918 } else {
3919 /* No base64 blob, nor XML document */
3920 isds_printf_message(context,
3921 _("Document has no dmEncodedContent, nor dmXMLContent "
3922 "element"));
3923 err = IE_ISDS;
3924 goto leave;
3929 leave:
3930 if (err) isds_document_free(document);
3931 free(string);
3932 xmlXPathFreeObject(result);
3933 xpath_ctx->node = file_node;
3934 return err;
3939 /* Extract message documents into reallocated list of documents
3940 * @context is ISDS context
3941 * @documents is automatically reallocated message documents list structure
3942 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3943 * In case of error @documents will be freed. */
3944 static isds_error extract_documents(struct isds_ctx *context,
3945 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
3946 isds_error err = IE_SUCCESS;
3947 xmlXPathObjectPtr result = NULL;
3948 xmlNodePtr files_node;
3949 struct isds_list *document, *prev_document = NULL;
3951 if (!context) return IE_INVALID_CONTEXT;
3952 if (!documents) return IE_INVAL;
3953 isds_list_free(documents);
3954 if (!xpath_ctx) return IE_INVAL;
3955 files_node = xpath_ctx->node;
3957 /* Find documents */
3958 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
3959 if (!result) {
3960 err = IE_XML;
3961 goto leave;
3964 /* No match */
3965 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3966 isds_printf_message(context,
3967 _("Message does not contain any document"));
3968 err = IE_ISDS;
3969 goto leave;
3973 /* Iterate over documents */
3974 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3976 /* Allocate and append list item */
3977 document = calloc(1, sizeof(*document));
3978 if (!document) {
3979 err = IE_NOMEM;
3980 goto leave;
3982 document->destructor = (void (*)(void **))isds_document_free;
3983 if (i == 0) *documents = document;
3984 else prev_document->next = document;
3985 prev_document = document;
3987 /* Extract document */
3988 xpath_ctx->node = result->nodesetval->nodeTab[i];
3989 err = extract_document(context,
3990 (struct isds_document **) &(document->data), xpath_ctx);
3991 if (err) goto leave;
3995 leave:
3996 if (err) isds_list_free(documents);
3997 xmlXPathFreeObject(result);
3998 xpath_ctx->node = files_node;
3999 return err;
4003 #if HAVE_LIBCURL
4004 /* Convert isds:dmRecord XML tree into structure
4005 * @context is ISDS context
4006 * @envelope is automatically reallocated message envelope structure
4007 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4008 * In case of error @envelope will be freed. */
4009 static isds_error extract_DmRecord(struct isds_ctx *context,
4010 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4011 isds_error err = IE_SUCCESS;
4012 xmlXPathObjectPtr result = NULL;
4014 if (!context) return IE_INVALID_CONTEXT;
4015 if (!envelope) return IE_INVAL;
4016 isds_envelope_free(envelope);
4017 if (!xpath_ctx) return IE_INVAL;
4020 *envelope = calloc(1, sizeof(**envelope));
4021 if (!*envelope) {
4022 err = IE_NOMEM;
4023 goto leave;
4027 /* Extract tRecord data */
4028 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4030 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4031 * dmAcceptanceTime. */
4032 err = append_status_size_times(context, envelope, xpath_ctx);
4033 if (err) goto leave;
4035 /* Extract envelope elements added by sender and ISDS
4036 * (XSD: gMessageEnvelope type) */
4037 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4038 if (err) goto leave;
4040 /* Get message type */
4041 err = append_message_type(context, envelope, xpath_ctx);
4042 if (err) goto leave;
4045 leave:
4046 if (err) isds_envelope_free(envelope);
4047 xmlXPathFreeObject(result);
4048 return err;
4052 /* Convert XSD:tStateChangesRecord type XML tree into structure
4053 * @context is ISDS context
4054 * @changed_status is automatically reallocated message state change structure
4055 * @xpath_ctx is XPath context with current node as element of
4056 * XSD:tStateChangesRecord type
4057 * In case of error @changed_status will be freed. */
4058 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4059 struct isds_message_status_change **changed_status,
4060 xmlXPathContextPtr xpath_ctx) {
4061 isds_error err = IE_SUCCESS;
4062 xmlXPathObjectPtr result = NULL;
4063 unsigned long int *unumber = NULL;
4064 char *string = NULL;
4066 if (!context) return IE_INVALID_CONTEXT;
4067 if (!changed_status) return IE_INVAL;
4068 isds_message_status_change_free(changed_status);
4069 if (!xpath_ctx) return IE_INVAL;
4072 *changed_status = calloc(1, sizeof(**changed_status));
4073 if (!*changed_status) {
4074 err = IE_NOMEM;
4075 goto leave;
4079 /* Extract tGetStateChangesInput data */
4080 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4082 /* dmEventTime is mandatory */
4083 EXTRACT_STRING("isds:dmEventTime", string);
4084 if (string) {
4085 err = timestring2timeval((xmlChar *) string,
4086 &((*changed_status)->time));
4087 if (err) {
4088 char *string_locale = _isds_utf82locale(string);
4089 if (err == IE_DATE) err = IE_ISDS;
4090 isds_printf_message(context,
4091 _("Could not convert dmEventTime as ISO time: %s"),
4092 string_locale);
4093 free(string_locale);
4094 goto leave;
4096 zfree(string);
4099 /* dmMessageStatus element is mandatory */
4100 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4101 if (!unumber) {
4102 isds_log_message(context,
4103 _("Missing mandatory isds:dmMessageStatus integer"));
4104 err = IE_ISDS;
4105 goto leave;
4107 err = uint2isds_message_status(context, unumber,
4108 &((*changed_status)->dmMessageStatus));
4109 if (err) {
4110 if (err == IE_ENUM) err = IE_ISDS;
4111 goto leave;
4113 zfree(unumber);
4116 leave:
4117 free(unumber);
4118 free(string);
4119 if (err) isds_message_status_change_free(changed_status);
4120 xmlXPathFreeObject(result);
4121 return err;
4123 #endif /* HAVE_LIBCURL */
4126 /* Find and convert isds:dmHash XML tree into structure
4127 * @context is ISDS context
4128 * @envelope is automatically reallocated message hash structure
4129 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4130 * In case of error @hash will be freed. */
4131 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4132 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4133 isds_error err = IE_SUCCESS;
4134 xmlNodePtr old_ctx_node;
4135 xmlXPathObjectPtr result = NULL;
4136 char *string = NULL;
4138 if (!context) return IE_INVALID_CONTEXT;
4139 if (!hash) return IE_INVAL;
4140 isds_hash_free(hash);
4141 if (!xpath_ctx) return IE_INVAL;
4143 old_ctx_node = xpath_ctx->node;
4145 *hash = calloc(1, sizeof(**hash));
4146 if (!*hash) {
4147 err = IE_NOMEM;
4148 goto leave;
4151 /* Locate dmHash */
4152 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4153 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4154 err = IE_ISDS;
4155 goto leave;
4157 if (err) {
4158 err = IE_ERROR;
4159 goto leave;
4162 /* Get hash algorithm */
4163 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4164 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4165 if (err) {
4166 if (err == IE_ENUM) {
4167 char *string_locale = _isds_utf82locale(string);
4168 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4169 string_locale);
4170 free(string_locale);
4172 goto leave;
4174 zfree(string);
4176 /* Get hash value */
4177 EXTRACT_STRING(".", string);
4178 if (!string) {
4179 isds_printf_message(context,
4180 _("sisds:dmHash element is missing hash value"));
4181 err = IE_ISDS;
4182 goto leave;
4184 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4185 if ((*hash)->length == (size_t) -1) {
4186 isds_printf_message(context,
4187 _("Error while Base64-decoding hash value"));
4188 err = IE_ERROR;
4189 goto leave;
4192 leave:
4193 if (err) isds_hash_free(hash);
4194 free(string);
4195 xmlXPathFreeObject(result);
4196 xpath_ctx->node = old_ctx_node;
4197 return err;
4201 /* Find and append isds:dmQTimestamp XML tree into envelope.
4202 * Because one service is allowed to miss time-stamp content, and we think
4203 * other could too (flaw in specification), this function is deliberated and
4204 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4205 * @context is ISDS context
4206 * @envelope is automatically allocated envelope structure
4207 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4208 * child
4209 * In case of error @envelope will be freed. */
4210 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4211 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4212 isds_error err = IE_SUCCESS;
4213 xmlXPathObjectPtr result = NULL;
4214 char *string = NULL;
4216 if (!context) return IE_INVALID_CONTEXT;
4217 if (!envelope) return IE_INVAL;
4218 if (!xpath_ctx) {
4219 isds_envelope_free(envelope);
4220 return IE_INVAL;
4223 if (!*envelope) {
4224 *envelope = calloc(1, sizeof(**envelope));
4225 if (!*envelope) {
4226 err = IE_NOMEM;
4227 goto leave;
4229 } else {
4230 zfree((*envelope)->timestamp);
4231 (*envelope)->timestamp_length = 0;
4234 /* Get dmQTimestamp */
4235 EXTRACT_STRING("sisds:dmQTimestamp", string);
4236 if (!string) {
4237 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4238 goto leave;
4240 (*envelope)->timestamp_length =
4241 _isds_b64decode(string, &((*envelope)->timestamp));
4242 if ((*envelope)->timestamp_length == (size_t) -1) {
4243 isds_printf_message(context,
4244 _("Error while Base64-decoding time stamp value"));
4245 err = IE_ERROR;
4246 goto leave;
4249 leave:
4250 if (err) isds_envelope_free(envelope);
4251 free(string);
4252 xmlXPathFreeObject(result);
4253 return err;
4257 /* Convert XSD tReturnedMessage XML tree into message structure.
4258 * It does not store serialized XML tree into message->raw.
4259 * It does store (pointer to) parsed XML tree into message->xml if needed.
4260 * @context is ISDS context
4261 * @include_documents Use true if documents must be extracted
4262 * (tReturnedMessage XSD type), use false if documents shall be omitted
4263 * (tReturnedMessageEnvelope).
4264 * @message is automatically reallocated message structure
4265 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4266 * type
4267 * In case of error @message will be freed. */
4268 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4269 const _Bool include_documents, struct isds_message **message,
4270 xmlXPathContextPtr xpath_ctx) {
4271 isds_error err = IE_SUCCESS;
4272 xmlNodePtr message_node;
4274 if (!context) return IE_INVALID_CONTEXT;
4275 if (!message) return IE_INVAL;
4276 isds_message_free(message);
4277 if (!xpath_ctx) return IE_INVAL;
4280 *message = calloc(1, sizeof(**message));
4281 if (!*message) {
4282 err = IE_NOMEM;
4283 goto leave;
4286 /* Save message XPATH context node */
4287 message_node = xpath_ctx->node;
4290 /* Extract dmDM */
4291 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4292 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4293 if (err) { err = IE_ERROR; goto leave; }
4294 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4295 if (err) goto leave;
4297 if (include_documents) {
4298 struct isds_list *item;
4300 /* Extract dmFiles */
4301 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4302 xpath_ctx);
4303 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4304 err = IE_ISDS; goto leave;
4306 if (err) { err = IE_ERROR; goto leave; }
4307 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4308 if (err) goto leave;
4310 /* Store xmlDoc of this message if needed */
4311 /* Only if we got a XML document in all the documents. */
4312 for (item = (*message)->documents; item; item = item->next) {
4313 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4314 (*message)->xml = xpath_ctx->doc;
4315 break;
4321 /* Restore context to message */
4322 xpath_ctx->node = message_node;
4324 /* Extract dmHash */
4325 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4326 xpath_ctx);
4327 if (err) goto leave;
4329 /* Extract dmQTimestamp, */
4330 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4331 xpath_ctx);
4332 if (err) goto leave;
4334 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4335 * dmAcceptanceTime. */
4336 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4337 if (err) goto leave;
4339 /* Get message type */
4340 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4341 if (err) goto leave;
4343 leave:
4344 if (err) isds_message_free(message);
4345 return err;
4349 /* Extract message event into reallocated isds_event structure
4350 * @context is ISDS context
4351 * @event is automatically reallocated message event structure
4352 * @xpath_ctx is XPath context with current node as isds:dmEvent
4353 * In case of error @event will be freed. */
4354 static isds_error extract_event(struct isds_ctx *context,
4355 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4356 isds_error err = IE_SUCCESS;
4357 xmlXPathObjectPtr result = NULL;
4358 xmlNodePtr event_node;
4359 char *string = NULL;
4361 if (!context) return IE_INVALID_CONTEXT;
4362 if (!event) return IE_INVAL;
4363 isds_event_free(event);
4364 if (!xpath_ctx) return IE_INVAL;
4365 event_node = xpath_ctx->node;
4367 *event = calloc(1, sizeof(**event));
4368 if (!*event) {
4369 err = IE_NOMEM;
4370 goto leave;
4373 /* Extract event data.
4374 * All elements are optional according XSD. That's funny. */
4375 EXTRACT_STRING("sisds:dmEventTime", string);
4376 if (string) {
4377 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4378 if (err) {
4379 char *string_locale = _isds_utf82locale(string);
4380 if (err == IE_DATE) err = IE_ISDS;
4381 isds_printf_message(context,
4382 _("Could not convert dmEventTime as ISO time: %s"),
4383 string_locale);
4384 free(string_locale);
4385 goto leave;
4387 zfree(string);
4390 /* dmEventDescr element has prefix and the rest */
4391 EXTRACT_STRING("sisds:dmEventDescr", string);
4392 if (string) {
4393 err = eventstring2event((xmlChar *) string, *event);
4394 if (err) goto leave;
4395 zfree(string);
4398 leave:
4399 if (err) isds_event_free(event);
4400 free(string);
4401 xmlXPathFreeObject(result);
4402 xpath_ctx->node = event_node;
4403 return err;
4407 /* Convert element of XSD tEventsArray type from XML tree into
4408 * isds_list of isds_event's structure. The list is automatically reallocated.
4409 * @context is ISDS context
4410 * @events is automatically reallocated list of event structures
4411 * @xpath_ctx is XPath context with current node as tEventsArray
4412 * In case of error @events will be freed. */
4413 static isds_error extract_events(struct isds_ctx *context,
4414 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4415 isds_error err = IE_SUCCESS;
4416 xmlXPathObjectPtr result = NULL;
4417 xmlNodePtr events_node;
4418 struct isds_list *event, *prev_event = NULL;
4420 if (!context) return IE_INVALID_CONTEXT;
4421 if (!events) return IE_INVAL;
4422 if (!xpath_ctx) return IE_INVAL;
4423 events_node = xpath_ctx->node;
4425 /* Free old list */
4426 isds_list_free(events);
4428 /* Find events */
4429 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4430 if (!result) {
4431 err = IE_XML;
4432 goto leave;
4435 /* No match */
4436 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4437 isds_printf_message(context,
4438 _("Delivery info does not contain any event"));
4439 err = IE_ISDS;
4440 goto leave;
4444 /* Iterate over events */
4445 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4447 /* Allocate and append list item */
4448 event = calloc(1, sizeof(*event));
4449 if (!event) {
4450 err = IE_NOMEM;
4451 goto leave;
4453 event->destructor = (void (*)(void **))isds_event_free;
4454 if (i == 0) *events = event;
4455 else prev_event->next = event;
4456 prev_event = event;
4458 /* Extract event */
4459 xpath_ctx->node = result->nodesetval->nodeTab[i];
4460 err = extract_event(context,
4461 (struct isds_event **) &(event->data), xpath_ctx);
4462 if (err) goto leave;
4466 leave:
4467 if (err) isds_list_free(events);
4468 xmlXPathFreeObject(result);
4469 xpath_ctx->node = events_node;
4470 return err;
4474 #if HAVE_LIBCURL
4475 /* Insert Base64 encoded data as element with text child.
4476 * @context is session context
4477 * @parent is XML node to append @element with @data as child
4478 * @ns is XML namespace of @element, use NULL to inherit from @parent
4479 * @element is UTF-8 encoded name of new element
4480 * @data is bit stream to encode into @element
4481 * @length is size of @data in bytes
4482 * @return standard error code and fill long error message if needed */
4483 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4484 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4485 const void *data, size_t length) {
4486 isds_error err = IE_SUCCESS;
4487 xmlNodePtr node;
4489 if (!context) return IE_INVALID_CONTEXT;
4490 if (!data && length > 0) return IE_INVAL;
4491 if (!parent || !element) return IE_INVAL;
4493 xmlChar *base64data = NULL;
4494 base64data = (xmlChar *) _isds_b64encode(data, length);
4495 if (!base64data) {
4496 isds_printf_message(context,
4497 ngettext("Not enough memory to encode %zd byte into Base64",
4498 "Not enough memory to encode %zd bytes into Base64",
4499 length),
4500 length);
4501 err = IE_NOMEM;
4502 goto leave;
4504 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4506 leave:
4507 free(base64data);
4508 return err;
4512 /* Convert isds_document structure into XML tree and append to dmFiles node.
4513 * @context is session context
4514 * @document is ISDS document
4515 * @dm_files is XML element the resulting tree will be appended to as a child.
4516 * @return error code, in case of error context' message is filled. */
4517 static isds_error insert_document(struct isds_ctx *context,
4518 struct isds_document *document, xmlNodePtr dm_files) {
4519 isds_error err = IE_SUCCESS;
4520 xmlNodePtr new_file = NULL, file = NULL, node;
4521 xmlAttrPtr attribute_node;
4523 if (!context) return IE_INVALID_CONTEXT;
4524 if (!document || !dm_files) return IE_INVAL;
4526 /* Allocate new dmFile */
4527 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4528 if (!new_file) {
4529 isds_printf_message(context, _("Could not allocate main dmFile"));
4530 err = IE_ERROR;
4531 goto leave;
4533 /* Append the new dmFile.
4534 * XXX: Main document must go first */
4535 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4536 file = xmlAddPrevSibling(dm_files->children, new_file);
4537 else
4538 file = xmlAddChild(dm_files, new_file);
4540 if (!file) {
4541 xmlFreeNode(new_file); new_file = NULL;
4542 isds_printf_message(context, _("Could not add dmFile child to "
4543 "%s element"), dm_files->name);
4544 err = IE_ERROR;
4545 goto leave;
4548 /* @dmMimeType is required */
4549 if (!document->dmMimeType) {
4550 isds_log_message(context,
4551 _("Document is missing mandatory MIME type definition"));
4552 err = IE_INVAL;
4553 goto leave;
4555 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4557 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4558 if (!string) {
4559 isds_printf_message(context,
4560 _("Document has unknown dmFileMetaType: %ld"),
4561 document->dmFileMetaType);
4562 err = IE_ENUM;
4563 goto leave;
4565 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4567 if (document->dmFileGuid) {
4568 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4570 if (document->dmUpFileGuid) {
4571 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4574 /* @dmFileDescr is required */
4575 if (!document->dmFileDescr) {
4576 isds_log_message(context,
4577 _("Document is missing mandatory description (title)"));
4578 err = IE_INVAL;
4579 goto leave;
4581 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4583 if (document->dmFormat) {
4584 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4588 /* Insert content (body) of the document. */
4589 if (document->is_xml) {
4590 /* XML document requested */
4592 /* Allocate new dmXMLContent */
4593 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4594 if (!xmlcontent) {
4595 isds_printf_message(context,
4596 _("Could not allocate dmXMLContent element"));
4597 err = IE_ERROR;
4598 goto leave;
4600 /* Append it */
4601 node = xmlAddChild(file, xmlcontent);
4602 if (!node) {
4603 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4604 isds_printf_message(context,
4605 _("Could not add dmXMLContent child to %s element"),
4606 file->name);
4607 err = IE_ERROR;
4608 goto leave;
4611 /* Copy non-empty node list */
4612 if (document->xml_node_list) {
4613 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4614 document->xml_node_list);
4615 if (!content) {
4616 isds_printf_message(context,
4617 _("Not enough memory to copy XML document"));
4618 err = IE_NOMEM;
4619 goto leave;
4622 if (!xmlAddChildList(node, content)) {
4623 xmlFreeNodeList(content);
4624 isds_printf_message(context,
4625 _("Error while adding XML document into dmXMLContent"));
4626 err = IE_XML;
4627 goto leave;
4629 /* XXX: We cannot free the content here because it's part of node's
4630 * document since now. It will be freed with it automatically. */
4632 } else {
4633 /* Binary document requested */
4634 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4635 document->data, document->data_length);
4636 if (err) goto leave;
4639 leave:
4640 return err;
4644 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4645 * The copy must be preallocated, the date are just appended into structure.
4646 * @context is ISDS context
4647 * @copy is message copy structure
4648 * @xpath_ctx is XPath context with current node as tMStatus */
4649 static isds_error append_TMStatus(struct isds_ctx *context,
4650 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4651 isds_error err = IE_SUCCESS;
4652 xmlXPathObjectPtr result = NULL;
4653 char *code = NULL, *message = NULL;
4655 if (!context) return IE_INVALID_CONTEXT;
4656 if (!copy || !xpath_ctx) return IE_INVAL;
4658 /* Free old values */
4659 zfree(copy->dmStatus);
4660 zfree(copy->dmID);
4662 /* Get error specific to this copy */
4663 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4664 if (!code) {
4665 isds_log_message(context,
4666 _("Missing isds:dmStatusCode under "
4667 "XSD:tMStatus type element"));
4668 err = IE_ISDS;
4669 goto leave;
4672 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4673 /* This copy failed */
4674 copy->error = IE_ISDS;
4675 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4676 if (message) {
4677 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4678 if (!copy->dmStatus) {
4679 copy->dmStatus = code;
4680 code = NULL;
4682 } else {
4683 copy->dmStatus = code;
4684 code = NULL;
4686 } else {
4687 /* This copy succeeded. In this case only, message ID is valid */
4688 copy->error = IE_SUCCESS;
4690 EXTRACT_STRING("isds:dmID", copy->dmID);
4691 if (!copy->dmID) {
4692 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4693 "but did not returned assigned message ID\n"));
4694 err = IE_ISDS;
4698 leave:
4699 free(code);
4700 free(message);
4701 xmlXPathFreeObject(result);
4702 return err;
4706 /* Insert struct isds_approval data (box approval) into XML tree
4707 * @context is session context
4708 * @approval is libisds structure with approval description. NULL is
4709 * acceptable.
4710 * @parent is XML element to append @approval to */
4711 static isds_error insert_GExtApproval(struct isds_ctx *context,
4712 const struct isds_approval *approval, xmlNodePtr parent) {
4714 isds_error err = IE_SUCCESS;
4715 xmlNodePtr node;
4717 if (!context) return IE_INVALID_CONTEXT;
4718 if (!parent) return IE_INVAL;
4720 if (!approval) return IE_SUCCESS;
4722 /* Build XSD:gExtApproval */
4723 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4724 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4726 leave:
4727 return err;
4731 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4732 * code
4733 * @context is session context
4734 * @service_name is name of SERVICE_DB_ACCESS
4735 * @response is reallocated server SOAP body response as XML document
4736 * @raw_response is reallocated bit stream with response body. Use
4737 * NULL if you don't care
4738 * @raw_response_length is size of @raw_response in bytes
4739 * @code is reallocated ISDS status code
4740 * @status_message is reallocated ISDS status message
4741 * @return error coded from lower layer, context message will be set up
4742 * appropriately. */
4743 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4744 const xmlChar *service_name,
4745 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4746 xmlChar **code, xmlChar **status_message) {
4748 isds_error err = IE_SUCCESS;
4749 char *service_name_locale = NULL;
4750 xmlNodePtr request = NULL, node;
4751 xmlNsPtr isds_ns = NULL;
4753 if (!context) return IE_INVALID_CONTEXT;
4754 if (!service_name) return IE_INVAL;
4755 if (!response || !code || !status_message) return IE_INVAL;
4756 if (!raw_response_length && raw_response) return IE_INVAL;
4758 /* Free output argument */
4759 xmlFreeDoc(*response); *response = NULL;
4760 if (raw_response) zfree(*raw_response);
4761 zfree(*code);
4762 zfree(*status_message);
4765 /* Check if connection is established
4766 * TODO: This check should be done downstairs. */
4767 if (!context->curl) return IE_CONNECTION_CLOSED;
4769 service_name_locale = _isds_utf82locale((char*)service_name);
4770 if (!service_name_locale) {
4771 err = IE_NOMEM;
4772 goto leave;
4775 /* Build request */
4776 request = xmlNewNode(NULL, service_name);
4777 if (!request) {
4778 isds_printf_message(context,
4779 _("Could not build %s request"), service_name_locale);
4780 err = IE_ERROR;
4781 goto leave;
4783 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4784 if(!isds_ns) {
4785 isds_log_message(context, _("Could not create ISDS name space"));
4786 err = IE_ERROR;
4787 goto leave;
4789 xmlSetNs(request, isds_ns);
4792 /* Add XSD:tDummyInput child */
4793 INSERT_STRING(request, "dbDummy", NULL);
4796 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4797 service_name_locale);
4799 /* Send request */
4800 err = isds(context, SERVICE_DB_ACCESS, request, response,
4801 raw_response, raw_response_length);
4802 xmlFreeNode(request); request = NULL;
4804 if (err) {
4805 isds_log(ILF_ISDS, ILL_DEBUG,
4806 _("Processing ISDS response on %s request failed\n"),
4807 service_name_locale);
4808 goto leave;
4811 /* Check for response status */
4812 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4813 code, status_message, NULL);
4814 if (err) {
4815 isds_log(ILF_ISDS, ILL_DEBUG,
4816 _("ISDS response on %s request is missing status\n"),
4817 service_name_locale);
4818 goto leave;
4821 /* Request processed, but nothing found */
4822 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4823 char *code_locale = _isds_utf82locale((char*) *code);
4824 char *status_message_locale =
4825 _isds_utf82locale((char*) *status_message);
4826 isds_log(ILF_ISDS, ILL_DEBUG,
4827 _("Server refused %s request (code=%s, message=%s)\n"),
4828 service_name_locale, code_locale, status_message_locale);
4829 isds_log_message(context, status_message_locale);
4830 free(code_locale);
4831 free(status_message_locale);
4832 err = IE_ISDS;
4833 goto leave;
4836 leave:
4837 free(service_name_locale);
4838 xmlFreeNode(request);
4839 return err;
4841 #endif
4844 /* Get data about logged in user and his box. */
4845 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4846 struct isds_DbOwnerInfo **db_owner_info) {
4847 isds_error err = IE_SUCCESS;
4848 #if HAVE_LIBCURL
4849 xmlDocPtr response = NULL;
4850 xmlChar *code = NULL, *message = NULL;
4851 xmlXPathContextPtr xpath_ctx = NULL;
4852 xmlXPathObjectPtr result = NULL;
4853 char *string = NULL;
4854 #endif
4856 if (!context) return IE_INVALID_CONTEXT;
4857 zfree(context->long_message);
4858 if (!db_owner_info) return IE_INVAL;
4859 isds_DbOwnerInfo_free(db_owner_info);
4861 #if HAVE_LIBCURL
4862 /* Check if connection is established */
4863 if (!context->curl) return IE_CONNECTION_CLOSED;
4866 /* Do request and check for success */
4867 err = build_send_check_dbdummy_request(context,
4868 BAD_CAST "GetOwnerInfoFromLogin",
4869 &response, NULL, NULL, &code, &message);
4870 if (err) goto leave;
4873 /* Extract data */
4874 /* Prepare structure */
4875 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4876 if (!*db_owner_info) {
4877 err = IE_NOMEM;
4878 goto leave;
4880 xpath_ctx = xmlXPathNewContext(response);
4881 if (!xpath_ctx) {
4882 err = IE_ERROR;
4883 goto leave;
4885 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4886 err = IE_ERROR;
4887 goto leave;
4890 /* Set context node */
4891 result = xmlXPathEvalExpression(BAD_CAST
4892 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4893 if (!result) {
4894 err = IE_ERROR;
4895 goto leave;
4897 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4898 isds_log_message(context, _("Missing dbOwnerInfo element"));
4899 err = IE_ISDS;
4900 goto leave;
4902 if (result->nodesetval->nodeNr > 1) {
4903 isds_log_message(context, _("Multiple dbOwnerInfo element"));
4904 err = IE_ISDS;
4905 goto leave;
4907 xpath_ctx->node = result->nodesetval->nodeTab[0];
4908 xmlXPathFreeObject(result); result = NULL;
4910 /* Extract it */
4911 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
4914 leave:
4915 if (err) {
4916 isds_DbOwnerInfo_free(db_owner_info);
4919 free(string);
4920 xmlXPathFreeObject(result);
4921 xmlXPathFreeContext(xpath_ctx);
4923 free(code);
4924 free(message);
4925 xmlFreeDoc(response);
4927 if (!err)
4928 isds_log(ILF_ISDS, ILL_DEBUG,
4929 _("GetOwnerInfoFromLogin request processed by server "
4930 "successfully.\n"));
4931 #else /* not HAVE_LIBCURL */
4932 err = IE_NOTSUP;
4933 #endif
4935 return err;
4939 /* Get data about logged in user. */
4940 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
4941 struct isds_DbUserInfo **db_user_info) {
4942 isds_error err = IE_SUCCESS;
4943 #if HAVE_LIBCURL
4944 xmlDocPtr response = NULL;
4945 xmlChar *code = NULL, *message = NULL;
4946 xmlXPathContextPtr xpath_ctx = NULL;
4947 xmlXPathObjectPtr result = NULL;
4948 #endif
4950 if (!context) return IE_INVALID_CONTEXT;
4951 zfree(context->long_message);
4952 if (!db_user_info) return IE_INVAL;
4953 isds_DbUserInfo_free(db_user_info);
4955 #if HAVE_LIBCURL
4956 /* Check if connection is established */
4957 if (!context->curl) return IE_CONNECTION_CLOSED;
4960 /* Do request and check for success */
4961 err = build_send_check_dbdummy_request(context,
4962 BAD_CAST "GetUserInfoFromLogin",
4963 &response, NULL, NULL, &code, &message);
4964 if (err) goto leave;
4967 /* Extract data */
4968 /* Prepare structure */
4969 *db_user_info = calloc(1, sizeof(**db_user_info));
4970 if (!*db_user_info) {
4971 err = IE_NOMEM;
4972 goto leave;
4974 xpath_ctx = xmlXPathNewContext(response);
4975 if (!xpath_ctx) {
4976 err = IE_ERROR;
4977 goto leave;
4979 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4980 err = IE_ERROR;
4981 goto leave;
4984 /* Set context node */
4985 result = xmlXPathEvalExpression(BAD_CAST
4986 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
4987 if (!result) {
4988 err = IE_ERROR;
4989 goto leave;
4991 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4992 isds_log_message(context, _("Missing dbUserInfo element"));
4993 err = IE_ISDS;
4994 goto leave;
4996 if (result->nodesetval->nodeNr > 1) {
4997 isds_log_message(context, _("Multiple dbUserInfo element"));
4998 err = IE_ISDS;
4999 goto leave;
5001 xpath_ctx->node = result->nodesetval->nodeTab[0];
5002 xmlXPathFreeObject(result); result = NULL;
5004 /* Extract it */
5005 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5007 leave:
5008 if (err) {
5009 isds_DbUserInfo_free(db_user_info);
5012 xmlXPathFreeObject(result);
5013 xmlXPathFreeContext(xpath_ctx);
5015 free(code);
5016 free(message);
5017 xmlFreeDoc(response);
5019 if (!err)
5020 isds_log(ILF_ISDS, ILL_DEBUG,
5021 _("GetUserInfoFromLogin request processed by server "
5022 "successfully.\n"));
5023 #else /* not HAVE_LIBCURL */
5024 err = IE_NOTSUP;
5025 #endif
5027 return err;
5031 /* Get expiration time of current password
5032 * @context is session context
5033 * @expiration is automatically reallocated time when password expires. If
5034 * password expiration is disabled, NULL will be returned. In case of error
5035 * it will be nulled too. */
5036 isds_error isds_get_password_expiration(struct isds_ctx *context,
5037 struct timeval **expiration) {
5038 isds_error err = IE_SUCCESS;
5039 #if HAVE_LIBCURL
5040 xmlDocPtr response = NULL;
5041 xmlChar *code = NULL, *message = NULL;
5042 xmlXPathContextPtr xpath_ctx = NULL;
5043 xmlXPathObjectPtr result = NULL;
5044 char *string = NULL;
5045 #endif
5047 if (!context) return IE_INVALID_CONTEXT;
5048 zfree(context->long_message);
5049 if (!expiration) return IE_INVAL;
5050 zfree(*expiration);
5052 #if HAVE_LIBCURL
5053 /* Check if connection is established */
5054 if (!context->curl) return IE_CONNECTION_CLOSED;
5057 /* Do request and check for success */
5058 err = build_send_check_dbdummy_request(context,
5059 BAD_CAST "GetPasswordInfo",
5060 &response, NULL, NULL, &code, &message);
5061 if (err) goto leave;
5064 /* Extract data */
5065 xpath_ctx = xmlXPathNewContext(response);
5066 if (!xpath_ctx) {
5067 err = IE_ERROR;
5068 goto leave;
5070 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5071 err = IE_ERROR;
5072 goto leave;
5075 /* Set context node */
5076 result = xmlXPathEvalExpression(BAD_CAST
5077 "/isds:GetPasswordInfoResponse", xpath_ctx);
5078 if (!result) {
5079 err = IE_ERROR;
5080 goto leave;
5082 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5083 isds_log_message(context,
5084 _("Missing GetPasswordInfoResponse element"));
5085 err = IE_ISDS;
5086 goto leave;
5088 if (result->nodesetval->nodeNr > 1) {
5089 isds_log_message(context,
5090 _("Multiple GetPasswordInfoResponse element"));
5091 err = IE_ISDS;
5092 goto leave;
5094 xpath_ctx->node = result->nodesetval->nodeTab[0];
5095 xmlXPathFreeObject(result); result = NULL;
5097 /* Extract expiration date */
5098 EXTRACT_STRING("isds:pswExpDate", string);
5099 if (string) {
5100 /* And convert it if any returned. Otherwise expiration is disabled. */
5101 err = timestring2timeval((xmlChar *) string, expiration);
5102 if (err) {
5103 char *string_locale = _isds_utf82locale(string);
5104 if (err == IE_DATE) err = IE_ISDS;
5105 isds_printf_message(context,
5106 _("Could not convert pswExpDate as ISO time: %s"),
5107 string_locale);
5108 free(string_locale);
5109 goto leave;
5113 leave:
5114 if (err) {
5115 if (*expiration) {
5116 zfree(*expiration);
5120 free(string);
5121 xmlXPathFreeObject(result);
5122 xmlXPathFreeContext(xpath_ctx);
5124 free(code);
5125 free(message);
5126 xmlFreeDoc(response);
5128 if (!err)
5129 isds_log(ILF_ISDS, ILL_DEBUG,
5130 _("GetPasswordInfo request processed by server "
5131 "successfully.\n"));
5132 #else /* not HAVE_LIBCURL */
5133 err = IE_NOTSUP;
5134 #endif
5136 return err;
5140 #if HAVE_LIBCURL
5141 /* Request delivering new TOTP code from ISDS through side channel before
5142 * changing password.
5143 * @context is session context
5144 * @password is current password.
5145 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5146 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5147 * function for more details.
5148 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5149 * NULL, if you don't care.
5150 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5151 * error code. */
5152 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5153 const char *password, struct isds_otp *otp, char **refnumber) {
5154 isds_error err = IE_SUCCESS;
5155 char *saved_url = NULL; /* No copy */
5156 #if HAVE_CURL_REAUTHORIZATION_BUG
5157 CURL *saved_curl = NULL; /* No copy */
5158 #endif
5159 xmlNsPtr isds_ns = NULL;
5160 xmlNodePtr request = NULL;
5161 xmlDocPtr response = NULL;
5162 xmlChar *code = NULL, *message = NULL;
5163 const xmlChar *codes[] = {
5164 BAD_CAST "2300",
5165 BAD_CAST "2301",
5166 BAD_CAST "2302"
5168 const char *meanings[] = {
5169 N_("Unexpected error"),
5170 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5171 N_("One-time code could not been sent. Try later again.")
5173 const isds_otp_resolution resolutions[] = {
5174 OTP_RESOLUTION_UNKNOWN,
5175 OTP_RESOLUTION_TO_FAST,
5176 OTP_RESOLUTION_TOTP_NOT_SENT
5179 if (NULL == context) return IE_INVALID_CONTEXT;
5180 zfree(context->long_message);
5181 if (NULL == password) {
5182 isds_log_message(context,
5183 _("Second argument (password) of isds_change_password() "
5184 "is NULL"));
5185 return IE_INVAL;
5188 /* Check if connection is established
5189 * TODO: This check should be done downstairs. */
5190 if (!context->curl) return IE_CONNECTION_CLOSED;
5192 if (!context->otp) {
5193 isds_log_message(context, _("This function requires OTP-authenticated "
5194 "context"));
5195 return IE_INVALID_CONTEXT;
5197 if (NULL == otp) {
5198 isds_log_message(context, _("If one-time password authentication "
5199 "method is in use, requesting new OTP code requires "
5200 "one-time credentials argument either"));
5201 return IE_INVAL;
5203 if (otp->method != OTP_TIME) {
5204 isds_log_message(context, _("Requesting new time-based OTP code from "
5205 "server requires one-time password authentication "
5206 "method"));
5207 return IE_INVAL;
5209 if (otp->otp_code != NULL) {
5210 isds_log_message(context, _("Requesting new time-based OTP code from "
5211 "server requires undefined OTP code member in "
5212 "one-time credentials argument"));
5213 return IE_INVAL;
5217 /* Build request */
5218 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5219 if (!request) {
5220 isds_log_message(context, _("Could not build SendSMSCode request"));
5221 return IE_ERROR;
5223 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5224 if(!isds_ns) {
5225 isds_log_message(context, _("Could not create ISDS name space"));
5226 xmlFreeNode(request);
5227 return IE_ERROR;
5229 xmlSetNs(request, isds_ns);
5231 /* Change URL temporarily for sending this request only */
5233 char *new_url = NULL;
5234 if ((err = _isds_build_url_from_context(context,
5235 "%1$.*2$sasws/changePassword", &new_url))) {
5236 goto leave;
5238 saved_url = context->url;
5239 context->url = new_url;
5242 /* Store credentials for sending this request only */
5243 context->otp_credentials = otp;
5244 _isds_discard_credentials(context, 0);
5245 if ((err = _isds_store_credentials(context, context->saved_username,
5246 password, NULL))) {
5247 _isds_discard_credentials(context, 0);
5248 goto leave;
5250 #if HAVE_CURL_REAUTHORIZATION_BUG
5251 saved_curl = context->curl;
5252 context->curl = curl_easy_init();
5253 if (NULL == context->curl) {
5254 err = IE_ERROR;
5255 goto leave;
5257 if (context->timeout) {
5258 err = isds_set_timeout(context, context->timeout);
5259 if (err) goto leave;
5261 #endif
5263 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5265 /* Sent request */
5266 err = isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5268 /* Remove temporal credentials */
5269 _isds_discard_credentials(context, 0);
5270 /* Detach pointer to OTP credentials from context */
5271 context->otp_credentials = NULL;
5272 /* Keep context->otp true to keep signaling this is OTP session */
5274 /* Destroy request */
5275 xmlFreeNode(request); request = NULL;
5277 if (err) {
5278 isds_log(ILF_ISDS, ILL_DEBUG,
5279 _("Processing ISDS response on SendSMSCode request failed\n"));
5280 goto leave;
5283 /* Check for response status */
5284 err = isds_response_status(context, SERVICE_ASWS, response,
5285 &code, &message, (xmlChar **)refnumber);
5286 if (err) {
5287 isds_log(ILF_ISDS, ILL_DEBUG,
5288 _("ISDS response on SendSMSCode request is missing "
5289 "status\n"));
5290 goto leave;
5293 /* Check for error */
5294 if (xmlStrcmp(code, BAD_CAST "0000")) {
5295 char *code_locale = _isds_utf82locale((char*)code);
5296 char *message_locale = _isds_utf82locale((char*)message);
5297 int i;
5298 isds_log(ILF_ISDS, ILL_DEBUG,
5299 _("Server refused to send new code on SendSMSCode "
5300 "request (code=%s, message=%s)\n"),
5301 code_locale, message_locale);
5303 /* Check for known error codes */
5304 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5305 if (!xmlStrcmp(code, codes[i])) break;
5307 if (i < sizeof(codes)/sizeof(*codes)) {
5308 isds_log_message(context, _(meanings[i]));
5309 /* Mimic otp->resolution according to the code, specification does
5310 * prescribe OTP header to be available. */
5311 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5312 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5313 otp->resolution = resolutions[i];
5314 } else
5315 isds_log_message(context, message_locale);
5317 free(code_locale);
5318 free(message_locale);
5320 err = IE_ISDS;
5321 goto leave;
5324 /* Otherwise new code sent successfully */
5325 /* Mimic otp->resolution according to the code, specification does
5326 * prescribe OTP header to be available. */
5327 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5328 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5330 leave:
5331 if (NULL != saved_url) {
5332 /* Revert URL to original one */
5333 zfree(context->url);
5334 context->url = saved_url;
5336 #if HAVE_CURL_REAUTHORIZATION_BUG
5337 if (NULL != saved_curl) {
5338 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5339 context->curl = saved_curl;
5341 #endif
5343 free(code);
5344 free(message);
5345 xmlFreeDoc(response);
5346 xmlFreeNode(request);
5348 if (!err)
5349 isds_log(ILF_ISDS, ILL_DEBUG,
5350 _("New OTP code has been sent successfully on SendSMSCode "
5351 "request.\n"));
5352 return err;
5356 /* Convert response status code to isds_error code and set long message
5357 * @context is context to save long message to
5358 * @map is mapping from codes to errors and messages. Pass NULL for generic
5359 * handling.
5360 * @code is status code to translate
5361 * @message is non-localized status message to put into long message in case
5362 * of uknown error. It can be NULL if server did not provide any.
5363 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5364 * invalid invocation. */
5365 static isds_error statuscode2isds_error(struct isds_ctx *context,
5366 const struct code_map_isds_error *map,
5367 const xmlChar *code, const xmlChar *message) {
5368 if (NULL == code) {
5369 isds_log_message(context,
5370 _("NULL status code passed to statuscode2isds_error()"));
5371 return IE_INVAL;
5374 if (NULL != map) {
5375 /* Check for known error codes */
5376 for (int i=0; map->codes[i] != NULL; i++) {
5377 if (!xmlStrcmp(code, map->codes[i])) {
5378 isds_log_message(context, _(map->meanings[i]));
5379 return map->errors[i];
5384 /* Other error */
5385 if (xmlStrcmp(code, BAD_CAST "0000")) {
5386 char *message_locale = _isds_utf82locale((char*)message);
5387 if (NULL == message_locale)
5388 isds_log_message(context, _("ISDS server returned unknown error"));
5389 else
5390 isds_log_message(context, message_locale);
5391 free(message_locale);
5392 return IE_ISDS;
5395 return IE_SUCCESS;
5397 #endif
5400 /* Change user password in ISDS.
5401 * User must supply old password, new password will takes effect after some
5402 * time, current session can continue. Password must fulfill some constraints.
5403 * @context is session context
5404 * @old_password is current password.
5405 * @new_password is requested new password
5406 * @otp auxiliary data required if one-time password authentication is in use,
5407 * defines OTP code (if known) and returns fine grade resolution of OTP
5408 * procedure. Pass NULL, if one-time password authentication is not needed.
5409 * Please note the @otp argument must match OTP method used at log-in time. See
5410 * isds_login() function for more details.
5411 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5412 * NULL, if you don't care.
5413 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5414 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5415 * awaiting OTP code that has been delivered by side channel to the user. */
5416 isds_error isds_change_password(struct isds_ctx *context,
5417 const char *old_password, const char *new_password,
5418 struct isds_otp *otp, char **refnumber) {
5419 isds_error err = IE_SUCCESS;
5420 #if HAVE_LIBCURL
5421 char *saved_url = NULL; /* No copy */
5422 #if HAVE_CURL_REAUTHORIZATION_BUG
5423 CURL *saved_curl = NULL; /* No copy */
5424 #endif
5425 xmlNsPtr isds_ns = NULL;
5426 xmlNodePtr request = NULL, node;
5427 xmlDocPtr response = NULL;
5428 xmlChar *code = NULL, *message = NULL;
5429 const xmlChar *codes[] = {
5430 BAD_CAST "1066",
5431 BAD_CAST "1067",
5432 BAD_CAST "1079",
5433 BAD_CAST "1080",
5434 BAD_CAST "1081",
5435 BAD_CAST "1082",
5436 BAD_CAST "1083",
5437 BAD_CAST "1090",
5438 BAD_CAST "1091",
5439 BAD_CAST "2300",
5440 BAD_CAST "9204"
5442 const char *meanings[] = {
5443 N_("Password length must be between 8 and 32 characters"),
5444 N_("Password cannot be reused"), /* Server does not distinguish 1067
5445 and 1091 on ChangePasswordOTP */
5446 N_("Password contains forbidden character"),
5447 N_("Password must contain at least one upper-case letter, "
5448 "one lower-case, and one digit"),
5449 N_("Password cannot contain sequence of three identical characters"),
5450 N_("Password cannot contain user identifier"),
5451 N_("Password is too simmple"),
5452 N_("Old password is not valid"),
5453 N_("Password cannot be reused"),
5454 N_("Unexpected error"),
5455 N_("LDAP update error")
5457 #endif
5459 if (!context) return IE_INVALID_CONTEXT;
5460 zfree(context->long_message);
5461 if (NULL != refnumber)
5462 zfree(*refnumber);
5463 if (NULL == old_password) {
5464 isds_log_message(context,
5465 _("Second argument (old password) of isds_change_password() "
5466 "is NULL"));
5467 return IE_INVAL;
5469 if (NULL == otp && NULL == new_password) {
5470 isds_log_message(context,
5471 _("Third argument (new password) of isds_change_password() "
5472 "is NULL"));
5473 return IE_INVAL;
5476 #if HAVE_LIBCURL
5477 /* Check if connection is established
5478 * TODO: This check should be done downstairs. */
5479 if (!context->curl) return IE_CONNECTION_CLOSED;
5481 if (context->otp && NULL == otp) {
5482 isds_log_message(context, _("If one-time password authentication "
5483 "method is in use, changing password requires one-time "
5484 "credentials either"));
5485 return IE_INVAL;
5488 /* Build ChangeISDSPassword request */
5489 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5490 BAD_CAST "ChangePasswordOTP");
5491 if (!request) {
5492 isds_log_message(context, (NULL == otp) ?
5493 _("Could not build ChangeISDSPassword request") :
5494 _("Could not build ChangePasswordOTP request"));
5495 return IE_ERROR;
5497 isds_ns = xmlNewNs(request,
5498 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5499 NULL);
5500 if(!isds_ns) {
5501 isds_log_message(context, _("Could not create ISDS name space"));
5502 xmlFreeNode(request);
5503 return IE_ERROR;
5505 xmlSetNs(request, isds_ns);
5507 INSERT_STRING(request, "dbOldPassword", old_password);
5508 INSERT_STRING(request, "dbNewPassword", new_password);
5510 if (NULL != otp) {
5511 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5512 switch (otp->method) {
5513 case OTP_HMAC:
5514 isds_log(ILF_SEC, ILL_INFO,
5515 _("Selected authentication method: "
5516 "HMAC-based one-time password\n"));
5517 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5518 break;
5519 case OTP_TIME:
5520 isds_log(ILF_SEC, ILL_INFO,
5521 _("Selected authentication method: "
5522 "Time-based one-time password\n"));
5523 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5524 if (otp->otp_code == NULL) {
5525 isds_log(ILF_SEC, ILL_INFO,
5526 _("OTP code has not been provided by "
5527 "application, requesting server for "
5528 "new one.\n"));
5529 err = _isds_request_totp_code(context, old_password, otp,
5530 refnumber);
5531 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5532 goto leave;
5534 } else {
5535 isds_log(ILF_SEC, ILL_INFO,
5536 _("OTP code has been provided by "
5537 "application, not requesting server "
5538 "for new one.\n"));
5540 break;
5541 default:
5542 isds_log_message(context,
5543 _("Unknown one-time password authentication "
5544 "method requested by application"));
5545 err = IE_ENUM;
5546 goto leave;
5549 /* Change URL temporarily for sending this request only */
5551 char *new_url = NULL;
5552 if ((err = _isds_build_url_from_context(context,
5553 "%1$.*2$sasws/changePassword", &new_url))) {
5554 goto leave;
5556 saved_url = context->url;
5557 context->url = new_url;
5560 /* Store credentials for sending this request only */
5561 context->otp_credentials = otp;
5562 _isds_discard_credentials(context, 0);
5563 if ((err = _isds_store_credentials(context, context->saved_username,
5564 old_password, NULL))) {
5565 _isds_discard_credentials(context, 0);
5566 goto leave;
5568 #if HAVE_CURL_REAUTHORIZATION_BUG
5569 saved_curl = context->curl;
5570 context->curl = curl_easy_init();
5571 if (NULL == context->curl) {
5572 err = IE_ERROR;
5573 goto leave;
5575 if (context->timeout) {
5576 err = isds_set_timeout(context, context->timeout);
5577 if (err) goto leave;
5579 #endif
5582 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5583 _("Sending ChangeISDSPassword request to ISDS\n") :
5584 _("Sending ChangePasswordOTP request to ISDS\n"));
5586 /* Sent request */
5587 err = isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5588 request, &response, NULL, NULL);
5590 if (otp) {
5591 /* Remove temporal credentials */
5592 _isds_discard_credentials(context, 0);
5593 /* Detach pointer to OTP credentials from context */
5594 context->otp_credentials = NULL;
5595 /* Keep context->otp true to keep signaling this is OTP session */
5598 /* Destroy request */
5599 xmlFreeNode(request); request = NULL;
5601 if (err) {
5602 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5603 _("Processing ISDS response on ChangeISDSPassword "
5604 "request failed\n") :
5605 _("Processing ISDS response on ChangePasswordOTP "
5606 "request failed\n"));
5607 goto leave;
5610 /* Check for response status */
5611 err = isds_response_status(context,
5612 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5613 &code, &message, (xmlChar **)refnumber);
5614 if (err) {
5615 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5616 _("ISDS response on ChangeISDSPassword request is missing "
5617 "status\n") :
5618 _("ISDS response on ChangePasswordOTP request is missing "
5619 "status\n"));
5620 goto leave;
5623 /* Check for known error codes */
5624 for (int i=0; i < sizeof(codes)/sizeof(*codes); i++) {
5625 if (!xmlStrcmp(code, codes[i])) {
5626 char *code_locale = _isds_utf82locale((char*)code);
5627 char *message_locale = _isds_utf82locale((char*)message);
5628 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5629 _("Server refused to change password on ChangeISDSPassword "
5630 "request (code=%s, message=%s)\n") :
5631 _("Server refused to change password on ChangePasswordOTP "
5632 "request (code=%s, message=%s)\n"),
5633 code_locale, message_locale);
5634 free(code_locale);
5635 free(message_locale);
5636 isds_log_message(context, _(meanings[i]));
5637 err = IE_INVAL;
5638 goto leave;
5642 /* Other error */
5643 if (xmlStrcmp(code, BAD_CAST "0000")) {
5644 char *code_locale = _isds_utf82locale((char*)code);
5645 char *message_locale = _isds_utf82locale((char*)message);
5646 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5647 _("Server refused to change password on ChangeISDSPassword "
5648 "request (code=%s, message=%s)\n") :
5649 _("Server refused to change password on ChangePasswordOTP "
5650 "request (code=%s, message=%s)\n"),
5651 code_locale, message_locale);
5652 isds_log_message(context, message_locale);
5653 free(code_locale);
5654 free(message_locale);
5655 err = IE_ISDS;
5656 goto leave;
5659 /* Otherwise password changed successfully */
5661 leave:
5662 if (NULL != saved_url) {
5663 /* Revert URL to original one */
5664 zfree(context->url);
5665 context->url = saved_url;
5667 #if HAVE_CURL_REAUTHORIZATION_BUG
5668 if (NULL != saved_curl) {
5669 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5670 context->curl = saved_curl;
5672 #endif
5674 free(code);
5675 free(message);
5676 xmlFreeDoc(response);
5677 xmlFreeNode(request);
5679 if (!err)
5680 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5681 _("Password changed successfully on ChangeISDSPassword "
5682 "request.\n") :
5683 _("Password changed successfully on ChangePasswordOTP "
5684 "request.\n"));
5685 #else /* not HAVE_LIBCURL */
5686 err = IE_NOTSUP;
5687 #endif
5689 return err;
5693 #if HAVE_LIBCURL
5694 /* Generic middle part with request sending and response check.
5695 * It sends prepared request and checks for error code.
5696 * @context is ISDS session context.
5697 * @service is ISDS service handler
5698 * @service_name is name in scope of given @service
5699 * @request is XML tree with request. Will be freed to save memory.
5700 * @response is XML document outputting ISDS response.
5701 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5702 * @map is mapping from status code to library error. Pass NULL if no special
5703 * handling is requested.
5704 * NULL, if you don't care. */
5705 static isds_error send_destroy_request_check_response(
5706 struct isds_ctx *context,
5707 const isds_service service, const xmlChar *service_name,
5708 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5709 const struct code_map_isds_error *map) {
5710 isds_error err = IE_SUCCESS;
5711 char *service_name_locale = NULL;
5712 xmlChar *code = NULL, *message = NULL;
5715 if (!context) return IE_INVALID_CONTEXT;
5716 if (!service_name || *service_name == '\0' || !request || !*request ||
5717 !response)
5718 return IE_INVAL;
5720 /* Check if connection is established
5721 * TODO: This check should be done downstairs. */
5722 if (!context->curl) return IE_CONNECTION_CLOSED;
5724 service_name_locale = _isds_utf82locale((char*) service_name);
5725 if (!service_name_locale) {
5726 err = IE_NOMEM;
5727 goto leave;
5730 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5731 service_name_locale);
5733 /* Send request */
5734 err = isds(context, service, *request, response, NULL, NULL);
5735 xmlFreeNode(*request); *request = NULL;
5737 if (err) {
5738 isds_log(ILF_ISDS, ILL_DEBUG,
5739 _("Processing ISDS response on %s request failed\n"),
5740 service_name_locale);
5741 goto leave;
5744 /* Check for response status */
5745 err = isds_response_status(context, service, *response,
5746 &code, &message, refnumber);
5747 if (err) {
5748 isds_log(ILF_ISDS, ILL_DEBUG,
5749 _("ISDS response on %s request is missing status\n"),
5750 service_name_locale);
5751 goto leave;
5754 err = statuscode2isds_error(context, map, code, message);
5756 /* Request processed, but server failed */
5757 if (xmlStrcmp(code, BAD_CAST "0000")) {
5758 char *code_locale = _isds_utf82locale((char*) code);
5759 char *message_locale = _isds_utf82locale((char*) message);
5760 isds_log(ILF_ISDS, ILL_DEBUG,
5761 _("Server refused %s request (code=%s, message=%s)\n"),
5762 service_name_locale, code_locale, message_locale);
5763 free(code_locale);
5764 free(message_locale);
5765 goto leave;
5769 leave:
5770 free(code);
5771 free(message);
5772 if (err && *response) {
5773 xmlFreeDoc(*response);
5774 *response = NULL;
5776 if (*request) {
5777 xmlFreeNode(*request);
5778 *request = NULL;
5780 free(service_name_locale);
5782 return err;
5786 /* Generic bottom half with request sending.
5787 * It sends prepared request, checks for error code, destroys response and
5788 * request and log success or failure.
5789 * @context is ISDS session context.
5790 * @service is ISDS service handler
5791 * @service_name is name in scope of given @service
5792 * @request is XML tree with request. Will be freed to save memory.
5793 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5794 * NULL, if you don't care. */
5795 static isds_error send_request_check_drop_response(
5796 struct isds_ctx *context,
5797 const isds_service service, const xmlChar *service_name,
5798 xmlNodePtr *request, xmlChar **refnumber) {
5799 isds_error err = IE_SUCCESS;
5800 xmlDocPtr response = NULL;
5803 if (!context) return IE_INVALID_CONTEXT;
5804 if (!service_name || *service_name == '\0' || !request || !*request)
5805 return IE_INVAL;
5807 /* Send request and check response*/
5808 err = send_destroy_request_check_response(context,
5809 service, service_name, request, &response, refnumber, NULL);
5811 xmlFreeDoc(response);
5813 if (*request) {
5814 xmlFreeNode(*request);
5815 *request = NULL;
5818 if (!err) {
5819 char *service_name_locale = _isds_utf82locale((char *) service_name);
5820 isds_log(ILF_ISDS, ILL_DEBUG,
5821 _("%s request processed by server successfully.\n"),
5822 service_name_locale);
5823 free(service_name_locale);
5826 return err;
5830 /* Insert isds_credentials_delivery structure into XML request if not NULL
5831 * @context is session context
5832 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5833 * credentials delivery. The email field is passed.
5834 * @parent is XML element where to insert */
5835 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5836 const struct isds_credentials_delivery *credentials_delivery,
5837 xmlNodePtr parent) {
5838 isds_error err = IE_SUCCESS;
5839 xmlNodePtr node;
5841 if (!context) return IE_INVALID_CONTEXT;
5842 if (!parent) return IE_INVAL;
5844 if (credentials_delivery) {
5845 /* Following elements are valid only for services:
5846 * NewAccessData, AddDataBoxUser, CreateDataBox */
5847 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5848 INSERT_STRING(parent, "email", credentials_delivery->email);
5851 leave:
5852 return err;
5856 /* Extract credentials delivery from ISDS response.
5857 * @context is session context
5858 * @credentials_delivery is pointer to valid structure to fill in returned
5859 * user's password (and new log-in name). If NULL, do not extract the data.
5860 * @response is pointer to XML document with ISDS response
5861 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5862 * @return IE_SUCCESS even if new user name has not been found because it's not
5863 * clear whether it's returned always. */
5864 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5865 struct isds_credentials_delivery *credentials_delivery,
5866 xmlDocPtr response, const char *request_name) {
5867 isds_error err = IE_SUCCESS;
5868 xmlXPathContextPtr xpath_ctx = NULL;
5869 xmlXPathObjectPtr result = NULL;
5870 char *xpath_query = NULL;
5872 if (!context) return IE_INVALID_CONTEXT;
5873 if (credentials_delivery) {
5874 zfree(credentials_delivery->token);
5875 zfree(credentials_delivery->new_user_name);
5877 if (!response || !request_name || !*request_name) return IE_INVAL;
5880 /* Extract optional token */
5881 if (credentials_delivery) {
5882 xpath_ctx = xmlXPathNewContext(response);
5883 if (!xpath_ctx) {
5884 err = IE_ERROR;
5885 goto leave;
5887 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5888 err = IE_ERROR;
5889 goto leave;
5892 /* Verify root element */
5893 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
5894 request_name)) {
5895 err = IE_NOMEM;
5896 goto leave;
5898 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
5899 if (!result) {
5900 err = IE_ERROR;
5901 goto leave;
5903 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5904 char *request_name_locale = _isds_utf82locale(request_name);
5905 isds_log(ILF_ISDS, ILL_WARNING,
5906 _("Wrong element in ISDS response for %s request "
5907 "while extracting credentials delivery details\n"),
5908 request_name_locale);
5909 free(request_name_locale);
5910 err = IE_ERROR;
5911 goto leave;
5913 xpath_ctx->node = result->nodesetval->nodeTab[0];
5916 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5917 * optional. */
5918 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
5920 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
5921 if (!credentials_delivery->token) {
5922 char *request_name_locale = _isds_utf82locale(request_name);
5923 isds_log(ILF_ISDS, ILL_ERR,
5924 _("ISDS did not return token on %s request "
5925 "even if requested\n"), request_name_locale);
5926 free(request_name_locale);
5927 err = IE_ERROR;
5931 leave:
5932 free(xpath_query);
5933 xmlXPathFreeObject(result);
5934 xmlXPathFreeContext(xpath_ctx);
5936 return err;
5940 /* Build XSD:tCreateDBInput request type for box creating.
5941 * @context is session context
5942 * @request outputs built XML tree
5943 * @service_name is request name of SERVICE_DB_MANIPULATION service
5944 * @box is box description to create including single primary user (in case of
5945 * FO box type)
5946 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5947 * box, or contact address of PFO box owner)
5948 * @former_names is optional former name of box owner. Pass NULL if otherwise.
5949 * @upper_box_id is optional ID of supper box if currently created box is
5950 * subordinated.
5951 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
5952 * don't care.
5953 * @credentials_delivery is valid pointer if ISDS should return token that box
5954 * owner can use to obtain his new credentials in on-line way. Then valid email
5955 * member value should be supplied.
5956 * @approval is optional external approval of box manipulation */
5957 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
5958 xmlNodePtr *request, const xmlChar *service_name,
5959 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
5960 const xmlChar *former_names, const xmlChar *upper_box_id,
5961 const xmlChar *ceo_label,
5962 const struct isds_credentials_delivery *credentials_delivery,
5963 const struct isds_approval *approval) {
5964 isds_error err = IE_SUCCESS;
5965 xmlNsPtr isds_ns = NULL;
5966 xmlNodePtr node, dbPrimaryUsers;
5967 xmlChar *string = NULL;
5968 const struct isds_list *item;
5971 if (!context) return IE_INVALID_CONTEXT;
5972 if (!request || !service_name || service_name[0] == '\0' || !box)
5973 return IE_INVAL;
5976 /* Build CreateDataBox-similar request */
5977 *request = xmlNewNode(NULL, service_name);
5978 if (!*request) {
5979 char *service_name_locale = _isds_utf82locale((char*) service_name);
5980 isds_printf_message(context, _("Could build %s request"),
5981 service_name_locale);
5982 free(service_name_locale);
5983 return IE_ERROR;
5985 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
5986 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
5987 if (!isds_ns) {
5988 isds_log_message(context, _("Could not create ISDS1 name space"));
5989 xmlFreeNode(*request);
5990 return IE_ERROR;
5992 } else {
5993 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
5994 if (!isds_ns) {
5995 isds_log_message(context, _("Could not create ISDS name space"));
5996 xmlFreeNode(*request);
5997 return IE_ERROR;
6000 xmlSetNs(*request, isds_ns);
6002 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6003 err = insert_DbOwnerInfo(context, box, node);
6004 if (err) goto leave;
6006 /* Insert users */
6007 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6008 * verbose documentation allows none dbUserInfo */
6009 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6010 for (item = users; item; item = item->next) {
6011 if (item->data) {
6012 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6013 err = insert_DbUserInfo(context,
6014 (struct isds_DbUserInfo *) item->data, node);
6015 if (err) goto leave;
6019 INSERT_STRING(*request, "dbFormerNames", former_names);
6020 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6021 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6023 err = insert_credentials_delivery(context, credentials_delivery, *request);
6024 if (err) goto leave;
6026 err = insert_GExtApproval(context, approval, *request);
6027 if (err) goto leave;
6029 leave:
6030 if (err) {
6031 xmlFreeNode(*request);
6032 *request = NULL;
6034 free(string);
6035 return err;
6037 #endif /* HAVE_LIBCURL */
6040 /* Create new box.
6041 * @context is session context
6042 * @box is box description to create including single primary user (in case of
6043 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6044 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6045 * box, or contact address of PFO box owner)
6046 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6047 * @upper_box_id is optional ID of supper box if currently created box is
6048 * subordinated.
6049 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6050 * @credentials_delivery is NULL if new password should be delivered off-line
6051 * to box owner. It is valid pointer if owner should obtain new password on-line
6052 * on dedicated web server. Then input @credentials_delivery.email value is
6053 * his e-mail address he must provide to dedicated web server together
6054 * with output reallocated @credentials_delivery.token member. Output
6055 * member @credentials_delivery.new_user_name is unused up on this call.
6056 * @approval is optional external approval of box manipulation
6057 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6058 * NULL, if you don't care.*/
6059 isds_error isds_add_box(struct isds_ctx *context,
6060 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6061 const char *former_names, const char *upper_box_id,
6062 const char *ceo_label,
6063 struct isds_credentials_delivery *credentials_delivery,
6064 const struct isds_approval *approval, char **refnumber) {
6065 isds_error err = IE_SUCCESS;
6066 #if HAVE_LIBCURL
6067 xmlNodePtr request = NULL;
6068 xmlDocPtr response = NULL;
6069 xmlXPathContextPtr xpath_ctx = NULL;
6070 xmlXPathObjectPtr result = NULL;
6071 #endif
6074 if (!context) return IE_INVALID_CONTEXT;
6075 zfree(context->long_message);
6076 if (credentials_delivery) {
6077 zfree(credentials_delivery->token);
6078 zfree(credentials_delivery->new_user_name);
6080 if (!box) return IE_INVAL;
6082 #if HAVE_LIBCURL
6083 /* Scratch box ID */
6084 zfree(box->dbID);
6086 /* Build CreateDataBox request */
6087 err = build_CreateDBInput_request(context,
6088 &request, BAD_CAST "CreateDataBox",
6089 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6090 (xmlChar *) ceo_label, credentials_delivery, approval);
6091 if (err) goto leave;
6093 /* Send it to server and process response */
6094 err = send_destroy_request_check_response(context,
6095 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6096 &response, (xmlChar **) refnumber, NULL);
6098 /* Extract box ID */
6099 xpath_ctx = xmlXPathNewContext(response);
6100 if (!xpath_ctx) {
6101 err = IE_ERROR;
6102 goto leave;
6104 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6105 err = IE_ERROR;
6106 goto leave;
6108 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6110 /* Extract optional token */
6111 err = extract_credentials_delivery(context, credentials_delivery, response,
6112 "CreateDataBox");
6114 leave:
6115 xmlXPathFreeObject(result);
6116 xmlXPathFreeContext(xpath_ctx);
6117 xmlFreeDoc(response);
6118 xmlFreeNode(request);
6120 if (!err) {
6121 isds_log(ILF_ISDS, ILL_DEBUG,
6122 _("CreateDataBox request processed by server successfully.\n"));
6124 #else /* not HAVE_LIBCURL */
6125 err = IE_NOTSUP;
6126 #endif
6128 return err;
6132 /* Notify ISDS about new PFO entity.
6133 * This function has no real effect.
6134 * @context is session context
6135 * @box is PFO description including single primary user.
6136 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6137 * @former_names is optional undocumented string. Pass NULL if you don't care.
6138 * @upper_box_id is optional ID of supper box if currently created box is
6139 * subordinated.
6140 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6141 * @approval is optional external approval of box manipulation
6142 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6143 * NULL, if you don't care.*/
6144 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6145 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6146 const char *former_names, const char *upper_box_id,
6147 const char *ceo_label, const struct isds_approval *approval,
6148 char **refnumber) {
6149 isds_error err = IE_SUCCESS;
6150 #if HAVE_LIBCURL
6151 xmlNodePtr request = NULL;
6152 #endif
6154 if (!context) return IE_INVALID_CONTEXT;
6155 zfree(context->long_message);
6156 if (!box) return IE_INVAL;
6158 #if HAVE_LIBCURL
6159 /* Build CreateDataBoxPFOInfo request */
6160 err = build_CreateDBInput_request(context,
6161 &request, BAD_CAST "CreateDataBoxPFOInfo",
6162 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6163 (xmlChar *) ceo_label, NULL, approval);
6164 if (err) goto leave;
6166 /* Send it to server and process response */
6167 err = send_request_check_drop_response(context,
6168 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6169 (xmlChar **) refnumber);
6170 /* XXX: XML Schema names output dbID element but textual documentation
6171 * states no box identifier is returned. */
6172 leave:
6173 xmlFreeNode(request);
6174 #else /* not HAVE_LIBCURL */
6175 err = IE_NOTSUP;
6176 #endif
6177 return err;
6181 /* Common implementation for removing given box.
6182 * @context is session context
6183 * @service_name is UTF-8 encoded name fo ISDS service
6184 * @box is box description to delete
6185 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6186 * carry sane value. If NULL, do not inject this information into request.
6187 * @approval is optional external approval of box manipulation
6188 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6189 * NULL, if you don't care.*/
6190 isds_error _isds_delete_box_common(struct isds_ctx *context,
6191 const xmlChar *service_name,
6192 const struct isds_DbOwnerInfo *box, const struct tm *since,
6193 const struct isds_approval *approval, char **refnumber) {
6194 isds_error err = IE_SUCCESS;
6195 #if HAVE_LIBCURL
6196 xmlNsPtr isds_ns = NULL;
6197 xmlNodePtr request = NULL;
6198 xmlNodePtr node;
6199 xmlChar *string = NULL;
6200 #endif
6203 if (!context) return IE_INVALID_CONTEXT;
6204 zfree(context->long_message);
6205 if (!service_name || !*service_name || !box) return IE_INVAL;
6208 #if HAVE_LIBCURL
6209 /* Build DeleteDataBox(Promptly) request */
6210 request = xmlNewNode(NULL, service_name);
6211 if (!request) {
6212 char *service_name_locale = _isds_utf82locale((char*)service_name);
6213 isds_printf_message(context,
6214 _("Could build %s request"), service_name_locale);
6215 free(service_name_locale);
6216 return IE_ERROR;
6218 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6219 if(!isds_ns) {
6220 isds_log_message(context, _("Could not create ISDS name space"));
6221 xmlFreeNode(request);
6222 return IE_ERROR;
6224 xmlSetNs(request, isds_ns);
6226 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6227 err = insert_DbOwnerInfo(context, box, node);
6228 if (err) goto leave;
6230 if (since) {
6231 err = tm2datestring(since, &string);
6232 if (err) {
6233 isds_log_message(context,
6234 _("Could not convert `since' argument to ISO date string"));
6235 goto leave;
6237 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6238 zfree(string);
6241 err = insert_GExtApproval(context, approval, request);
6242 if (err) goto leave;
6245 /* Send it to server and process response */
6246 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6247 service_name, &request, (xmlChar **) refnumber);
6249 leave:
6250 xmlFreeNode(request);
6251 free(string);
6252 #else /* not HAVE_LIBCURL */
6253 err = IE_NOTSUP;
6254 #endif
6255 return err;
6259 /* Remove given box permanently.
6260 * @context is session context
6261 * @box is box description to delete
6262 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6263 * carry sane value.
6264 * @approval is optional external approval of box manipulation
6265 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6266 * NULL, if you don't care.*/
6267 isds_error isds_delete_box(struct isds_ctx *context,
6268 const struct isds_DbOwnerInfo *box, const struct tm *since,
6269 const struct isds_approval *approval, char **refnumber) {
6270 if (!context) return IE_INVALID_CONTEXT;
6271 zfree(context->long_message);
6272 if (!box || !since) return IE_INVAL;
6274 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6275 box, since, approval, refnumber);
6279 /* Undocumented function.
6280 * @context is session context
6281 * @box is box description to delete
6282 * @approval is optional external approval of box manipulation
6283 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6284 * NULL, if you don't care.*/
6285 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6286 const struct isds_DbOwnerInfo *box,
6287 const struct isds_approval *approval, char **refnumber) {
6288 if (!context) return IE_INVALID_CONTEXT;
6289 zfree(context->long_message);
6290 if (!box) return IE_INVAL;
6292 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6293 box, NULL, approval, refnumber);
6297 /* Update data about given box.
6298 * @context is session context
6299 * @old_box current box description
6300 * @new_box are updated data about @old_box
6301 * @approval is optional external approval of box manipulation
6302 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6303 * NULL, if you don't care.*/
6304 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6305 const struct isds_DbOwnerInfo *old_box,
6306 const struct isds_DbOwnerInfo *new_box,
6307 const struct isds_approval *approval, char **refnumber) {
6308 isds_error err = IE_SUCCESS;
6309 #if HAVE_LIBCURL
6310 xmlNsPtr isds_ns = NULL;
6311 xmlNodePtr request = NULL;
6312 xmlNodePtr node;
6313 #endif
6316 if (!context) return IE_INVALID_CONTEXT;
6317 zfree(context->long_message);
6318 if (!old_box || !new_box) return IE_INVAL;
6321 #if HAVE_LIBCURL
6322 /* Build UpdateDataBoxDescr request */
6323 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6324 if (!request) {
6325 isds_log_message(context,
6326 _("Could build UpdateDataBoxDescr request"));
6327 return IE_ERROR;
6329 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6330 if(!isds_ns) {
6331 isds_log_message(context, _("Could not create ISDS name space"));
6332 xmlFreeNode(request);
6333 return IE_ERROR;
6335 xmlSetNs(request, isds_ns);
6337 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6338 err = insert_DbOwnerInfo(context, old_box, node);
6339 if (err) goto leave;
6341 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6342 err = insert_DbOwnerInfo(context, new_box, node);
6343 if (err) goto leave;
6345 err = insert_GExtApproval(context, approval, request);
6346 if (err) goto leave;
6349 /* Send it to server and process response */
6350 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6351 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6353 leave:
6354 xmlFreeNode(request);
6355 #else /* not HAVE_LIBCURL */
6356 err = IE_NOTSUP;
6357 #endif
6359 return err;
6363 #if HAVE_LIBCURL
6364 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6365 * code
6366 * @context is session context
6367 * @service is SOAP service
6368 * @service_name is name of request in @service
6369 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6370 * @box_id is box ID of interest
6371 * @approval is optional external approval of box manipulation
6372 * @response is server SOAP body response as XML document
6373 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6374 * NULL, if you don't care.
6375 * @return error coded from lower layer, context message will be set up
6376 * appropriately. */
6377 static isds_error build_send_dbid_request_check_response(
6378 struct isds_ctx *context, const isds_service service,
6379 const xmlChar *service_name, const xmlChar *box_id_element,
6380 const xmlChar *box_id, const struct isds_approval *approval,
6381 xmlDocPtr *response, xmlChar **refnumber) {
6383 isds_error err = IE_SUCCESS;
6384 char *service_name_locale = NULL, *box_id_locale = NULL;
6385 xmlNodePtr request = NULL, node;
6386 xmlNsPtr isds_ns = NULL;
6388 if (!context) return IE_INVALID_CONTEXT;
6389 if (!service_name || !box_id) return IE_INVAL;
6390 if (!response) return IE_INVAL;
6392 /* Free output argument */
6393 xmlFreeDoc(*response); *response = NULL;
6395 /* Prepare strings */
6396 service_name_locale = _isds_utf82locale((char*)service_name);
6397 if (!service_name_locale) {
6398 err = IE_NOMEM;
6399 goto leave;
6401 box_id_locale = _isds_utf82locale((char*)box_id);
6402 if (!box_id_locale) {
6403 err = IE_NOMEM;
6404 goto leave;
6407 /* Build request */
6408 request = xmlNewNode(NULL, service_name);
6409 if (!request) {
6410 isds_printf_message(context,
6411 _("Could not build %s request for %s box"), service_name_locale,
6412 box_id_locale);
6413 err = IE_ERROR;
6414 goto leave;
6416 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6417 if(!isds_ns) {
6418 isds_log_message(context, _("Could not create ISDS name space"));
6419 err = IE_ERROR;
6420 goto leave;
6422 xmlSetNs(request, isds_ns);
6424 /* Add XSD:tIdDbInput children */
6425 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6426 INSERT_STRING(request, box_id_element, box_id);
6427 err = insert_GExtApproval(context, approval, request);
6428 if (err) goto leave;
6430 /* Send request and check response*/
6431 err = send_destroy_request_check_response(context,
6432 service, service_name, &request, response, refnumber, NULL);
6434 leave:
6435 free(service_name_locale);
6436 free(box_id_locale);
6437 xmlFreeNode(request);
6438 return err;
6440 #endif /* HAVE_LIBCURL */
6443 /* Get data about all users assigned to given box.
6444 * @context is session context
6445 * @box_id is box ID
6446 * @users is automatically reallocated list of struct isds_DbUserInfo */
6447 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6448 struct isds_list **users) {
6449 isds_error err = IE_SUCCESS;
6450 #if HAVE_LIBCURL
6451 xmlDocPtr response = NULL;
6452 xmlXPathContextPtr xpath_ctx = NULL;
6453 xmlXPathObjectPtr result = NULL;
6454 int i;
6455 struct isds_list *item, *prev_item = NULL;
6456 #endif
6458 if (!context) return IE_INVALID_CONTEXT;
6459 zfree(context->long_message);
6460 if (!users || !box_id) return IE_INVAL;
6461 isds_list_free(users);
6464 #if HAVE_LIBCURL
6465 /* Do request and check for success */
6466 err = build_send_dbid_request_check_response(context,
6467 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6468 BAD_CAST box_id, NULL, &response, NULL);
6469 if (err) goto leave;
6472 /* Extract data */
6473 /* Prepare structure */
6474 xpath_ctx = xmlXPathNewContext(response);
6475 if (!xpath_ctx) {
6476 err = IE_ERROR;
6477 goto leave;
6479 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6480 err = IE_ERROR;
6481 goto leave;
6484 /* Set context node */
6485 result = xmlXPathEvalExpression(BAD_CAST
6486 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6487 xpath_ctx);
6488 if (!result) {
6489 err = IE_ERROR;
6490 goto leave;
6492 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6493 /* Iterate over all users */
6494 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6496 /* Prepare structure */
6497 item = calloc(1, sizeof(*item));
6498 if (!item) {
6499 err = IE_NOMEM;
6500 goto leave;
6502 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6503 if (i == 0) *users = item;
6504 else prev_item->next = item;
6505 prev_item = item;
6507 /* Extract it */
6508 xpath_ctx->node = result->nodesetval->nodeTab[i];
6509 err = extract_DbUserInfo(context,
6510 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6511 if (err) goto leave;
6515 leave:
6516 if (err) {
6517 isds_list_free(users);
6520 xmlXPathFreeObject(result);
6521 xmlXPathFreeContext(xpath_ctx);
6522 xmlFreeDoc(response);
6524 if (!err)
6525 isds_log(ILF_ISDS, ILL_DEBUG,
6526 _("GetDataBoxUsers request processed by server "
6527 "successfully.\n"));
6528 #else /* not HAVE_LIBCURL */
6529 err = IE_NOTSUP;
6530 #endif
6532 return err;
6536 /* Update data about user assigned to given box.
6537 * @context is session context
6538 * @box is box identification
6539 * @old_user identifies user to update
6540 * @new_user are updated data about @old_user
6541 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6542 * NULL, if you don't care.*/
6543 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6544 const struct isds_DbOwnerInfo *box,
6545 const struct isds_DbUserInfo *old_user,
6546 const struct isds_DbUserInfo *new_user,
6547 char **refnumber) {
6548 isds_error err = IE_SUCCESS;
6549 #if HAVE_LIBCURL
6550 xmlNsPtr isds_ns = NULL;
6551 xmlNodePtr request = NULL;
6552 xmlNodePtr node;
6553 #endif
6556 if (!context) return IE_INVALID_CONTEXT;
6557 zfree(context->long_message);
6558 if (!box || !old_user || !new_user) return IE_INVAL;
6561 #if HAVE_LIBCURL
6562 /* Build UpdateDataBoxUser request */
6563 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6564 if (!request) {
6565 isds_log_message(context,
6566 _("Could build UpdateDataBoxUser request"));
6567 return IE_ERROR;
6569 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6570 if(!isds_ns) {
6571 isds_log_message(context, _("Could not create ISDS name space"));
6572 xmlFreeNode(request);
6573 return IE_ERROR;
6575 xmlSetNs(request, isds_ns);
6577 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6578 err = insert_DbOwnerInfo(context, box, node);
6579 if (err) goto leave;
6581 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6582 err = insert_DbUserInfo(context, old_user, node);
6583 if (err) goto leave;
6585 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6586 err = insert_DbUserInfo(context, new_user, node);
6587 if (err) goto leave;
6589 /* Send it to server and process response */
6590 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6591 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6593 leave:
6594 xmlFreeNode(request);
6595 #else /* not HAVE_LIBCURL */
6596 err = IE_NOTSUP;
6597 #endif
6599 return err;
6603 /* Undocumented function.
6604 * @context is session context
6605 * @box_id is UTF-8 encoded box identifier
6606 * @token is UTF-8 encoded temporary password
6607 * @user_id outputs UTF-8 encoded reallocated user identifier
6608 * @password outpus UTF-8 encoded reallocated user password
6609 * Output arguments will be nulled in case of error */
6610 isds_error isds_activate(struct isds_ctx *context,
6611 const char *box_id, const char *token,
6612 char **user_id, char **password) {
6613 isds_error err = IE_SUCCESS;
6614 #if HAVE_LIBCURL
6615 xmlNsPtr isds_ns = NULL;
6616 xmlNodePtr request = NULL, node;
6617 xmlDocPtr response = NULL;
6618 xmlXPathContextPtr xpath_ctx = NULL;
6619 xmlXPathObjectPtr result = NULL;
6620 #endif
6623 if (!context) return IE_INVALID_CONTEXT;
6624 zfree(context->long_message);
6626 if (user_id) zfree(*user_id);
6627 if (password) zfree(*password);
6629 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6632 #if HAVE_LIBCURL
6633 /* Build Activate request */
6634 request = xmlNewNode(NULL, BAD_CAST "Activate");
6635 if (!request) {
6636 isds_log_message(context, _("Could build Activate request"));
6637 return IE_ERROR;
6639 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6640 if(!isds_ns) {
6641 isds_log_message(context, _("Could not create ISDS name space"));
6642 xmlFreeNode(request);
6643 return IE_ERROR;
6645 xmlSetNs(request, isds_ns);
6647 INSERT_STRING(request, "dbAccessDataId", token);
6648 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6649 INSERT_STRING(request, "dbID", box_id);
6652 /* Send request and check response*/
6653 err = send_destroy_request_check_response(context,
6654 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6655 &response, NULL, NULL);
6656 if (err) goto leave;
6659 /* Extract data */
6660 xpath_ctx = xmlXPathNewContext(response);
6661 if (!xpath_ctx) {
6662 err = IE_ERROR;
6663 goto leave;
6665 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6666 err = IE_ERROR;
6667 goto leave;
6669 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6670 xpath_ctx);
6671 if (!result) {
6672 err = IE_ERROR;
6673 goto leave;
6675 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6676 isds_log_message(context, _("Missing ActivateResponse element"));
6677 err = IE_ISDS;
6678 goto leave;
6680 if (result->nodesetval->nodeNr > 1) {
6681 isds_log_message(context, _("Multiple ActivateResponse element"));
6682 err = IE_ISDS;
6683 goto leave;
6685 xpath_ctx->node = result->nodesetval->nodeTab[0];
6686 xmlXPathFreeObject(result); result = NULL;
6688 EXTRACT_STRING("isds:userId", *user_id);
6689 if (!*user_id)
6690 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6691 "but did not return `userId' element.\n"));
6693 EXTRACT_STRING("isds:password", *password);
6694 if (!*password)
6695 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6696 "but did not return `password' element.\n"));
6698 leave:
6699 xmlXPathFreeObject(result);
6700 xmlXPathFreeContext(xpath_ctx);
6701 xmlFreeDoc(response);
6702 xmlFreeNode(request);
6704 if (!err)
6705 isds_log(ILF_ISDS, ILL_DEBUG,
6706 _("Activate request processed by server successfully.\n"));
6707 #else /* not HAVE_LIBCURL */
6708 err = IE_NOTSUP;
6709 #endif
6711 return err;
6715 /* Reset credentials of user assigned to given box.
6716 * @context is session context
6717 * @box is box identification
6718 * @user identifies user to reset password
6719 * @fee_paid is true if fee has been paid, false otherwise
6720 * @approval is optional external approval of box manipulation
6721 * @credentials_delivery is NULL if new password should be delivered off-line
6722 * to the user. It is valid pointer if user should obtain new password on-line
6723 * on dedicated web server. Then input @credentials_delivery.email value is
6724 * user's e-mail address user must provide to dedicated web server together
6725 * with @credentials_delivery.token. The output reallocated token user needs
6726 * to use to authorize on the web server to view his new password. Output
6727 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6728 * ISDS changed up on this call. (No reason why server could change the name
6729 * is known now.)
6730 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6731 * NULL, if you don't care.*/
6732 isds_error isds_reset_password(struct isds_ctx *context,
6733 const struct isds_DbOwnerInfo *box,
6734 const struct isds_DbUserInfo *user,
6735 const _Bool fee_paid, const struct isds_approval *approval,
6736 struct isds_credentials_delivery *credentials_delivery,
6737 char **refnumber) {
6738 isds_error err = IE_SUCCESS;
6739 #if HAVE_LIBCURL
6740 xmlNsPtr isds_ns = NULL;
6741 xmlNodePtr request = NULL, node;
6742 xmlDocPtr response = NULL;
6743 #endif
6746 if (!context) return IE_INVALID_CONTEXT;
6747 zfree(context->long_message);
6749 if (credentials_delivery) {
6750 zfree(credentials_delivery->token);
6751 zfree(credentials_delivery->new_user_name);
6753 if (!box || !user) return IE_INVAL;
6756 #if HAVE_LIBCURL
6757 /* Build NewAccessData request */
6758 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6759 if (!request) {
6760 isds_log_message(context,
6761 _("Could build NewAccessData request"));
6762 return IE_ERROR;
6764 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6765 if(!isds_ns) {
6766 isds_log_message(context, _("Could not create ISDS name space"));
6767 xmlFreeNode(request);
6768 return IE_ERROR;
6770 xmlSetNs(request, isds_ns);
6772 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6773 err = insert_DbOwnerInfo(context, box, node);
6774 if (err) goto leave;
6776 INSERT_ELEMENT(node, request, "dbUserInfo");
6777 err = insert_DbUserInfo(context, user, node);
6778 if (err) goto leave;
6780 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6782 err = insert_credentials_delivery(context, credentials_delivery, request);
6783 if (err) goto leave;
6785 err = insert_GExtApproval(context, approval, request);
6786 if (err) goto leave;
6788 /* Send request and check response*/
6789 err = send_destroy_request_check_response(context,
6790 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6791 &response, (xmlChar **) refnumber, NULL);
6792 if (err) goto leave;
6795 /* Extract optional token */
6796 err = extract_credentials_delivery(context, credentials_delivery,
6797 response, "NewAccessData");
6799 leave:
6800 xmlFreeDoc(response);
6801 xmlFreeNode(request);
6803 if (!err)
6804 isds_log(ILF_ISDS, ILL_DEBUG,
6805 _("NewAccessData request processed by server "
6806 "successfully.\n"));
6807 #else /* not HAVE_LIBCURL */
6808 err = IE_NOTSUP;
6809 #endif
6811 return err;
6815 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6816 * code, destroy response and log success.
6817 * @context is ISDS session context.
6818 * @service_name is name of SERVICE_DB_MANIPULATION service
6819 * @box is box identification
6820 * @user identifies user to remove
6821 * @credentials_delivery is NULL if new user's password should be delivered
6822 * off-line to the user. It is valid pointer if user should obtain new
6823 * password on-line on dedicated web server. Then input
6824 * @credentials_delivery.email value is user's e-mail address user must
6825 * provide to dedicated web server together with @credentials_delivery.token.
6826 * The output reallocated token user needs to use to authorize on the web
6827 * server to view his new password. Output reallocated
6828 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6829 * assingned or changed up on this call.
6830 * @approval is optional external approval of box manipulation
6831 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6832 * NULL, if you don't care. */
6833 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6834 struct isds_ctx *context, const xmlChar *service_name,
6835 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6836 struct isds_credentials_delivery *credentials_delivery,
6837 const struct isds_approval *approval, xmlChar **refnumber) {
6838 isds_error err = IE_SUCCESS;
6839 #if HAVE_LIBCURL
6840 xmlNsPtr isds_ns = NULL;
6841 xmlNodePtr request = NULL, node;
6842 xmlDocPtr response = NULL;
6843 #endif
6846 if (!context) return IE_INVALID_CONTEXT;
6847 zfree(context->long_message);
6848 if (credentials_delivery) {
6849 zfree(credentials_delivery->token);
6850 zfree(credentials_delivery->new_user_name);
6852 if (!service_name || service_name[0] == '\0' || !box || !user)
6853 return IE_INVAL;
6856 #if HAVE_LIBCURL
6857 /* Build NewAccessData or similar request */
6858 request = xmlNewNode(NULL, service_name);
6859 if (!request) {
6860 char *service_name_locale = _isds_utf82locale((char *) service_name);
6861 isds_printf_message(context, _("Could not build %s request"),
6862 service_name_locale);
6863 free(service_name_locale);
6864 return IE_ERROR;
6866 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6867 if(!isds_ns) {
6868 isds_log_message(context, _("Could not create ISDS name space"));
6869 xmlFreeNode(request);
6870 return IE_ERROR;
6872 xmlSetNs(request, isds_ns);
6874 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6875 err = insert_DbOwnerInfo(context, box, node);
6876 if (err) goto leave;
6878 INSERT_ELEMENT(node, request, "dbUserInfo");
6879 err = insert_DbUserInfo(context, user, node);
6880 if (err) goto leave;
6882 err = insert_credentials_delivery(context, credentials_delivery, request);
6883 if (err) goto leave;
6885 err = insert_GExtApproval(context, approval, request);
6886 if (err) goto leave;
6889 /* Send request and check response*/
6890 err = send_destroy_request_check_response(context,
6891 SERVICE_DB_MANIPULATION, service_name, &request, &response,
6892 refnumber, NULL);
6894 xmlFreeNode(request);
6895 request = NULL;
6897 /* Pick up credentials_delivery if requested */
6898 err = extract_credentials_delivery(context, credentials_delivery, response,
6899 (char *)service_name);
6901 leave:
6902 xmlFreeDoc(response);
6903 if (request) xmlFreeNode(request);
6905 if (!err) {
6906 char *service_name_locale = _isds_utf82locale((char *) service_name);
6907 isds_log(ILF_ISDS, ILL_DEBUG,
6908 _("%s request processed by server successfully.\n"),
6909 service_name_locale);
6910 free(service_name_locale);
6912 #else /* not HAVE_LIBCURL */
6913 err = IE_NOTSUP;
6914 #endif
6916 return err;
6920 /* Assign new user to given box.
6921 * @context is session context
6922 * @box is box identification
6923 * @user defines new user to add
6924 * @credentials_delivery is NULL if new user's password should be delivered
6925 * off-line to the user. It is valid pointer if user should obtain new
6926 * password on-line on dedicated web server. Then input
6927 * @credentials_delivery.email value is user's e-mail address user must
6928 * provide to dedicated web server together with @credentials_delivery.token.
6929 * The output reallocated token user needs to use to authorize on the web
6930 * server to view his new password. Output reallocated
6931 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6932 * assingned up on this call.
6933 * @approval is optional external approval of box manipulation
6934 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6935 * NULL, if you don't care.*/
6936 isds_error isds_add_user(struct isds_ctx *context,
6937 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6938 struct isds_credentials_delivery *credentials_delivery,
6939 const struct isds_approval *approval, char **refnumber) {
6940 return build_send_manipulationboxuser_request_check_drop_response(context,
6941 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
6942 approval, (xmlChar **) refnumber);
6946 /* Remove user assigned to given box.
6947 * @context is session context
6948 * @box is box identification
6949 * @user identifies user to remove
6950 * @approval is optional external approval of box manipulation
6951 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6952 * NULL, if you don't care.*/
6953 isds_error isds_delete_user(struct isds_ctx *context,
6954 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6955 const struct isds_approval *approval, char **refnumber) {
6956 return build_send_manipulationboxuser_request_check_drop_response(context,
6957 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
6958 (xmlChar **) refnumber);
6962 /* Get list of boxes in ZIP archive.
6963 * @context is session context
6964 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
6965 * System recognizes following values currently: ALL (all boxes), UPG
6966 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
6967 * receiving commercial messages). This argument is a string because
6968 * specification states new values can appear in the future. Not all list
6969 * types are available to all users.
6970 * @buffer is automatically reallocated memory to store the list of boxes. The
6971 * list is zipped CSV file.
6972 * @buffer_length is size of @buffer data in bytes.
6973 * In case of error @buffer will be freed and @buffer_length will be
6974 * undefined.*/
6975 isds_error isds_get_box_list_archive(struct isds_ctx *context,
6976 const char *list_identifier, void **buffer, size_t *buffer_length) {
6977 isds_error err = IE_SUCCESS;
6978 #if HAVE_LIBCURL
6979 xmlNsPtr isds_ns = NULL;
6980 xmlNodePtr request = NULL, node;
6981 xmlDocPtr response = NULL;
6982 xmlXPathContextPtr xpath_ctx = NULL;
6983 xmlXPathObjectPtr result = NULL;
6984 char *string = NULL;
6985 #endif
6988 if (!context) return IE_INVALID_CONTEXT;
6989 zfree(context->long_message);
6990 if (buffer) zfree(*buffer);
6991 if (!buffer || !buffer_length) return IE_INVAL;
6994 #if HAVE_LIBCURL
6995 /* Check if connection is established
6996 * TODO: This check should be done downstairs. */
6997 if (!context->curl) return IE_CONNECTION_CLOSED;
7000 /* Build AuthenticateMessage request */
7001 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7002 if (!request) {
7003 isds_log_message(context,
7004 _("Could not build GetDataBoxList request"));
7005 return IE_ERROR;
7007 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7008 if(!isds_ns) {
7009 isds_log_message(context, _("Could not create ISDS name space"));
7010 xmlFreeNode(request);
7011 return IE_ERROR;
7013 xmlSetNs(request, isds_ns);
7014 INSERT_STRING(request, "dblType", list_identifier);
7016 /* Send request to server and process response */
7017 err = send_destroy_request_check_response(context,
7018 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7019 &response, NULL, NULL);
7020 if (err) goto leave;
7023 /* Extract Base-64 encoded ZIP file */
7024 xpath_ctx = xmlXPathNewContext(response);
7025 if (!xpath_ctx) {
7026 err = IE_ERROR;
7027 goto leave;
7029 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7030 err = IE_ERROR;
7031 goto leave;
7033 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7035 /* Decode non-empty archive */
7036 if (string && string[0] != '\0') {
7037 *buffer_length = _isds_b64decode(string, buffer);
7038 if (*buffer_length == (size_t) -1) {
7039 isds_printf_message(context,
7040 _("Error while Base64-decoding box list archive"));
7041 err = IE_ERROR;
7042 goto leave;
7047 leave:
7048 free(string);
7049 xmlXPathFreeObject(result);
7050 xmlXPathFreeContext(xpath_ctx);
7051 xmlFreeDoc(response);
7052 xmlFreeNode(request);
7054 if (!err) {
7055 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7056 "processed by server successfully.\n"));
7058 #else /* not HAVE_LIBCURL */
7059 err = IE_NOTSUP;
7060 #endif
7062 return err;
7066 /* Find boxes suiting given criteria.
7067 * @criteria is filter. You should fill in at least some members.
7068 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7069 * possibly empty. Input NULL or valid old structure.
7070 * @return:
7071 * IE_SUCCESS if search succeeded, @boxes contains useful data
7072 * IE_NOEXIST if no such box exists, @boxes will be NULL
7073 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7074 * contains still valid data
7075 * other code if something bad happens. @boxes will be NULL. */
7076 isds_error isds_FindDataBox(struct isds_ctx *context,
7077 const struct isds_DbOwnerInfo *criteria,
7078 struct isds_list **boxes) {
7079 isds_error err = IE_SUCCESS;
7080 #if HAVE_LIBCURL
7081 _Bool truncated = 0;
7082 xmlNsPtr isds_ns = NULL;
7083 xmlNodePtr request = NULL;
7084 xmlDocPtr response = NULL;
7085 xmlChar *code = NULL, *message = NULL;
7086 xmlNodePtr db_owner_info;
7087 xmlXPathContextPtr xpath_ctx = NULL;
7088 xmlXPathObjectPtr result = NULL;
7089 xmlChar *string = NULL;
7090 #endif
7093 if (!context) return IE_INVALID_CONTEXT;
7094 zfree(context->long_message);
7095 if (!boxes) return IE_INVAL;
7096 isds_list_free(boxes);
7098 if (!criteria) {
7099 return IE_INVAL;
7102 #if HAVE_LIBCURL
7103 /* Check if connection is established
7104 * TODO: This check should be done downstairs. */
7105 if (!context->curl) return IE_CONNECTION_CLOSED;
7108 /* Build FindDataBox request */
7109 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
7110 if (!request) {
7111 isds_log_message(context,
7112 _("Could build FindDataBox request"));
7113 return IE_ERROR;
7115 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7116 if(!isds_ns) {
7117 isds_log_message(context, _("Could not create ISDS name space"));
7118 xmlFreeNode(request);
7119 return IE_ERROR;
7121 xmlSetNs(request, isds_ns);
7122 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7123 if (!db_owner_info) {
7124 isds_log_message(context, _("Could not add dbOwnerInfo child to "
7125 "FindDataBox element"));
7126 xmlFreeNode(request);
7127 return IE_ERROR;
7130 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
7131 if (err) goto leave;
7134 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
7136 /* Sent request */
7137 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7139 /* Destroy request */
7140 xmlFreeNode(request); request = NULL;
7142 if (err) {
7143 isds_log(ILF_ISDS, ILL_DEBUG,
7144 _("Processing ISDS response on FindDataBox "
7145 "request failed\n"));
7146 goto leave;
7149 /* Check for response status */
7150 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7151 &code, &message, NULL);
7152 if (err) {
7153 isds_log(ILF_ISDS, ILL_DEBUG,
7154 _("ISDS response on FindDataBox request is missing status\n"));
7155 goto leave;
7158 /* Request processed, but nothing found */
7159 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7160 !xmlStrcmp(code, BAD_CAST "5001")) {
7161 char *code_locale = _isds_utf82locale((char*)code);
7162 char *message_locale = _isds_utf82locale((char*)message);
7163 isds_log(ILF_ISDS, ILL_DEBUG,
7164 _("Server did not found any box on FindDataBox request "
7165 "(code=%s, message=%s)\n"), code_locale, message_locale);
7166 isds_log_message(context, message_locale);
7167 free(code_locale);
7168 free(message_locale);
7169 err = IE_NOEXIST;
7170 goto leave;
7173 /* Warning, not a error */
7174 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7175 char *code_locale = _isds_utf82locale((char*)code);
7176 char *message_locale = _isds_utf82locale((char*)message);
7177 isds_log(ILF_ISDS, ILL_DEBUG,
7178 _("Server truncated response on FindDataBox request "
7179 "(code=%s, message=%s)\n"), code_locale, message_locale);
7180 isds_log_message(context, message_locale);
7181 free(code_locale);
7182 free(message_locale);
7183 truncated = 1;
7186 /* Other error */
7187 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7188 char *code_locale = _isds_utf82locale((char*)code);
7189 char *message_locale = _isds_utf82locale((char*)message);
7190 isds_log(ILF_ISDS, ILL_DEBUG,
7191 _("Server refused FindDataBox request "
7192 "(code=%s, message=%s)\n"), code_locale, message_locale);
7193 isds_log_message(context, message_locale);
7194 free(code_locale);
7195 free(message_locale);
7196 err = IE_ISDS;
7197 goto leave;
7200 xpath_ctx = xmlXPathNewContext(response);
7201 if (!xpath_ctx) {
7202 err = IE_ERROR;
7203 goto leave;
7205 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7206 err = IE_ERROR;
7207 goto leave;
7210 /* Extract boxes if they present */
7211 result = xmlXPathEvalExpression(BAD_CAST
7212 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7213 xpath_ctx);
7214 if (!result) {
7215 err = IE_ERROR;
7216 goto leave;
7218 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7219 struct isds_list *item, *prev_item = NULL;
7220 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7221 item = calloc(1, sizeof(*item));
7222 if (!item) {
7223 err = IE_NOMEM;
7224 goto leave;
7227 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7228 if (i == 0) *boxes = item;
7229 else prev_item->next = item;
7230 prev_item = item;
7232 xpath_ctx->node = result->nodesetval->nodeTab[i];
7233 err = extract_DbOwnerInfo(context,
7234 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7235 if (err) goto leave;
7239 leave:
7240 if (err) {
7241 isds_list_free(boxes);
7242 } else {
7243 if (truncated) err = IE_2BIG;
7246 free(string);
7247 xmlFreeNode(request);
7248 xmlXPathFreeObject(result);
7249 xmlXPathFreeContext(xpath_ctx);
7251 free(code);
7252 free(message);
7253 xmlFreeDoc(response);
7255 if (!err)
7256 isds_log(ILF_ISDS, ILL_DEBUG,
7257 _("FindDataBox request processed by server successfully.\n"));
7258 #else /* not HAVE_LIBCURL */
7259 err = IE_NOTSUP;
7260 #endif
7262 return err;
7266 /* Get status of a box.
7267 * @context is ISDS session context.
7268 * @box_id is UTF-8 encoded box identifier as zero terminated string
7269 * @box_status is return value of box status.
7270 * @return:
7271 * IE_SUCCESS if box has been found and its status retrieved
7272 * IE_NOEXIST if box is not known to ISDS server
7273 * or other appropriate error.
7274 * You can use isds_DbState to enumerate box status. However out of enum
7275 * range value can be returned too. This is feature because ISDS
7276 * specification leaves the set of values open.
7277 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7278 * the box has been deleted, but ISDS still lists its former existence. */
7279 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7280 long int *box_status) {
7281 isds_error err = IE_SUCCESS;
7282 #if HAVE_LIBCURL
7283 xmlNsPtr isds_ns = NULL;
7284 xmlNodePtr request = NULL, db_id;
7285 xmlDocPtr response = NULL;
7286 xmlXPathContextPtr xpath_ctx = NULL;
7287 xmlXPathObjectPtr result = NULL;
7288 xmlChar *string = NULL;
7290 const xmlChar *codes[] = {
7291 BAD_CAST "5001",
7292 BAD_CAST "1007",
7293 BAD_CAST "2011",
7294 NULL
7296 const char *meanings[] = {
7297 "The box does not exist",
7298 "Box ID is malformed",
7299 "Box ID malformed",
7301 const isds_error errors[] = {
7302 IE_NOEXIST,
7303 IE_INVAL,
7304 IE_INVAL,
7306 struct code_map_isds_error map = {
7307 .codes = codes,
7308 .meanings = meanings,
7309 .errors = errors
7311 #endif
7313 if (!context) return IE_INVALID_CONTEXT;
7314 zfree(context->long_message);
7315 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7317 #if HAVE_LIBCURL
7318 /* Check if connection is established
7319 * TODO: This check should be done downstairs. */
7320 if (!context->curl) return IE_CONNECTION_CLOSED;
7323 /* Build CheckDataBox request */
7324 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7325 if (!request) {
7326 isds_log_message(context,
7327 _("Could build CheckDataBox request"));
7328 return IE_ERROR;
7330 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7331 if(!isds_ns) {
7332 isds_log_message(context, _("Could not create ISDS name space"));
7333 xmlFreeNode(request);
7334 return IE_ERROR;
7336 xmlSetNs(request, isds_ns);
7337 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7338 if (!db_id) {
7339 isds_log_message(context, _("Could not add dbID child to "
7340 "CheckDataBox element"));
7341 xmlFreeNode(request);
7342 return IE_ERROR;
7346 /* Send request and check response*/
7347 err = send_destroy_request_check_response(context,
7348 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7349 &request, &response, NULL, &map);
7350 if (err) goto leave;
7353 /* Extract data */
7354 xpath_ctx = xmlXPathNewContext(response);
7355 if (!xpath_ctx) {
7356 err = IE_ERROR;
7357 goto leave;
7359 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7360 err = IE_ERROR;
7361 goto leave;
7363 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7364 xpath_ctx);
7365 if (!result) {
7366 err = IE_ERROR;
7367 goto leave;
7369 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7370 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7371 err = IE_ISDS;
7372 goto leave;
7374 if (result->nodesetval->nodeNr > 1) {
7375 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7376 err = IE_ISDS;
7377 goto leave;
7379 xpath_ctx->node = result->nodesetval->nodeTab[0];
7380 xmlXPathFreeObject(result); result = NULL;
7382 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7385 leave:
7386 free(string);
7387 xmlXPathFreeObject(result);
7388 xmlXPathFreeContext(xpath_ctx);
7390 xmlFreeDoc(response);
7392 if (!err)
7393 isds_log(ILF_ISDS, ILL_DEBUG,
7394 _("CheckDataBox request processed by server successfully.\n"));
7395 #else /* not HAVE_LIBCURL */
7396 err = IE_NOTSUP;
7397 #endif
7399 return err;
7403 /* Get list of permissions to send commercial messages.
7404 * @context is ISDS session context.
7405 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7406 * @permissions is a reallocated list of permissions (struct
7407 * isds_commercial_permission*) to send commercial messages from @box_id. The
7408 * order of permissions is significant as the server applies the permissions
7409 * and associated pre-paid credits in the order. Empty list means no
7410 * permission.
7411 * @return:
7412 * IE_SUCCESS if the list has been obtained correctly,
7413 * or other appropriate error. */
7414 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7415 const char *box_id, struct isds_list **permissions) {
7416 isds_error err = IE_SUCCESS;
7417 #if HAVE_LIBCURL
7418 xmlDocPtr response = NULL;
7419 xmlXPathContextPtr xpath_ctx = NULL;
7420 xmlXPathObjectPtr result = NULL;
7421 #endif
7423 if (!context) return IE_INVALID_CONTEXT;
7424 zfree(context->long_message);
7425 if (NULL == permissions) return IE_INVAL;
7426 isds_list_free(permissions);
7427 if (NULL == box_id) return IE_INVAL;
7429 #if HAVE_LIBCURL
7430 /* Check if connection is established */
7431 if (!context->curl) return IE_CONNECTION_CLOSED;
7433 /* Do request and check for success */
7434 err = build_send_dbid_request_check_response(context,
7435 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7436 BAD_CAST box_id, NULL, &response, NULL);
7437 if (!err) {
7438 isds_log(ILF_ISDS, ILL_DEBUG,
7439 _("PDZInfo request processed by server successfully.\n"));
7442 /* Extract data */
7443 /* Prepare structure */
7444 xpath_ctx = xmlXPathNewContext(response);
7445 if (!xpath_ctx) {
7446 err = IE_ERROR;
7447 goto leave;
7449 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7450 err = IE_ERROR;
7451 goto leave;
7454 /* Set context node */
7455 result = xmlXPathEvalExpression(BAD_CAST
7456 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7457 xpath_ctx);
7458 if (!result) {
7459 err = IE_ERROR;
7460 goto leave;
7462 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7463 struct isds_list *prev_item = NULL;
7465 /* Iterate over all permission records */
7466 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7467 struct isds_list *item;
7469 /* Prepare structure */
7470 item = calloc(1, sizeof(*item));
7471 if (!item) {
7472 err = IE_NOMEM;
7473 goto leave;
7475 item->destructor = (void(*)(void**))isds_commercial_permission_free;
7476 if (i == 0) *permissions = item;
7477 else prev_item->next = item;
7478 prev_item = item;
7480 /* Extract it */
7481 xpath_ctx->node = result->nodesetval->nodeTab[i];
7482 err = extract_DbPDZRecord(context,
7483 (struct isds_commercial_permission **) (&item->data),
7484 xpath_ctx);
7485 if (err) goto leave;
7489 leave:
7490 if (err) {
7491 isds_list_free(permissions);
7494 xmlXPathFreeObject(result);
7495 xmlXPathFreeContext(xpath_ctx);
7496 xmlFreeDoc(response);
7498 #else /* not HAVE_LIBCURL */
7499 err = IE_NOTSUP;
7500 #endif
7502 return err;
7506 /* Get details about credit for sending pre-paid commercial messages.
7507 * @context is ISDS session context.
7508 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
7509 * @from_date is first day of credit history to return in @history. Only
7510 * tm_year, tm_mon and tm_mday carry sane value.
7511 * @to_date is last day of credit history to return in @history. Only
7512 * tm_year, tm_mon and tm_mday carry sane value.
7513 * @credit outputs current credit value into pre-allocated memory. Pass NULL
7514 * if you don't care. This and all other credit values are integers in
7515 * hundredths of Czech Crowns.
7516 * @email outputs notification e-mail address where notifications about credit
7517 * are sent. This is automatically reallocated string. Pass NULL if you don't
7518 * care. It can return NULL if no address is defined.
7519 * @history outputs auto-reallocated list of pointers to struct
7520 * isds_credit_event. Events in closed interval @from_time to @to_time are
7521 * returned. Pass NULL @to_time and @from_time if you don't care. The events
7522 * are sorted by time.
7523 * @return:
7524 * IE_SUCCESS if the credit details have been obtained correctly,
7525 * or other appropriate error. Please note that server allows to retrieve
7526 * only limited history of events. */
7527 isds_error isds_get_commercial_credit(struct isds_ctx *context,
7528 const char *box_id,
7529 const struct tm *from_date, const struct tm *to_date,
7530 long int *credit, char **email, struct isds_list **history) {
7531 isds_error err = IE_SUCCESS;
7532 #if HAVE_LIBCURL
7533 char *box_id_locale = NULL;
7534 xmlNodePtr request = NULL, node;
7535 xmlNsPtr isds_ns = NULL;
7536 xmlChar *string = NULL;
7538 xmlDocPtr response = NULL;
7539 xmlXPathContextPtr xpath_ctx = NULL;
7540 xmlXPathObjectPtr result = NULL;
7542 const xmlChar *codes[] = {
7543 BAD_CAST "1004",
7544 BAD_CAST "2011",
7545 BAD_CAST "1093",
7546 BAD_CAST "1137",
7547 BAD_CAST "1058",
7548 NULL
7550 const char *meanings[] = {
7551 "Insufficient priviledges for the box",
7552 "The box does not exist",
7553 "Date is too long (history is not available after 15 months)",
7554 "Interval is too long (limit is 3 months)",
7555 "Invalid date"
7557 const isds_error errors[] = {
7558 IE_ISDS,
7559 IE_NOEXIST,
7560 IE_DATE,
7561 IE_DATE,
7562 IE_DATE,
7564 struct code_map_isds_error map = {
7565 .codes = codes,
7566 .meanings = meanings,
7567 .errors = errors
7569 #endif
7571 if (!context) return IE_INVALID_CONTEXT;
7572 zfree(context->long_message);
7574 /* Free output argument */
7575 if (NULL != credit) *credit = 0;
7576 if (NULL != email) zfree(*email);
7577 isds_list_free(history);
7579 if (NULL == box_id) return IE_INVAL;
7581 #if HAVE_LIBCURL
7582 /* Check if connection is established */
7583 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7585 box_id_locale = _isds_utf82locale((char*)box_id);
7586 if (NULL == box_id_locale) {
7587 err = IE_NOMEM;
7588 goto leave;
7591 /* Build request */
7592 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
7593 if (NULL == request) {
7594 isds_printf_message(context,
7595 _("Could not build DataBoxCreditInfo request for %s box"),
7596 box_id_locale);
7597 err = IE_ERROR;
7598 goto leave;
7600 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7601 if(!isds_ns) {
7602 isds_log_message(context, _("Could not create ISDS name space"));
7603 err = IE_ERROR;
7604 goto leave;
7606 xmlSetNs(request, isds_ns);
7608 /* Add mandatory XSD:tIdDbInput child */
7609 INSERT_STRING(request, BAD_CAST "dbID", box_id);
7610 /* Add mandatory dates elements with optional values */
7611 if (from_date) {
7612 err = tm2datestring(from_date, &string);
7613 if (err) {
7614 isds_log_message(context,
7615 _("Could not convert `from_date' argument to ISO date "
7616 "string"));
7617 goto leave;
7619 INSERT_STRING(request, "ciFromDate", string);
7620 zfree(string);
7621 } else {
7622 INSERT_STRING(request, "ciFromDate", NULL);
7624 if (to_date) {
7625 err = tm2datestring(to_date, &string);
7626 if (err) {
7627 isds_log_message(context,
7628 _("Could not convert `to_date' argument to ISO date "
7629 "string"));
7630 goto leave;
7632 INSERT_STRING(request, "ciTodate", string);
7633 zfree(string);
7634 } else {
7635 INSERT_STRING(request, "ciTodate", NULL);
7638 /* Send request and check response*/
7639 err = send_destroy_request_check_response(context,
7640 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
7641 &request, &response, NULL, &map);
7642 if (err) goto leave;
7645 /* Extract data */
7646 /* Set context to the root */
7647 xpath_ctx = xmlXPathNewContext(response);
7648 if (!xpath_ctx) {
7649 err = IE_ERROR;
7650 goto leave;
7652 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7653 err = IE_ERROR;
7654 goto leave;
7656 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
7657 xpath_ctx);
7658 if (!result) {
7659 err = IE_ERROR;
7660 goto leave;
7662 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7663 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
7664 err = IE_ISDS;
7665 goto leave;
7667 if (result->nodesetval->nodeNr > 1) {
7668 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
7669 err = IE_ISDS;
7670 goto leave;
7672 xpath_ctx->node = result->nodesetval->nodeTab[0];
7673 xmlXPathFreeObject(result); result = NULL;
7675 /* Extract common data */
7676 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
7677 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
7679 /* Extract records */
7680 if (NULL == history) goto leave;
7681 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
7682 xpath_ctx);
7683 if (!result) {
7684 err = IE_ERROR;
7685 goto leave;
7687 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7688 struct isds_list *prev_item = NULL;
7690 /* Iterate over all records */
7691 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7692 struct isds_list *item;
7694 /* Prepare structure */
7695 item = calloc(1, sizeof(*item));
7696 if (!item) {
7697 err = IE_NOMEM;
7698 goto leave;
7700 item->destructor = (void(*)(void**))isds_credit_event_free;
7701 if (i == 0) *history = item;
7702 else prev_item->next = item;
7703 prev_item = item;
7705 /* Extract it */
7706 xpath_ctx->node = result->nodesetval->nodeTab[i];
7707 err = extract_CiRecord(context,
7708 (struct isds_credit_event **) (&item->data),
7709 xpath_ctx);
7710 if (err) goto leave;
7714 leave:
7715 if (!err) {
7716 isds_log(ILF_ISDS, ILL_DEBUG,
7717 _("DataBoxCreditInfo request processed by server successfully.\n"));
7719 if (err) {
7720 isds_list_free(history);
7721 if (NULL != email) zfree(*email)
7724 free(box_id_locale);
7725 xmlXPathFreeObject(result);
7726 xmlXPathFreeContext(xpath_ctx);
7727 xmlFreeDoc(response);
7729 #else /* not HAVE_LIBCURL */
7730 err = IE_NOTSUP;
7731 #endif
7733 return err;
7737 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
7738 * code, destroy response and log success.
7739 * @context is ISDS session context.
7740 * @service_name is name of SERVICE_DB_MANIPULATION service
7741 * @box_id is UTF-8 encoded box identifier as zero terminated string
7742 * @approval is optional external approval of box manipulation
7743 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7744 * NULL, if you don't care. */
7745 static isds_error build_send_manipulationdbid_request_check_drop_response(
7746 struct isds_ctx *context, const xmlChar *service_name,
7747 const xmlChar *box_id, const struct isds_approval *approval,
7748 xmlChar **refnumber) {
7749 isds_error err = IE_SUCCESS;
7750 #if HAVE_LIBCURL
7751 xmlDocPtr response = NULL;
7752 #endif
7754 if (!context) return IE_INVALID_CONTEXT;
7755 zfree(context->long_message);
7756 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
7758 #if HAVE_LIBCURL
7759 /* Check if connection is established */
7760 if (!context->curl) return IE_CONNECTION_CLOSED;
7762 /* Do request and check for success */
7763 err = build_send_dbid_request_check_response(context,
7764 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
7765 &response, refnumber);
7766 xmlFreeDoc(response);
7768 if (!err) {
7769 char *service_name_locale = _isds_utf82locale((char *) service_name);
7770 isds_log(ILF_ISDS, ILL_DEBUG,
7771 _("%s request processed by server successfully.\n"),
7772 service_name_locale);
7773 free(service_name_locale);
7775 #else /* not HAVE_LIBCURL */
7776 err = IE_NOTSUP;
7777 #endif
7779 return err;
7783 /* Switch box into state where box can receive commercial messages (off by
7784 * default)
7785 * @context is ISDS session context.
7786 * @box_id is UTF-8 encoded box identifier as zero terminated string
7787 * @allow is true for enable, false for disable commercial messages income
7788 * @approval is optional external approval of box manipulation
7789 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7790 * NULL, if you don't care. */
7791 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
7792 const char *box_id, const _Bool allow,
7793 const struct isds_approval *approval, char **refnumber) {
7794 return build_send_manipulationdbid_request_check_drop_response(context,
7795 (allow) ? BAD_CAST "SetOpenAddressing" :
7796 BAD_CAST "ClearOpenAddressing",
7797 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7801 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
7802 * message acceptance). This is just a box permission. Sender must apply
7803 * such role by sending each message.
7804 * @context is ISDS session context.
7805 * @box_id is UTF-8 encoded box identifier as zero terminated string
7806 * @allow is true for enable, false for disable OVM role permission
7807 * @approval is optional external approval of box manipulation
7808 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7809 * NULL, if you don't care. */
7810 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
7811 const char *box_id, const _Bool allow,
7812 const struct isds_approval *approval, char **refnumber) {
7813 return build_send_manipulationdbid_request_check_drop_response(context,
7814 (allow) ? BAD_CAST "SetEffectiveOVM" :
7815 BAD_CAST "ClearEffectiveOVM",
7816 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7820 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
7821 * code, destroy response and log success.
7822 * @context is ISDS session context.
7823 * @service_name is name of SERVICE_DB_MANIPULATION service
7824 * @owner is structure describing box
7825 * @approval is optional external approval of box manipulation
7826 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7827 * NULL, if you don't care. */
7828 static isds_error build_send_manipulationdbowner_request_check_drop_response(
7829 struct isds_ctx *context, const xmlChar *service_name,
7830 const struct isds_DbOwnerInfo *owner,
7831 const struct isds_approval *approval, xmlChar **refnumber) {
7832 isds_error err = IE_SUCCESS;
7833 #if HAVE_LIBCURL
7834 char *service_name_locale = NULL;
7835 xmlNodePtr request = NULL, db_owner_info;
7836 xmlNsPtr isds_ns = NULL;
7837 #endif
7840 if (!context) return IE_INVALID_CONTEXT;
7841 zfree(context->long_message);
7842 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
7844 #if HAVE_LIBCURL
7845 service_name_locale = _isds_utf82locale((char*)service_name);
7846 if (!service_name_locale) {
7847 err = IE_NOMEM;
7848 goto leave;
7851 /* Build request */
7852 request = xmlNewNode(NULL, service_name);
7853 if (!request) {
7854 isds_printf_message(context,
7855 _("Could not build %s request"), service_name_locale);
7856 err = IE_ERROR;
7857 goto leave;
7859 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7860 if(!isds_ns) {
7861 isds_log_message(context, _("Could not create ISDS name space"));
7862 err = IE_ERROR;
7863 goto leave;
7865 xmlSetNs(request, isds_ns);
7868 /* Add XSD:tOwnerInfoInput child*/
7869 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
7870 err = insert_DbOwnerInfo(context, owner, db_owner_info);
7871 if (err) goto leave;
7873 /* Add XSD:gExtApproval*/
7874 err = insert_GExtApproval(context, approval, request);
7875 if (err) goto leave;
7877 /* Send it to server and process response */
7878 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7879 service_name, &request, refnumber);
7881 leave:
7882 xmlFreeNode(request);
7883 free(service_name_locale);
7884 #else /* not HAVE_LIBCURL */
7885 err = IE_NOTSUP;
7886 #endif
7888 return err;
7892 /* Switch box accessibility state on request of box owner.
7893 * Despite the name, owner must do the request off-line. This function is
7894 * designed for such off-line meeting points (e.g. Czech POINT).
7895 * @context is ISDS session context.
7896 * @box identifies box to switch accessibility state.
7897 * @allow is true for making accessible, false to disallow access.
7898 * @approval is optional external approval of box manipulation
7899 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7900 * NULL, if you don't care. */
7901 isds_error isds_switch_box_accessibility_on_owner_request(
7902 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7903 const _Bool allow, const struct isds_approval *approval,
7904 char **refnumber) {
7905 return build_send_manipulationdbowner_request_check_drop_response(context,
7906 (allow) ? BAD_CAST "EnableOwnDataBox" :
7907 BAD_CAST "DisableOwnDataBox",
7908 box, approval, (xmlChar **) refnumber);
7912 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
7913 * date.
7914 * @context is ISDS session context.
7915 * @box identifies box to switch accessibility state.
7916 * @since is date since accessibility has been denied. This can be past too.
7917 * Only tm_year, tm_mon and tm_mday carry sane value.
7918 * @approval is optional external approval of box manipulation
7919 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7920 * NULL, if you don't care. */
7921 isds_error isds_disable_box_accessibility_externaly(
7922 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7923 const struct tm *since, const struct isds_approval *approval,
7924 char **refnumber) {
7925 isds_error err = IE_SUCCESS;
7926 #if HAVE_LIBCURL
7927 char *service_name_locale = NULL;
7928 xmlNodePtr request = NULL, node;
7929 xmlNsPtr isds_ns = NULL;
7930 xmlChar *string = NULL;
7931 #endif
7934 if (!context) return IE_INVALID_CONTEXT;
7935 zfree(context->long_message);
7936 if (!box || !since) return IE_INVAL;
7938 #if HAVE_LIBCURL
7939 /* Build request */
7940 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
7941 if (!request) {
7942 isds_printf_message(context,
7943 _("Could not build %s request"), "DisableDataBoxExternally");
7944 err = IE_ERROR;
7945 goto leave;
7947 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7948 if(!isds_ns) {
7949 isds_log_message(context, _("Could not create ISDS name space"));
7950 err = IE_ERROR;
7951 goto leave;
7953 xmlSetNs(request, isds_ns);
7956 /* Add @box identification */
7957 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7958 err = insert_DbOwnerInfo(context, box, node);
7959 if (err) goto leave;
7961 /* Add @since date */
7962 err = tm2datestring(since, &string);
7963 if(err) {
7964 isds_log_message(context,
7965 _("Could not convert `since' argument to ISO date string"));
7966 goto leave;
7968 INSERT_STRING(request, "dbOwnerDisableDate", string);
7969 zfree(string);
7971 /* Add @approval */
7972 err = insert_GExtApproval(context, approval, request);
7973 if (err) goto leave;
7975 /* Send it to server and process response */
7976 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7977 BAD_CAST "DisableDataBoxExternally", &request,
7978 (xmlChar **) refnumber);
7980 leave:
7981 free(string);
7982 xmlFreeNode(request);
7983 free(service_name_locale);
7984 #else /* not HAVE_LIBCURL */
7985 err = IE_NOTSUP;
7986 #endif
7988 return err;
7992 #if HAVE_LIBCURL
7993 /* Insert struct isds_message data (envelope (recipient data optional) and
7994 * documents into XML tree
7995 * @context is session context
7996 * @outgoing_message is libisds structure with message data
7997 * @create_message is XML CreateMessage or CreateMultipleMessage element
7998 * @process_recipient true for recipient data serialization, false for no
7999 * serialization */
8000 static isds_error insert_envelope_files(struct isds_ctx *context,
8001 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8002 const _Bool process_recipient) {
8004 isds_error err = IE_SUCCESS;
8005 xmlNodePtr envelope, dm_files, node;
8006 xmlChar *string = NULL;
8008 if (!context) return IE_INVALID_CONTEXT;
8009 if (!outgoing_message || !create_message) return IE_INVAL;
8012 /* Build envelope */
8013 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8014 if (!envelope) {
8015 isds_printf_message(context, _("Could not add dmEnvelope child to "
8016 "%s element"), create_message->name);
8017 return IE_ERROR;
8020 if (!outgoing_message->envelope) {
8021 isds_log_message(context, _("Outgoing message is missing envelope"));
8022 err = IE_INVAL;
8023 goto leave;
8026 /* Insert optional message type */
8027 err = insert_message_type(context, outgoing_message->envelope->dmType,
8028 envelope);
8029 if (err) goto leave;
8031 INSERT_STRING(envelope, "dmSenderOrgUnit",
8032 outgoing_message->envelope->dmSenderOrgUnit);
8033 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8034 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8036 if (process_recipient) {
8037 if (!outgoing_message->envelope->dbIDRecipient) {
8038 isds_log_message(context,
8039 _("Outgoing message is missing recipient box identifier"));
8040 err = IE_INVAL;
8041 goto leave;
8043 INSERT_STRING(envelope, "dbIDRecipient",
8044 outgoing_message->envelope->dbIDRecipient);
8046 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8047 outgoing_message->envelope->dmRecipientOrgUnit);
8048 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8049 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8050 INSERT_STRING(envelope, "dmToHands",
8051 outgoing_message->envelope->dmToHands);
8054 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8055 "dmAnnotation");
8056 INSERT_STRING(envelope, "dmAnnotation",
8057 outgoing_message->envelope->dmAnnotation);
8059 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8060 0, 50, "dmRecipientRefNumber");
8061 INSERT_STRING(envelope, "dmRecipientRefNumber",
8062 outgoing_message->envelope->dmRecipientRefNumber);
8064 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8065 0, 50, "dmSenderRefNumber");
8066 INSERT_STRING(envelope, "dmSenderRefNumber",
8067 outgoing_message->envelope->dmSenderRefNumber);
8069 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8070 0, 50, "dmRecipientIdent");
8071 INSERT_STRING(envelope, "dmRecipientIdent",
8072 outgoing_message->envelope->dmRecipientIdent);
8074 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8075 0, 50, "dmSenderIdent");
8076 INSERT_STRING(envelope, "dmSenderIdent",
8077 outgoing_message->envelope->dmSenderIdent);
8079 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8080 outgoing_message->envelope->dmLegalTitleLaw, string);
8081 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8082 outgoing_message->envelope->dmLegalTitleYear, string);
8083 INSERT_STRING(envelope, "dmLegalTitleSect",
8084 outgoing_message->envelope->dmLegalTitleSect);
8085 INSERT_STRING(envelope, "dmLegalTitlePar",
8086 outgoing_message->envelope->dmLegalTitlePar);
8087 INSERT_STRING(envelope, "dmLegalTitlePoint",
8088 outgoing_message->envelope->dmLegalTitlePoint);
8090 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8091 outgoing_message->envelope->dmPersonalDelivery);
8092 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8093 outgoing_message->envelope->dmAllowSubstDelivery);
8095 /* ???: Should we require value for dbEffectiveOVM sender?
8096 * ISDS has default as true */
8097 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8098 INSERT_BOOLEAN(envelope, "dmOVM",
8099 outgoing_message->envelope->dmPublishOwnID);
8102 /* Append dmFiles */
8103 if (!outgoing_message->documents) {
8104 isds_log_message(context,
8105 _("Outgoing message is missing list of documents"));
8106 err = IE_INVAL;
8107 goto leave;
8109 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8110 if (!dm_files) {
8111 isds_printf_message(context, _("Could not add dmFiles child to "
8112 "%s element"), create_message->name);
8113 err = IE_ERROR;
8114 goto leave;
8117 /* Check for document hierarchy */
8118 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8119 if (err) goto leave;
8121 /* Process each document */
8122 for (struct isds_list *item =
8123 (struct isds_list *) outgoing_message->documents;
8124 item; item = item->next) {
8125 if (!item->data) {
8126 isds_log_message(context,
8127 _("List of documents contains empty item"));
8128 err = IE_INVAL;
8129 goto leave;
8131 /* FIXME: Check for dmFileMetaType and for document references.
8132 * Only first document can be of MAIN type */
8133 err = insert_document(context, (struct isds_document*) item->data,
8134 dm_files);
8136 if (err) goto leave;
8139 leave:
8140 free(string);
8141 return err;
8143 #endif /* HAVE_LIBCURL */
8146 /* Send a message via ISDS to a recipient
8147 * @context is session context
8148 * @outgoing_message is message to send; Some members are mandatory (like
8149 * dbIDRecipient), some are optional and some are irrelevant (especially data
8150 * about sender). Included pointer to isds_list documents must contain at
8151 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8152 * members will be filled with valid data from ISDS. Exact list of write
8153 * members is subject to change. Currently dmID is changed.
8154 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8155 isds_error isds_send_message(struct isds_ctx *context,
8156 struct isds_message *outgoing_message) {
8158 isds_error err = IE_SUCCESS;
8159 #if HAVE_LIBCURL
8160 xmlNsPtr isds_ns = NULL;
8161 xmlNodePtr request = NULL;
8162 xmlDocPtr response = NULL;
8163 xmlChar *code = NULL, *message = NULL;
8164 xmlXPathContextPtr xpath_ctx = NULL;
8165 xmlXPathObjectPtr result = NULL;
8166 /*_Bool message_is_complete = 0;*/
8167 #endif
8169 if (!context) return IE_INVALID_CONTEXT;
8170 zfree(context->long_message);
8171 if (!outgoing_message) return IE_INVAL;
8173 #if HAVE_LIBCURL
8174 /* Check if connection is established
8175 * TODO: This check should be done downstairs. */
8176 if (!context->curl) return IE_CONNECTION_CLOSED;
8179 /* Build CreateMessage request */
8180 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8181 if (!request) {
8182 isds_log_message(context,
8183 _("Could not build CreateMessage request"));
8184 return IE_ERROR;
8186 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8187 if(!isds_ns) {
8188 isds_log_message(context, _("Could not create ISDS name space"));
8189 xmlFreeNode(request);
8190 return IE_ERROR;
8192 xmlSetNs(request, isds_ns);
8194 /* Append envelope and files */
8195 err = insert_envelope_files(context, outgoing_message, request, 1);
8196 if (err) goto leave;
8199 /* Signal we can serialize message since now */
8200 /*message_is_complete = 1;*/
8203 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8205 /* Sent request */
8206 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8208 /* Don't' destroy request, we want to provide it to application later */
8210 if (err) {
8211 isds_log(ILF_ISDS, ILL_DEBUG,
8212 _("Processing ISDS response on CreateMessage "
8213 "request failed\n"));
8214 goto leave;
8217 /* Check for response status */
8218 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8219 &code, &message, NULL);
8220 if (err) {
8221 isds_log(ILF_ISDS, ILL_DEBUG,
8222 _("ISDS response on CreateMessage request "
8223 "is missing status\n"));
8224 goto leave;
8227 /* Request processed, but refused by server or server failed */
8228 if (xmlStrcmp(code, BAD_CAST "0000")) {
8229 char *box_id_locale =
8230 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8231 char *code_locale = _isds_utf82locale((char*)code);
8232 char *message_locale = _isds_utf82locale((char*)message);
8233 isds_log(ILF_ISDS, ILL_DEBUG,
8234 _("Server did not accept message for %s on CreateMessage "
8235 "request (code=%s, message=%s)\n"),
8236 box_id_locale, code_locale, message_locale);
8237 isds_log_message(context, message_locale);
8238 free(box_id_locale);
8239 free(code_locale);
8240 free(message_locale);
8241 err = IE_ISDS;
8242 goto leave;
8246 /* Extract data */
8247 xpath_ctx = xmlXPathNewContext(response);
8248 if (!xpath_ctx) {
8249 err = IE_ERROR;
8250 goto leave;
8252 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8253 err = IE_ERROR;
8254 goto leave;
8256 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8257 xpath_ctx);
8258 if (!result) {
8259 err = IE_ERROR;
8260 goto leave;
8262 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8263 isds_log_message(context, _("Missing CreateMessageResponse element"));
8264 err = IE_ISDS;
8265 goto leave;
8267 if (result->nodesetval->nodeNr > 1) {
8268 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8269 err = IE_ISDS;
8270 goto leave;
8272 xpath_ctx->node = result->nodesetval->nodeTab[0];
8273 xmlXPathFreeObject(result); result = NULL;
8275 if (outgoing_message->envelope->dmID) {
8276 free(outgoing_message->envelope->dmID);
8277 outgoing_message->envelope->dmID = NULL;
8279 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8280 if (!outgoing_message->envelope->dmID) {
8281 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8282 "but did not return assigned message ID\n"));
8285 leave:
8286 /* TODO: Serialize message into structure member raw */
8287 /* XXX: Each web service transport message in different format.
8288 * Therefore it's not possible to save them directly.
8289 * To save them, one must figure out common format.
8290 * We can leave it on application, or we can implement the ESS format. */
8291 /*if (message_is_complete) {
8292 if (outgoing_message->envelope->dmID) {
8294 /* Add assigned message ID as first child*/
8295 /*xmlNodePtr dmid_text = xmlNewText(
8296 (xmlChar *) outgoing_message->envelope->dmID);
8297 if (!dmid_text) goto serialization_failed;
8299 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8300 BAD_CAST "dmID");
8301 if (!dmid_element) {
8302 xmlFreeNode(dmid_text);
8303 goto serialization_failed;
8306 xmlNodePtr dmid_element_with_text =
8307 xmlAddChild(dmid_element, dmid_text);
8308 if (!dmid_element_with_text) {
8309 xmlFreeNode(dmid_element);
8310 xmlFreeNode(dmid_text);
8311 goto serialization_failed;
8314 node = xmlAddPrevSibling(envelope->childern,
8315 dmid_element_with_text);
8316 if (!node) {
8317 xmlFreeNodeList(dmid_element_with_text);
8318 goto serialization_failed;
8322 /* Serialize message with ID into raw */
8323 /*buffer = serialize_element(envelope)*/
8324 /* }
8326 serialization_failed:
8330 /* Clean up */
8331 xmlXPathFreeObject(result);
8332 xmlXPathFreeContext(xpath_ctx);
8334 free(code);
8335 free(message);
8336 xmlFreeDoc(response);
8337 xmlFreeNode(request);
8339 if (!err)
8340 isds_log(ILF_ISDS, ILL_DEBUG,
8341 _("CreateMessage request processed by server "
8342 "successfully.\n"));
8343 #else /* not HAVE_LIBCURL */
8344 err = IE_NOTSUP;
8345 #endif
8347 return err;
8351 /* Send a message via ISDS to a multiple recipients
8352 * @context is session context
8353 * @outgoing_message is message to send; Some members are mandatory,
8354 * some are optional and some are irrelevant (especially data
8355 * about sender). Data about recipient will be substituted by ISDS from
8356 * @copies. Included pointer to isds_list documents must
8357 * contain at least one document of FILEMETATYPE_MAIN.
8358 * @copies is list of isds_message_copy structures addressing all desired
8359 * recipients. This is read-write structure, some members will be filled with
8360 * valid data from ISDS (message IDs, error codes, error descriptions).
8361 * @return
8362 * ISDS_SUCCESS if all messages have been sent
8363 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8364 * succeeded messages can be identified by copies->data->error),
8365 * or other error code if something other goes wrong. */
8366 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8367 const struct isds_message *outgoing_message,
8368 struct isds_list *copies) {
8370 isds_error err = IE_SUCCESS;
8371 #if HAVE_LIBCURL
8372 isds_error append_err;
8373 xmlNsPtr isds_ns = NULL;
8374 xmlNodePtr request = NULL, recipients, recipient, node;
8375 struct isds_list *item;
8376 struct isds_message_copy *copy;
8377 xmlDocPtr response = NULL;
8378 xmlChar *code = NULL, *message = NULL;
8379 xmlXPathContextPtr xpath_ctx = NULL;
8380 xmlXPathObjectPtr result = NULL;
8381 xmlChar *string = NULL;
8382 int i;
8383 #endif
8385 if (!context) return IE_INVALID_CONTEXT;
8386 zfree(context->long_message);
8387 if (!outgoing_message || !copies) return IE_INVAL;
8389 #if HAVE_LIBCURL
8390 /* Check if connection is established
8391 * TODO: This check should be done downstairs. */
8392 if (!context->curl) return IE_CONNECTION_CLOSED;
8395 /* Build CreateMultipleMessage request */
8396 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
8397 if (!request) {
8398 isds_log_message(context,
8399 _("Could not build CreateMultipleMessage request"));
8400 return IE_ERROR;
8402 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8403 if(!isds_ns) {
8404 isds_log_message(context, _("Could not create ISDS name space"));
8405 xmlFreeNode(request);
8406 return IE_ERROR;
8408 xmlSetNs(request, isds_ns);
8411 /* Build recipients */
8412 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8413 if (!recipients) {
8414 isds_log_message(context, _("Could not add dmRecipients child to "
8415 "CreateMultipleMessage element"));
8416 xmlFreeNode(request);
8417 return IE_ERROR;
8420 /* Insert each recipient */
8421 for (item = copies; item; item = item->next) {
8422 copy = (struct isds_message_copy *) item->data;
8423 if (!copy) {
8424 isds_log_message(context,
8425 _("`copies' list item contains empty data"));
8426 err = IE_INVAL;
8427 goto leave;
8430 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
8431 if (!recipient) {
8432 isds_log_message(context, _("Could not add dmRecipient child to "
8433 "dmRecipients element"));
8434 err = IE_ERROR;
8435 goto leave;
8438 if (!copy->dbIDRecipient) {
8439 isds_log_message(context,
8440 _("Message copy is missing recipient box identifier"));
8441 err = IE_INVAL;
8442 goto leave;
8444 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
8445 INSERT_STRING(recipient, "dmRecipientOrgUnit",
8446 copy->dmRecipientOrgUnit);
8447 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
8448 copy->dmRecipientOrgUnitNum, string);
8449 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
8452 /* Append envelope and files */
8453 err = insert_envelope_files(context, outgoing_message, request, 0);
8454 if (err) goto leave;
8457 isds_log(ILF_ISDS, ILL_DEBUG,
8458 _("Sending CreateMultipleMessage request to ISDS\n"));
8460 /* Sent request */
8461 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8462 if (err) {
8463 isds_log(ILF_ISDS, ILL_DEBUG,
8464 _("Processing ISDS response on CreateMultipleMessage "
8465 "request failed\n"));
8466 goto leave;
8469 /* Check for response status */
8470 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8471 &code, &message, NULL);
8472 if (err) {
8473 isds_log(ILF_ISDS, ILL_DEBUG,
8474 _("ISDS response on CreateMultipleMessage request "
8475 "is missing status\n"));
8476 goto leave;
8479 /* Request processed, but some copies failed */
8480 if (!xmlStrcmp(code, BAD_CAST "0004")) {
8481 char *box_id_locale =
8482 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8483 char *code_locale = _isds_utf82locale((char*)code);
8484 char *message_locale = _isds_utf82locale((char*)message);
8485 isds_log(ILF_ISDS, ILL_DEBUG,
8486 _("Server did accept message for multiple recipients "
8487 "on CreateMultipleMessage request but delivery to "
8488 "some of them failed (code=%s, message=%s)\n"),
8489 box_id_locale, code_locale, message_locale);
8490 isds_log_message(context, message_locale);
8491 free(box_id_locale);
8492 free(code_locale);
8493 free(message_locale);
8494 err = IE_PARTIAL_SUCCESS;
8497 /* Request refused by server as whole */
8498 else if (xmlStrcmp(code, BAD_CAST "0000")) {
8499 char *box_id_locale =
8500 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8501 char *code_locale = _isds_utf82locale((char*)code);
8502 char *message_locale = _isds_utf82locale((char*)message);
8503 isds_log(ILF_ISDS, ILL_DEBUG,
8504 _("Server did not accept message for multiple recipients "
8505 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
8506 box_id_locale, code_locale, message_locale);
8507 isds_log_message(context, message_locale);
8508 free(box_id_locale);
8509 free(code_locale);
8510 free(message_locale);
8511 err = IE_ISDS;
8512 goto leave;
8516 /* Extract data */
8517 xpath_ctx = xmlXPathNewContext(response);
8518 if (!xpath_ctx) {
8519 err = IE_ERROR;
8520 goto leave;
8522 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8523 err = IE_ERROR;
8524 goto leave;
8526 result = xmlXPathEvalExpression(
8527 BAD_CAST "/isds:CreateMultipleMessageResponse"
8528 "/isds:dmMultipleStatus/isds:dmSingleStatus",
8529 xpath_ctx);
8530 if (!result) {
8531 err = IE_ERROR;
8532 goto leave;
8534 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8535 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
8536 err = IE_ISDS;
8537 goto leave;
8540 /* Extract message ID and delivery status for each copy */
8541 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
8542 item = item->next, i++) {
8543 copy = (struct isds_message_copy *) item->data;
8544 xpath_ctx->node = result->nodesetval->nodeTab[i];
8546 append_err = append_TMStatus(context, copy, xpath_ctx);
8547 if (append_err) {
8548 err = append_err;
8549 goto leave;
8552 if (item || i < result->nodesetval->nodeNr) {
8553 isds_printf_message(context, _("ISDS returned unexpected number of "
8554 "message copy delivery states: %d"),
8555 result->nodesetval->nodeNr);
8556 err = IE_ISDS;
8557 goto leave;
8561 leave:
8562 /* Clean up */
8563 free(string);
8564 xmlXPathFreeObject(result);
8565 xmlXPathFreeContext(xpath_ctx);
8567 free(code);
8568 free(message);
8569 xmlFreeDoc(response);
8570 xmlFreeNode(request);
8572 if (!err)
8573 isds_log(ILF_ISDS, ILL_DEBUG,
8574 _("CreateMultipleMessageResponse request processed by server "
8575 "successfully.\n"));
8576 #else /* not HAVE_LIBCURL */
8577 err = IE_NOTSUP;
8578 #endif
8580 return err;
8584 /* Get list of messages. This is common core for getting sent or received
8585 * messages.
8586 * Any criterion argument can be NULL, if you don't care about it.
8587 * @context is session context. Must not be NULL.
8588 * @outgoing_direction is true if you want list of outgoing messages,
8589 * it's false if you want incoming messages.
8590 * @from_time is minimal time and date of message sending inclusive.
8591 * @to_time is maximal time and date of message sending inclusive
8592 * @organization_unit_number is number of sender/recipient respectively.
8593 * @status_filter is bit field of isds_message_status values. Use special
8594 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8595 * all values, you can use bit-wise arithmetic if you want.)
8596 * @offset is index of first message we are interested in. First message is 1.
8597 * Set to 0 (or 1) if you don't care.
8598 * @number is maximal length of list you want to get as input value, outputs
8599 * number of messages matching these criteria. Can be NULL if you don't care
8600 * (applies to output value either).
8601 * @messages is automatically reallocated list of isds_message's. Be ware that
8602 * it returns only brief overview (envelope and some other fields) about each
8603 * message, not the complete message. FIXME: Specify exact fields.
8604 * The list is sorted by delivery time in ascending order.
8605 * Use NULL if you don't care about don't need the data (useful if you want to
8606 * know only the @number). If you provide &NULL, list will be allocated on
8607 * heap, if you provide pointer to non-NULL, list will be freed automatically
8608 * at first. Also in case of error the list will be NULLed.
8609 * @return IE_SUCCESS or appropriate error code. */
8610 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
8611 _Bool outgoing_direction,
8612 const struct timeval *from_time, const struct timeval *to_time,
8613 const long int *organization_unit_number,
8614 const unsigned int status_filter,
8615 const unsigned long int offset, unsigned long int *number,
8616 struct isds_list **messages) {
8618 isds_error err = IE_SUCCESS;
8619 #if HAVE_LIBCURL
8620 xmlNsPtr isds_ns = NULL;
8621 xmlNodePtr request = NULL, node;
8622 xmlDocPtr response = NULL;
8623 xmlChar *code = NULL, *message = NULL;
8624 xmlXPathContextPtr xpath_ctx = NULL;
8625 xmlXPathObjectPtr result = NULL;
8626 xmlChar *string = NULL;
8627 long unsigned int count = 0;
8628 #endif
8630 if (!context) return IE_INVALID_CONTEXT;
8631 zfree(context->long_message);
8633 /* Free former message list if any */
8634 if (messages) isds_list_free(messages);
8636 #if HAVE_LIBCURL
8637 /* Check if connection is established
8638 * TODO: This check should be done downstairs. */
8639 if (!context->curl) return IE_CONNECTION_CLOSED;
8641 /* Build GetListOf*Messages request */
8642 request = xmlNewNode(NULL,
8643 (outgoing_direction) ?
8644 BAD_CAST "GetListOfSentMessages" :
8645 BAD_CAST "GetListOfReceivedMessages"
8647 if (!request) {
8648 isds_log_message(context,
8649 (outgoing_direction) ?
8650 _("Could not build GetListOfSentMessages request") :
8651 _("Could not build GetListOfReceivedMessages request")
8653 return IE_ERROR;
8655 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8656 if(!isds_ns) {
8657 isds_log_message(context, _("Could not create ISDS name space"));
8658 xmlFreeNode(request);
8659 return IE_ERROR;
8661 xmlSetNs(request, isds_ns);
8664 if (from_time) {
8665 err = timeval2timestring(from_time, &string);
8666 if (err) goto leave;
8668 INSERT_STRING(request, "dmFromTime", string);
8669 free(string); string = NULL;
8671 if (to_time) {
8672 err = timeval2timestring(to_time, &string);
8673 if (err) goto leave;
8675 INSERT_STRING(request, "dmToTime", string);
8676 free(string); string = NULL;
8678 if (outgoing_direction) {
8679 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
8680 organization_unit_number, string);
8681 } else {
8682 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
8683 organization_unit_number, string);
8686 if (status_filter > MESSAGESTATE_ANY) {
8687 isds_printf_message(context,
8688 _("Invalid message state filter value: %ld"), status_filter);
8689 err = IE_INVAL;
8690 goto leave;
8692 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
8694 if (offset > 0 ) {
8695 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
8696 } else {
8697 INSERT_STRING(request, "dmOffset", "1");
8700 /* number 0 means no limit */
8701 if (number && *number == 0) {
8702 INSERT_STRING(request, "dmLimit", NULL);
8703 } else {
8704 INSERT_ULONGINT(request, "dmLimit", number, string);
8708 isds_log(ILF_ISDS, ILL_DEBUG,
8709 (outgoing_direction) ?
8710 _("Sending GetListOfSentMessages request to ISDS\n") :
8711 _("Sending GetListOfReceivedMessages request to ISDS\n")
8714 /* Sent request */
8715 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
8716 xmlFreeNode(request); request = NULL;
8718 if (err) {
8719 isds_log(ILF_ISDS, ILL_DEBUG,
8720 (outgoing_direction) ?
8721 _("Processing ISDS response on GetListOfSentMessages "
8722 "request failed\n") :
8723 _("Processing ISDS response on GetListOfReceivedMessages "
8724 "request failed\n")
8726 goto leave;
8729 /* Check for response status */
8730 err = isds_response_status(context, SERVICE_DM_INFO, response,
8731 &code, &message, NULL);
8732 if (err) {
8733 isds_log(ILF_ISDS, ILL_DEBUG,
8734 (outgoing_direction) ?
8735 _("ISDS response on GetListOfSentMessages request "
8736 "is missing status\n") :
8737 _("ISDS response on GetListOfReceivedMessages request "
8738 "is missing status\n")
8740 goto leave;
8743 /* Request processed, but nothing found */
8744 if (xmlStrcmp(code, BAD_CAST "0000")) {
8745 char *code_locale = _isds_utf82locale((char*)code);
8746 char *message_locale = _isds_utf82locale((char*)message);
8747 isds_log(ILF_ISDS, ILL_DEBUG,
8748 (outgoing_direction) ?
8749 _("Server refused GetListOfSentMessages request "
8750 "(code=%s, message=%s)\n") :
8751 _("Server refused GetListOfReceivedMessages request "
8752 "(code=%s, message=%s)\n"),
8753 code_locale, message_locale);
8754 isds_log_message(context, message_locale);
8755 free(code_locale);
8756 free(message_locale);
8757 err = IE_ISDS;
8758 goto leave;
8762 /* Extract data */
8763 xpath_ctx = xmlXPathNewContext(response);
8764 if (!xpath_ctx) {
8765 err = IE_ERROR;
8766 goto leave;
8768 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8769 err = IE_ERROR;
8770 goto leave;
8772 result = xmlXPathEvalExpression(
8773 (outgoing_direction) ?
8774 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
8775 "isds:dmRecords/isds:dmRecord" :
8776 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
8777 "isds:dmRecords/isds:dmRecord",
8778 xpath_ctx);
8779 if (!result) {
8780 err = IE_ERROR;
8781 goto leave;
8784 /* Fill output arguments in */
8785 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8786 struct isds_envelope *envelope;
8787 struct isds_list *item = NULL, *last_item = NULL;
8789 for (count = 0; count < result->nodesetval->nodeNr; count++) {
8790 /* Create new message */
8791 item = calloc(1, sizeof(*item));
8792 if (!item) {
8793 err = IE_NOMEM;
8794 goto leave;
8796 item->destructor = (void(*)(void**)) &isds_message_free;
8797 item->data = calloc(1, sizeof(struct isds_message));
8798 if (!item->data) {
8799 isds_list_free(&item);
8800 err = IE_NOMEM;
8801 goto leave;
8804 /* Extract envelope data */
8805 xpath_ctx->node = result->nodesetval->nodeTab[count];
8806 envelope = NULL;
8807 err = extract_DmRecord(context, &envelope, xpath_ctx);
8808 if (err) {
8809 isds_list_free(&item);
8810 goto leave;
8813 /* Attach extracted envelope */
8814 ((struct isds_message *) item->data)->envelope = envelope;
8816 /* Append new message into the list */
8817 if (!*messages) {
8818 *messages = last_item = item;
8819 } else {
8820 last_item->next = item;
8821 last_item = item;
8825 if (number) *number = count;
8827 leave:
8828 if (err) {
8829 isds_list_free(messages);
8832 free(string);
8833 xmlXPathFreeObject(result);
8834 xmlXPathFreeContext(xpath_ctx);
8836 free(code);
8837 free(message);
8838 xmlFreeDoc(response);
8839 xmlFreeNode(request);
8841 if (!err)
8842 isds_log(ILF_ISDS, ILL_DEBUG,
8843 (outgoing_direction) ?
8844 _("GetListOfSentMessages request processed by server "
8845 "successfully.\n") :
8846 _("GetListOfReceivedMessages request processed by server "
8847 "successfully.\n")
8849 #else /* not HAVE_LIBCURL */
8850 err = IE_NOTSUP;
8851 #endif
8852 return err;
8856 /* Get list of outgoing (already sent) messages.
8857 * Any criterion argument can be NULL, if you don't care about it.
8858 * @context is session context. Must not be NULL.
8859 * @from_time is minimal time and date of message sending inclusive.
8860 * @to_time is maximal time and date of message sending inclusive
8861 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
8862 * @status_filter is bit field of isds_message_status values. Use special
8863 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8864 * all values, you can use bit-wise arithmetic if you want.)
8865 * @offset is index of first message we are interested in. First message is 1.
8866 * Set to 0 (or 1) if you don't care.
8867 * @number is maximal length of list you want to get as input value, outputs
8868 * number of messages matching these criteria. Can be NULL if you don't care
8869 * (applies to output value either).
8870 * @messages is automatically reallocated list of isds_message's. Be ware that
8871 * it returns only brief overview (envelope and some other fields) about each
8872 * message, not the complete message. FIXME: Specify exact fields.
8873 * The list is sorted by delivery time in ascending order.
8874 * Use NULL if you don't care about the meta data (useful if you want to know
8875 * only the @number). If you provide &NULL, list will be allocated on heap,
8876 * if you provide pointer to non-NULL, list will be freed automatically at
8877 * first. Also in case of error the list will be NULLed.
8878 * @return IE_SUCCESS or appropriate error code. */
8879 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
8880 const struct timeval *from_time, const struct timeval *to_time,
8881 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
8882 const unsigned long int offset, unsigned long int *number,
8883 struct isds_list **messages) {
8885 return isds_get_list_of_messages(
8886 context, 1,
8887 from_time, to_time, dmSenderOrgUnitNum, status_filter,
8888 offset, number,
8889 messages);
8893 /* Get list of incoming (addressed to you) messages.
8894 * Any criterion argument can be NULL, if you don't care about it.
8895 * @context is session context. Must not be NULL.
8896 * @from_time is minimal time and date of message sending inclusive.
8897 * @to_time is maximal time and date of message sending inclusive
8898 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
8899 * @status_filter is bit field of isds_message_status values. Use special
8900 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8901 * all values, you can use bit-wise arithmetic if you want.)
8902 * @offset is index of first message we are interested in. First message is 1.
8903 * Set to 0 (or 1) if you don't care.
8904 * @number is maximal length of list you want to get as input value, outputs
8905 * number of messages matching these criteria. Can be NULL if you don't care
8906 * (applies to output value either).
8907 * @messages is automatically reallocated list of isds_message's. Be ware that
8908 * it returns only brief overview (envelope and some other fields) about each
8909 * message, not the complete message. FIXME: Specify exact fields.
8910 * Use NULL if you don't care about the meta data (useful if you want to know
8911 * only the @number). If you provide &NULL, list will be allocated on heap,
8912 * if you provide pointer to non-NULL, list will be freed automatically at
8913 * first. Also in case of error the list will be NULLed.
8914 * @return IE_SUCCESS or appropriate error code. */
8915 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
8916 const struct timeval *from_time, const struct timeval *to_time,
8917 const long int *dmRecipientOrgUnitNum,
8918 const unsigned int status_filter,
8919 const unsigned long int offset, unsigned long int *number,
8920 struct isds_list **messages) {
8922 return isds_get_list_of_messages(
8923 context, 0,
8924 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
8925 offset, number,
8926 messages);
8930 /* Get list of sent message state changes.
8931 * Any criterion argument can be NULL, if you don't care about it.
8932 * @context is session context. Must not be NULL.
8933 * @from_time is minimal time and date of status changes inclusive
8934 * @to_time is maximal time and date of status changes inclusive
8935 * @changed_states is automatically reallocated list of
8936 * isds_message_status_change's. If you provide &NULL, list will be allocated
8937 * on heap, if you provide pointer to non-NULL, list will be freed
8938 * automatically at first. Also in case of error the list will be NULLed.
8939 * XXX: The list item ordering is not specified.
8940 * XXX: Server provides only `recent' changes.
8941 * @return IE_SUCCESS or appropriate error code. */
8942 isds_error isds_get_list_of_sent_message_state_changes(
8943 struct isds_ctx *context,
8944 const struct timeval *from_time, const struct timeval *to_time,
8945 struct isds_list **changed_states) {
8947 isds_error err = IE_SUCCESS;
8948 #if HAVE_LIBCURL
8949 xmlNsPtr isds_ns = NULL;
8950 xmlNodePtr request = NULL, node;
8951 xmlDocPtr response = NULL;
8952 xmlXPathContextPtr xpath_ctx = NULL;
8953 xmlXPathObjectPtr result = NULL;
8954 xmlChar *string = NULL;
8955 long unsigned int count = 0;
8956 #endif
8958 if (!context) return IE_INVALID_CONTEXT;
8959 zfree(context->long_message);
8961 /* Free former message list if any */
8962 isds_list_free(changed_states);
8964 #if HAVE_LIBCURL
8965 /* Check if connection is established
8966 * TODO: This check should be done downstairs. */
8967 if (!context->curl) return IE_CONNECTION_CLOSED;
8969 /* Build GetMessageStateChanges request */
8970 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
8971 if (!request) {
8972 isds_log_message(context,
8973 _("Could not build GetMessageStateChanges request"));
8974 return IE_ERROR;
8976 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8977 if(!isds_ns) {
8978 isds_log_message(context, _("Could not create ISDS name space"));
8979 xmlFreeNode(request);
8980 return IE_ERROR;
8982 xmlSetNs(request, isds_ns);
8985 if (from_time) {
8986 err = timeval2timestring(from_time, &string);
8987 if (err) goto leave;
8989 INSERT_STRING(request, "dmFromTime", string);
8990 zfree(string);
8992 if (to_time) {
8993 err = timeval2timestring(to_time, &string);
8994 if (err) goto leave;
8996 INSERT_STRING(request, "dmToTime", string);
8997 zfree(string);
9000 /* Sent request */
9001 err = send_destroy_request_check_response(context,
9002 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9003 &response, NULL, NULL);
9004 if (err) goto leave;
9007 /* Extract data */
9008 xpath_ctx = xmlXPathNewContext(response);
9009 if (!xpath_ctx) {
9010 err = IE_ERROR;
9011 goto leave;
9013 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9014 err = IE_ERROR;
9015 goto leave;
9017 result = xmlXPathEvalExpression(
9018 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9019 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9020 if (!result) {
9021 err = IE_ERROR;
9022 goto leave;
9025 /* Fill output arguments in */
9026 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9027 struct isds_list *item = NULL, *last_item = NULL;
9029 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9030 /* Create new status change */
9031 item = calloc(1, sizeof(*item));
9032 if (!item) {
9033 err = IE_NOMEM;
9034 goto leave;
9036 item->destructor =
9037 (void(*)(void**)) &isds_message_status_change_free;
9039 /* Extract message status change */
9040 xpath_ctx->node = result->nodesetval->nodeTab[count];
9041 err = extract_StateChangesRecord(context,
9042 (struct isds_message_status_change **) &item->data,
9043 xpath_ctx);
9044 if (err) {
9045 isds_list_free(&item);
9046 goto leave;
9049 /* Append new message status change into the list */
9050 if (!*changed_states) {
9051 *changed_states = last_item = item;
9052 } else {
9053 last_item->next = item;
9054 last_item = item;
9059 leave:
9060 if (err) {
9061 isds_list_free(changed_states);
9064 free(string);
9065 xmlXPathFreeObject(result);
9066 xmlXPathFreeContext(xpath_ctx);
9067 xmlFreeDoc(response);
9068 xmlFreeNode(request);
9070 if (!err)
9071 isds_log(ILF_ISDS, ILL_DEBUG,
9072 _("GetMessageStateChanges request processed by server "
9073 "successfully.\n"));
9074 #else /* not HAVE_LIBCURL */
9075 err = IE_NOTSUP;
9076 #endif
9077 return err;
9081 #if HAVE_LIBCURL
9082 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9083 * code
9084 * @context is session context
9085 * @service is ISDS WS service handler
9086 * @service_name is name of SERVICE_DM_OPERATIONS
9087 * @message_id is message ID to send as service argument to ISDS
9088 * @response is reallocated server SOAP body response as XML document
9089 * @raw_response is reallocated bit stream with response body. Use
9090 * NULL if you don't care
9091 * @raw_response_length is size of @raw_response in bytes
9092 * @code is reallocated ISDS status code
9093 * @status_message is reallocated ISDS status message
9094 * @return error coded from lower layer, context message will be set up
9095 * appropriately. */
9096 static isds_error build_send_check_message_request(struct isds_ctx *context,
9097 const isds_service service, const xmlChar *service_name,
9098 const char *message_id,
9099 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9100 xmlChar **code, xmlChar **status_message) {
9102 isds_error err = IE_SUCCESS;
9103 char *service_name_locale = NULL, *message_id_locale = NULL;
9104 xmlNodePtr request = NULL, node;
9105 xmlNsPtr isds_ns = NULL;
9107 if (!context) return IE_INVALID_CONTEXT;
9108 if (!service_name || !message_id) return IE_INVAL;
9109 if (!response || !code || !status_message) return IE_INVAL;
9110 if (!raw_response_length && raw_response) return IE_INVAL;
9112 /* Free output argument */
9113 xmlFreeDoc(*response); *response = NULL;
9114 if (raw_response) zfree(*raw_response);
9115 zfree(*code);
9116 zfree(*status_message);
9119 /* Check if connection is established
9120 * TODO: This check should be done downstairs. */
9121 if (!context->curl) return IE_CONNECTION_CLOSED;
9123 service_name_locale = _isds_utf82locale((char*)service_name);
9124 message_id_locale = _isds_utf82locale(message_id);
9125 if (!service_name_locale || !message_id_locale) {
9126 err = IE_NOMEM;
9127 goto leave;
9130 /* Build request */
9131 request = xmlNewNode(NULL, service_name);
9132 if (!request) {
9133 isds_printf_message(context,
9134 _("Could not build %s request for %s message ID"),
9135 service_name_locale, message_id_locale);
9136 err = IE_ERROR;
9137 goto leave;
9139 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9140 if(!isds_ns) {
9141 isds_log_message(context, _("Could not create ISDS name space"));
9142 err = IE_ERROR;
9143 goto leave;
9145 xmlSetNs(request, isds_ns);
9148 /* Add requested ID */
9149 err = validate_message_id_length(context, (xmlChar *) message_id);
9150 if (err) goto leave;
9151 INSERT_STRING(request, "dmID", message_id);
9154 isds_log(ILF_ISDS, ILL_DEBUG,
9155 _("Sending %s request for %s message ID to ISDS\n"),
9156 service_name_locale, message_id_locale);
9158 /* Send request */
9159 err = isds(context, service, request, response,
9160 raw_response, raw_response_length);
9161 xmlFreeNode(request); request = NULL;
9163 if (err) {
9164 isds_log(ILF_ISDS, ILL_DEBUG,
9165 _("Processing ISDS response on %s request failed\n"),
9166 service_name_locale);
9167 goto leave;
9170 /* Check for response status */
9171 err = isds_response_status(context, service, *response,
9172 code, status_message, NULL);
9173 if (err) {
9174 isds_log(ILF_ISDS, ILL_DEBUG,
9175 _("ISDS response on %s request is missing status\n"),
9176 service_name_locale);
9177 goto leave;
9180 /* Request processed, but nothing found */
9181 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9182 char *code_locale = _isds_utf82locale((char*) *code);
9183 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9184 isds_log(ILF_ISDS, ILL_DEBUG,
9185 _("Server refused %s request for %s message ID "
9186 "(code=%s, message=%s)\n"),
9187 service_name_locale, message_id_locale,
9188 code_locale, status_message_locale);
9189 isds_log_message(context, status_message_locale);
9190 free(code_locale);
9191 free(status_message_locale);
9192 err = IE_ISDS;
9193 goto leave;
9196 leave:
9197 free(message_id_locale);
9198 free(service_name_locale);
9199 xmlFreeNode(request);
9200 return err;
9204 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9205 * signed data and free ISDS response.
9206 * @context is session context
9207 * @message_id is UTF-8 encoded message ID for logging purpose
9208 * @response is parsed XML document. It will be freed and NULLed in the middle
9209 * of function run to save memory. This is not guaranteed in case of error.
9210 * @request_name is name of ISDS request used to construct response root
9211 * element name and for logging purpose.
9212 * @raw is reallocated output buffer with DER encoded CMS data
9213 * @raw_length is size of @raw buffer in bytes
9214 * @returns standard error codes, in case of error, @raw will be freed and
9215 * NULLed, @response sometimes. */
9216 static isds_error find_extract_signed_data_free_response(
9217 struct isds_ctx *context, const xmlChar *message_id,
9218 xmlDocPtr *response, const xmlChar *request_name,
9219 void **raw, size_t *raw_length) {
9221 isds_error err = IE_SUCCESS;
9222 char *xpath_expression = NULL;
9223 xmlXPathContextPtr xpath_ctx = NULL;
9224 xmlXPathObjectPtr result = NULL;
9225 char *encoded_structure = NULL;
9227 if (!context) return IE_INVALID_CONTEXT;
9228 if (!raw) return IE_INVAL;
9229 zfree(*raw);
9230 if (!message_id || !response || !*response || !request_name || !raw_length)
9231 return IE_INVAL;
9233 /* Build XPath expression */
9234 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9235 "Response/isds:dmSignature");
9236 if (!xpath_expression) return IE_NOMEM;
9238 /* Extract data */
9239 xpath_ctx = xmlXPathNewContext(*response);
9240 if (!xpath_ctx) {
9241 err = IE_ERROR;
9242 goto leave;
9244 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9245 err = IE_ERROR;
9246 goto leave;
9248 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9249 if (!result) {
9250 err = IE_ERROR;
9251 goto leave;
9253 /* Empty response */
9254 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9255 char *message_id_locale = _isds_utf82locale((char*) message_id);
9256 isds_printf_message(context,
9257 _("Server did not return any signed data for message ID `%s' "
9258 "on %s request"),
9259 message_id_locale, request_name);
9260 free(message_id_locale);
9261 err = IE_ISDS;
9262 goto leave;
9264 /* More responses */
9265 if (result->nodesetval->nodeNr > 1) {
9266 char *message_id_locale = _isds_utf82locale((char*) message_id);
9267 isds_printf_message(context,
9268 _("Server did return more signed data for message ID `%s' "
9269 "on %s request"),
9270 message_id_locale, request_name);
9271 free(message_id_locale);
9272 err = IE_ISDS;
9273 goto leave;
9275 /* One response */
9276 xpath_ctx->node = result->nodesetval->nodeTab[0];
9278 /* Extract PKCS#7 structure */
9279 EXTRACT_STRING(".", encoded_structure);
9280 if (!encoded_structure) {
9281 isds_log_message(context, _("dmSignature element is empty"));
9284 /* Here we have delivery info as standalone CMS in encoded_structure.
9285 * We don't need any other data, free them: */
9286 xmlXPathFreeObject(result); result = NULL;
9287 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9288 xmlFreeDoc(*response); *response = NULL;
9291 /* Decode PKCS#7 to DER format */
9292 *raw_length = _isds_b64decode(encoded_structure, raw);
9293 if (*raw_length == (size_t) -1) {
9294 isds_log_message(context,
9295 _("Error while Base64-decoding PKCS#7 structure"));
9296 err = IE_ERROR;
9297 goto leave;
9300 leave:
9301 if (err) {
9302 zfree(*raw);
9303 raw_length = 0;
9306 free(encoded_structure);
9307 xmlXPathFreeObject(result);
9308 xmlXPathFreeContext(xpath_ctx);
9309 free(xpath_expression);
9311 return err;
9313 #endif /* HAVE_LIBCURL */
9316 /* Download incoming message envelope identified by ID.
9317 * @context is session context
9318 * @message_id is message identifier (you can get them from
9319 * isds_get_list_of_received_messages())
9320 * @message is automatically reallocated message retrieved from ISDS.
9321 * It will miss documents per se. Use isds_get_received_message(), if you are
9322 * interested in documents (content) too.
9323 * Returned hash and timestamp require documents to be verifiable. */
9324 isds_error isds_get_received_envelope(struct isds_ctx *context,
9325 const char *message_id, struct isds_message **message) {
9327 isds_error err = IE_SUCCESS;
9328 #if HAVE_LIBCURL
9329 xmlDocPtr response = NULL;
9330 xmlChar *code = NULL, *status_message = NULL;
9331 xmlXPathContextPtr xpath_ctx = NULL;
9332 xmlXPathObjectPtr result = NULL;
9333 #endif
9335 if (!context) return IE_INVALID_CONTEXT;
9336 zfree(context->long_message);
9338 /* Free former message if any */
9339 if (!message) return IE_INVAL;
9340 isds_message_free(message);
9342 #if HAVE_LIBCURL
9343 /* Do request and check for success */
9344 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9345 BAD_CAST "MessageEnvelopeDownload", message_id,
9346 &response, NULL, NULL, &code, &status_message);
9347 if (err) goto leave;
9349 /* Extract data */
9350 xpath_ctx = xmlXPathNewContext(response);
9351 if (!xpath_ctx) {
9352 err = IE_ERROR;
9353 goto leave;
9355 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9356 err = IE_ERROR;
9357 goto leave;
9359 result = xmlXPathEvalExpression(
9360 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9361 "isds:dmReturnedMessageEnvelope",
9362 xpath_ctx);
9363 if (!result) {
9364 err = IE_ERROR;
9365 goto leave;
9367 /* Empty response */
9368 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9369 char *message_id_locale = _isds_utf82locale((char*) message_id);
9370 isds_printf_message(context,
9371 _("Server did not return any envelope for ID `%s' "
9372 "on MessageEnvelopeDownload request"), message_id_locale);
9373 free(message_id_locale);
9374 err = IE_ISDS;
9375 goto leave;
9377 /* More envelops */
9378 if (result->nodesetval->nodeNr > 1) {
9379 char *message_id_locale = _isds_utf82locale((char*) message_id);
9380 isds_printf_message(context,
9381 _("Server did return more envelopes for ID `%s' "
9382 "on MessageEnvelopeDownload request"), message_id_locale);
9383 free(message_id_locale);
9384 err = IE_ISDS;
9385 goto leave;
9387 /* One message */
9388 xpath_ctx->node = result->nodesetval->nodeTab[0];
9390 /* Extract the envelope (= message without documents, hence 0) */
9391 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9392 if (err) goto leave;
9394 /* Save XML blob */
9395 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9396 &(*message)->raw_length);
9398 leave:
9399 if (err) {
9400 isds_message_free(message);
9403 xmlXPathFreeObject(result);
9404 xmlXPathFreeContext(xpath_ctx);
9406 free(code);
9407 free(status_message);
9408 if (!*message || !(*message)->xml) {
9409 xmlFreeDoc(response);
9412 if (!err)
9413 isds_log(ILF_ISDS, ILL_DEBUG,
9414 _("MessageEnvelopeDownload request processed by server "
9415 "successfully.\n")
9417 #else /* not HAVE_LIBCURL */
9418 err = IE_NOTSUP;
9419 #endif
9420 return err;
9424 /* Load delivery info of any format from buffer.
9425 * @context is session context
9426 * @raw_type advertises format of @buffer content. Only delivery info types
9427 * are accepted.
9428 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9429 * retrieve such data from message->raw after calling
9430 * isds_get_signed_delivery_info().
9431 * @length is length of buffer in bytes.
9432 * @message is automatically reallocated message parsed from @buffer.
9433 * @strategy selects how buffer will be attached into raw isds_message member.
9434 * */
9435 isds_error isds_load_delivery_info(struct isds_ctx *context,
9436 const isds_raw_type raw_type,
9437 const void *buffer, const size_t length,
9438 struct isds_message **message, const isds_buffer_strategy strategy) {
9440 isds_error err = IE_SUCCESS;
9441 message_ns_type message_ns;
9442 xmlDocPtr message_doc = NULL;
9443 xmlXPathContextPtr xpath_ctx = NULL;
9444 xmlXPathObjectPtr result = NULL;
9445 void *xml_stream = NULL;
9446 size_t xml_stream_length = 0;
9448 if (!context) return IE_INVALID_CONTEXT;
9449 zfree(context->long_message);
9450 if (!message) return IE_INVAL;
9451 isds_message_free(message);
9452 if (!buffer) return IE_INVAL;
9455 /* Select buffer format and extract XML from CMS*/
9456 switch (raw_type) {
9457 case RAWTYPE_DELIVERYINFO:
9458 message_ns = MESSAGE_NS_UNSIGNED;
9459 xml_stream = (void *) buffer;
9460 xml_stream_length = length;
9461 break;
9463 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
9464 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9465 xml_stream = (void *) buffer;
9466 xml_stream_length = length;
9467 break;
9469 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
9470 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9471 err = _isds_extract_cms_data(context, buffer, length,
9472 &xml_stream, &xml_stream_length);
9473 if (err) goto leave;
9474 break;
9476 default:
9477 isds_log_message(context, _("Bad raw delivery representation type"));
9478 return IE_INVAL;
9479 break;
9482 isds_log(ILF_ISDS, ILL_DEBUG,
9483 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
9484 xml_stream_length, xml_stream);
9486 /* Convert delivery info XML stream into XPath context */
9487 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9488 if (!message_doc) {
9489 err = IE_XML;
9490 goto leave;
9492 xpath_ctx = xmlXPathNewContext(message_doc);
9493 if (!xpath_ctx) {
9494 err = IE_ERROR;
9495 goto leave;
9497 /* XXX: Name spaces mangled for signed delivery info:
9498 * http://isds.czechpoint.cz/v20/delivery:
9500 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
9501 * <q:dmDelivery>
9502 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9503 * <p:dmID>170272</p:dmID>
9504 * ...
9505 * </p:dmDm>
9506 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9507 * ...
9508 * </q:dmEvents>...</q:dmEvents>
9509 * </q:dmDelivery>
9510 * </q:GetDeliveryInfoResponse>
9511 * */
9512 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
9513 err = IE_ERROR;
9514 goto leave;
9516 result = xmlXPathEvalExpression(
9517 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
9518 xpath_ctx);
9519 if (!result) {
9520 err = IE_ERROR;
9521 goto leave;
9523 /* Empty delivery info */
9524 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9525 isds_printf_message(context,
9526 _("XML document is not sisds:dmDelivery document"));
9527 err = IE_ISDS;
9528 goto leave;
9530 /* More delivery info's */
9531 if (result->nodesetval->nodeNr > 1) {
9532 isds_printf_message(context,
9533 _("XML document has more sisds:dmDelivery elements"));
9534 err = IE_ISDS;
9535 goto leave;
9537 /* One delivery info */
9538 xpath_ctx->node = result->nodesetval->nodeTab[0];
9540 /* Extract the envelope (= message without documents, hence 0).
9541 * XXX: extract_TReturnedMessage() can obtain attachments size,
9542 * but delivery info carries none. It's coded as option elements,
9543 * so it should work. */
9544 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9545 if (err) goto leave;
9547 /* Extract events */
9548 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
9549 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
9550 if (err) { err = IE_ERROR; goto leave; }
9551 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
9552 if (err) goto leave;
9554 /* Append raw CMS structure into message */
9555 (*message)->raw_type = raw_type;
9556 switch (strategy) {
9557 case BUFFER_DONT_STORE:
9558 break;
9559 case BUFFER_COPY:
9560 (*message)->raw = malloc(length);
9561 if (!(*message)->raw) {
9562 err = IE_NOMEM;
9563 goto leave;
9565 memcpy((*message)->raw, buffer, length);
9566 (*message)->raw_length = length;
9567 break;
9568 case BUFFER_MOVE:
9569 (*message)->raw = (void *) buffer;
9570 (*message)->raw_length = length;
9571 break;
9572 default:
9573 err = IE_ENUM;
9574 goto leave;
9577 leave:
9578 if (err) {
9579 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
9580 isds_message_free(message);
9583 xmlXPathFreeObject(result);
9584 xmlXPathFreeContext(xpath_ctx);
9585 if (!*message || !(*message)->xml) {
9586 xmlFreeDoc(message_doc);
9588 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9590 if (!err)
9591 isds_log(ILF_ISDS, ILL_DEBUG,
9592 _("Delivery info loaded successfully.\n"));
9593 return err;
9597 /* Download signed delivery info-sheet of given message identified by ID.
9598 * @context is session context
9599 * @message_id is message identifier (you can get them from
9600 * isds_get_list_of_{sent,received}_messages())
9601 * @message is automatically reallocated message retrieved from ISDS.
9602 * It will miss documents per se. Use isds_get_signed_received_message(),
9603 * if you are interested in documents (content). OTOH, only this function
9604 * can get list events message has gone through. */
9605 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
9606 const char *message_id, struct isds_message **message) {
9608 isds_error err = IE_SUCCESS;
9609 #if HAVE_LIBCURL
9610 xmlDocPtr response = NULL;
9611 xmlChar *code = NULL, *status_message = NULL;
9612 void *raw = NULL;
9613 size_t raw_length = 0;
9614 #endif
9616 if (!context) return IE_INVALID_CONTEXT;
9617 zfree(context->long_message);
9619 /* Free former message if any */
9620 if (!message) return IE_INVAL;
9621 isds_message_free(message);
9623 #if HAVE_LIBCURL
9624 /* Do request and check for success */
9625 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9626 BAD_CAST "GetSignedDeliveryInfo", message_id,
9627 &response, NULL, NULL, &code, &status_message);
9628 if (err) goto leave;
9630 /* Find signed delivery info, extract it into raw and maybe free
9631 * response */
9632 err = find_extract_signed_data_free_response(context,
9633 (xmlChar *)message_id, &response,
9634 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
9635 if (err) goto leave;
9637 /* Parse delivery info */
9638 err = isds_load_delivery_info(context,
9639 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
9640 message, BUFFER_MOVE);
9641 if (err) goto leave;
9643 raw = NULL;
9645 leave:
9646 if (err) {
9647 isds_message_free(message);
9650 free(raw);
9651 free(code);
9652 free(status_message);
9653 xmlFreeDoc(response);
9655 if (!err)
9656 isds_log(ILF_ISDS, ILL_DEBUG,
9657 _("GetSignedDeliveryInfo request processed by server "
9658 "successfully.\n")
9660 #else /* not HAVE_LIBCURL */
9661 err = IE_NOTSUP;
9662 #endif
9663 return err;
9667 /* Download delivery info-sheet of given message identified by ID.
9668 * @context is session context
9669 * @message_id is message identifier (you can get them from
9670 * isds_get_list_of_{sent,received}_messages())
9671 * @message is automatically reallocated message retrieved from ISDS.
9672 * It will miss documents per se. Use isds_get_received_message(), if you are
9673 * interested in documents (content). OTOH, only this function can get list
9674 * of events message has gone through. */
9675 isds_error isds_get_delivery_info(struct isds_ctx *context,
9676 const char *message_id, struct isds_message **message) {
9678 isds_error err = IE_SUCCESS;
9679 #if HAVE_LIBCURL
9680 xmlDocPtr response = NULL;
9681 xmlChar *code = NULL, *status_message = NULL;
9682 xmlNodePtr delivery_node = NULL;
9683 void *raw = NULL;
9684 size_t raw_length = 0;
9685 #endif
9687 if (!context) return IE_INVALID_CONTEXT;
9688 zfree(context->long_message);
9690 /* Free former message if any */
9691 if (!message) return IE_INVAL;
9692 isds_message_free(message);
9694 #if HAVE_LIBCURL
9695 /* Do request and check for success */
9696 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9697 BAD_CAST "GetDeliveryInfo", message_id,
9698 &response, NULL, NULL, &code, &status_message);
9699 if (err) goto leave;
9702 /* Serialize delivery info */
9703 delivery_node = xmlDocGetRootElement(response);
9704 if (!delivery_node) {
9705 char *message_id_locale = _isds_utf82locale((char*) message_id);
9706 isds_printf_message(context,
9707 _("Server did not return any delivery info for ID `%s' "
9708 "on GetDeliveryInfo request"), message_id_locale);
9709 free(message_id_locale);
9710 err = IE_ISDS;
9711 goto leave;
9713 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
9714 if (err) goto leave;
9716 /* Parse delivery info */
9717 /* TODO: Here we parse the response second time. We could single delivery
9718 * parser from isds_load_delivery_info() to make things faster. */
9719 err = isds_load_delivery_info(context,
9720 RAWTYPE_DELIVERYINFO, raw, raw_length,
9721 message, BUFFER_MOVE);
9722 if (err) goto leave;
9724 raw = NULL;
9727 leave:
9728 if (err) {
9729 isds_message_free(message);
9732 free(raw);
9733 free(code);
9734 free(status_message);
9735 xmlFreeDoc(response);
9737 if (!err)
9738 isds_log(ILF_ISDS, ILL_DEBUG,
9739 _("GetDeliveryInfo request processed by server "
9740 "successfully.\n")
9742 #else /* not HAVE_LIBCURL */
9743 err = IE_NOTSUP;
9744 #endif
9745 return err;
9749 /* Download incoming message identified by ID.
9750 * @context is session context
9751 * @message_id is message identifier (you can get them from
9752 * isds_get_list_of_received_messages())
9753 * @message is automatically reallocated message retrieved from ISDS */
9754 isds_error isds_get_received_message(struct isds_ctx *context,
9755 const char *message_id, struct isds_message **message) {
9757 isds_error err = IE_SUCCESS;
9758 #if HAVE_LIBCURL
9759 xmlDocPtr response = NULL;
9760 void *xml_stream = NULL;
9761 size_t xml_stream_length;
9762 xmlChar *code = NULL, *status_message = NULL;
9763 xmlXPathContextPtr xpath_ctx = NULL;
9764 xmlXPathObjectPtr result = NULL;
9765 char *phys_path = NULL;
9766 size_t phys_start, phys_end;
9767 #endif
9769 if (!context) return IE_INVALID_CONTEXT;
9770 zfree(context->long_message);
9772 /* Free former message if any */
9773 if (NULL == message) return IE_INVAL;
9774 if (message) isds_message_free(message);
9776 #if HAVE_LIBCURL
9777 /* Do request and check for success */
9778 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
9779 BAD_CAST "MessageDownload", message_id,
9780 &response, &xml_stream, &xml_stream_length,
9781 &code, &status_message);
9782 if (err) goto leave;
9784 /* Extract data */
9785 xpath_ctx = xmlXPathNewContext(response);
9786 if (!xpath_ctx) {
9787 err = IE_ERROR;
9788 goto leave;
9790 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9791 err = IE_ERROR;
9792 goto leave;
9794 result = xmlXPathEvalExpression(
9795 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
9796 xpath_ctx);
9797 if (!result) {
9798 err = IE_ERROR;
9799 goto leave;
9801 /* Empty response */
9802 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9803 char *message_id_locale = _isds_utf82locale((char*) message_id);
9804 isds_printf_message(context,
9805 _("Server did not return any message for ID `%s' "
9806 "on MessageDownload request"), message_id_locale);
9807 free(message_id_locale);
9808 err = IE_ISDS;
9809 goto leave;
9811 /* More messages */
9812 if (result->nodesetval->nodeNr > 1) {
9813 char *message_id_locale = _isds_utf82locale((char*) message_id);
9814 isds_printf_message(context,
9815 _("Server did return more messages for ID `%s' "
9816 "on MessageDownload request"), message_id_locale);
9817 free(message_id_locale);
9818 err = IE_ISDS;
9819 goto leave;
9821 /* One message */
9822 xpath_ctx->node = result->nodesetval->nodeTab[0];
9824 /* Extract the message */
9825 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
9826 if (err) goto leave;
9828 /* Locate raw XML blob */
9829 phys_path = strdup(
9830 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
9831 PHYSXML_ELEMENT_SEPARATOR
9832 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
9833 PHYSXML_ELEMENT_SEPARATOR
9834 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
9836 if (!phys_path) {
9837 err = IE_NOMEM;
9838 goto leave;
9840 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
9841 phys_path, &phys_start, &phys_end);
9842 zfree(phys_path);
9843 if (err) {
9844 isds_log_message(context,
9845 _("Substring with isds:MessageDownloadResponse element "
9846 "could not be located in raw SOAP message"));
9847 goto leave;
9849 /* Save XML blob */
9850 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9851 &(*message)->raw_length);*/
9852 /* TODO: Store name space declarations from ancestors */
9853 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
9854 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
9855 (*message)->raw_length = phys_end - phys_start + 1;
9856 (*message)->raw = malloc((*message)->raw_length);
9857 if (!(*message)->raw) {
9858 err = IE_NOMEM;
9859 goto leave;
9861 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
9864 leave:
9865 if (err) {
9866 isds_message_free(message);
9869 free(phys_path);
9871 xmlXPathFreeObject(result);
9872 xmlXPathFreeContext(xpath_ctx);
9874 free(code);
9875 free(status_message);
9876 free(xml_stream);
9877 if (!*message || !(*message)->xml) {
9878 xmlFreeDoc(response);
9881 if (!err)
9882 isds_log(ILF_ISDS, ILL_DEBUG,
9883 _("MessageDownload request processed by server "
9884 "successfully.\n")
9886 #else /* not HAVE_LIBCURL */
9887 err = IE_NOTSUP;
9888 #endif
9889 return err;
9893 /* Load message of any type from buffer.
9894 * @context is session context
9895 * @raw_type defines content type of @buffer. Only message types are allowed.
9896 * @buffer is message raw representation. Format (CMS, plain signed,
9897 * message direction) is defined in @raw_type. You can retrieve such data
9898 * from message->raw after calling isds_get_[signed]{received,sent}_message().
9899 * @length is length of buffer in bytes.
9900 * @message is automatically reallocated message parsed from @buffer.
9901 * @strategy selects how buffer will be attached into raw isds_message member.
9902 * */
9903 isds_error isds_load_message(struct isds_ctx *context,
9904 const isds_raw_type raw_type, const void *buffer, const size_t length,
9905 struct isds_message **message, const isds_buffer_strategy strategy) {
9907 isds_error err = IE_SUCCESS;
9908 void *xml_stream = NULL;
9909 size_t xml_stream_length = 0;
9910 message_ns_type message_ns;
9911 xmlDocPtr message_doc = NULL;
9912 xmlXPathContextPtr xpath_ctx = NULL;
9913 xmlXPathObjectPtr result = NULL;
9915 if (!context) return IE_INVALID_CONTEXT;
9916 zfree(context->long_message);
9917 if (!message) return IE_INVAL;
9918 isds_message_free(message);
9919 if (!buffer) return IE_INVAL;
9922 /* Select buffer format and extract XML from CMS*/
9923 switch (raw_type) {
9924 case RAWTYPE_INCOMING_MESSAGE:
9925 message_ns = MESSAGE_NS_UNSIGNED;
9926 xml_stream = (void *) buffer;
9927 xml_stream_length = length;
9928 break;
9930 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
9931 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9932 xml_stream = (void *) buffer;
9933 xml_stream_length = length;
9934 break;
9936 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
9937 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9938 err = _isds_extract_cms_data(context, buffer, length,
9939 &xml_stream, &xml_stream_length);
9940 if (err) goto leave;
9941 break;
9943 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
9944 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9945 xml_stream = (void *) buffer;
9946 xml_stream_length = length;
9947 break;
9949 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
9950 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9951 err = _isds_extract_cms_data(context, buffer, length,
9952 &xml_stream, &xml_stream_length);
9953 if (err) goto leave;
9954 break;
9956 default:
9957 isds_log_message(context, _("Bad raw message representation type"));
9958 return IE_INVAL;
9959 break;
9962 isds_log(ILF_ISDS, ILL_DEBUG,
9963 _("Loading message:\n%.*s\nEnd of message\n"),
9964 xml_stream_length, xml_stream);
9966 /* Convert messages XML stream into XPath context */
9967 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9968 if (!message_doc) {
9969 err = IE_XML;
9970 goto leave;
9972 xpath_ctx = xmlXPathNewContext(message_doc);
9973 if (!xpath_ctx) {
9974 err = IE_ERROR;
9975 goto leave;
9977 /* XXX: Standard name space for unsigned incoming direction:
9978 * http://isds.czechpoint.cz/v20/
9980 * XXX: Name spaces mangled for signed outgoing direction:
9981 * http://isds.czechpoint.cz/v20/SentMessage:
9983 * <q:MessageDownloadResponse
9984 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
9985 * <q:dmReturnedMessage>
9986 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9987 * <p:dmID>151916</p:dmID>
9988 * ...
9989 * </p:dmDm>
9990 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9991 * ...
9992 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9993 * </q:dmReturnedMessage>
9994 * </q:MessageDownloadResponse>
9996 * XXX: Name spaces mangled for signed incoming direction:
9997 * http://isds.czechpoint.cz/v20/message:
9999 * <q:MessageDownloadResponse
10000 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10001 * <q:dmReturnedMessage>
10002 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10003 * <p:dmID>151916</p:dmID>
10004 * ...
10005 * </p:dmDm>
10006 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10007 * ...
10008 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10009 * </q:dmReturnedMessage>
10010 * </q:MessageDownloadResponse>
10012 * Stupidity of ISDS developers is unlimited */
10013 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10014 err = IE_ERROR;
10015 goto leave;
10017 result = xmlXPathEvalExpression(
10018 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10019 xpath_ctx);
10020 if (!result) {
10021 err = IE_ERROR;
10022 goto leave;
10024 /* Empty message */
10025 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10026 isds_printf_message(context,
10027 _("XML document does not contain "
10028 "sisds:dmReturnedMessage element"));
10029 err = IE_ISDS;
10030 goto leave;
10032 /* More messages */
10033 if (result->nodesetval->nodeNr > 1) {
10034 isds_printf_message(context,
10035 _("XML document has more sisds:dmReturnedMessage elements"));
10036 err = IE_ISDS;
10037 goto leave;
10039 /* One message */
10040 xpath_ctx->node = result->nodesetval->nodeTab[0];
10042 /* Extract the message */
10043 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10044 if (err) goto leave;
10046 /* Append raw buffer into message */
10047 (*message)->raw_type = raw_type;
10048 switch (strategy) {
10049 case BUFFER_DONT_STORE:
10050 break;
10051 case BUFFER_COPY:
10052 (*message)->raw = malloc(length);
10053 if (!(*message)->raw) {
10054 err = IE_NOMEM;
10055 goto leave;
10057 memcpy((*message)->raw, buffer, length);
10058 (*message)->raw_length = length;
10059 break;
10060 case BUFFER_MOVE:
10061 (*message)->raw = (void *) buffer;
10062 (*message)->raw_length = length;
10063 break;
10064 default:
10065 err = IE_ENUM;
10066 goto leave;
10070 leave:
10071 if (err) {
10072 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10073 isds_message_free(message);
10076 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10077 xmlXPathFreeObject(result);
10078 xmlXPathFreeContext(xpath_ctx);
10079 if (!*message || !(*message)->xml) {
10080 xmlFreeDoc(message_doc);
10083 if (!err)
10084 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10085 return err;
10089 /* Determine type of raw message or delivery info according some heuristics.
10090 * It does not validate the raw blob.
10091 * @context is session context
10092 * @raw_type returns content type of @buffer. Valid only if exit code of this
10093 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10094 * reallocated memory.
10095 * @buffer is message raw representation.
10096 * @length is length of buffer in bytes. */
10097 isds_error isds_guess_raw_type(struct isds_ctx *context,
10098 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10099 isds_error err;
10100 void *xml_stream = NULL;
10101 size_t xml_stream_length = 0;
10102 xmlDocPtr document = NULL;
10103 xmlNodePtr root = NULL;
10105 if (!context) return IE_INVALID_CONTEXT;
10106 zfree(context->long_message);
10107 if (length == 0 || !buffer) return IE_INVAL;
10108 if (!raw_type) return IE_INVAL;
10110 /* Try CMS */
10111 err = _isds_extract_cms_data(context, buffer, length,
10112 &xml_stream, &xml_stream_length);
10113 if (err) {
10114 xml_stream = (void *) buffer;
10115 xml_stream_length = (size_t) length;
10116 err = IE_SUCCESS;
10119 /* Try XML */
10120 document = xmlParseMemory(xml_stream, xml_stream_length);
10121 if (!document) {
10122 isds_printf_message(context,
10123 _("Could not parse data as XML document"));
10124 err = IE_NOTSUP;
10125 goto leave;
10128 /* Get root element */
10129 root = xmlDocGetRootElement(document);
10130 if (!root) {
10131 isds_printf_message(context,
10132 _("XML document is missing root element"));
10133 err = IE_XML;
10134 goto leave;
10137 if (!root->ns || !root->ns->href) {
10138 isds_printf_message(context,
10139 _("Root element does not belong to any name space"));
10140 err = IE_NOTSUP;
10141 goto leave;
10144 /* Test name space */
10145 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10146 if (xml_stream == buffer)
10147 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10148 else
10149 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10150 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10151 if (xml_stream == buffer)
10152 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10153 else
10154 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10155 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10156 if (xml_stream == buffer)
10157 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10158 else
10159 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10160 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10161 if (xml_stream != buffer) {
10162 isds_printf_message(context,
10163 _("Document in ISDS name space is encapsulated into CMS" ));
10164 err = IE_NOTSUP;
10165 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10166 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10167 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10168 *raw_type = RAWTYPE_DELIVERYINFO;
10169 else {
10170 isds_printf_message(context,
10171 _("Unknown root element in ISDS name space"));
10172 err = IE_NOTSUP;
10174 } else {
10175 isds_printf_message(context,
10176 _("Unknown name space"));
10177 err = IE_NOTSUP;
10180 leave:
10181 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10182 xmlFreeDoc(document);
10183 return err;
10187 /* Download signed incoming/outgoing message identified by ID.
10188 * @context is session context
10189 * @output is true for outgoing message, false for incoming message
10190 * @message_id is message identifier (you can get them from
10191 * isds_get_list_of_{sent,received}_messages())
10192 * @message is automatically reallocated message retrieved from ISDS. The raw
10193 * member will be filled with PKCS#7 structure in DER format. */
10194 static isds_error isds_get_signed_message(struct isds_ctx *context,
10195 const _Bool outgoing, const char *message_id,
10196 struct isds_message **message) {
10198 isds_error err = IE_SUCCESS;
10199 #if HAVE_LIBCURL
10200 xmlDocPtr response = NULL;
10201 xmlChar *code = NULL, *status_message = NULL;
10202 xmlXPathContextPtr xpath_ctx = NULL;
10203 xmlXPathObjectPtr result = NULL;
10204 char *encoded_structure = NULL;
10205 void *raw = NULL;
10206 size_t raw_length = 0;
10207 #endif
10209 if (!context) return IE_INVALID_CONTEXT;
10210 zfree(context->long_message);
10211 if (!message) return IE_INVAL;
10212 isds_message_free(message);
10214 #if HAVE_LIBCURL
10215 /* Do request and check for success */
10216 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10217 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10218 BAD_CAST "SignedMessageDownload",
10219 message_id, &response, NULL, NULL, &code, &status_message);
10220 if (err) goto leave;
10222 /* Find signed message, extract it into raw and maybe free
10223 * response */
10224 err = find_extract_signed_data_free_response(context,
10225 (xmlChar *)message_id, &response,
10226 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10227 BAD_CAST "SignedMessageDownload",
10228 &raw, &raw_length);
10229 if (err) goto leave;
10231 /* Parse message */
10232 err = isds_load_message(context,
10233 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10234 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10235 raw, raw_length, message, BUFFER_MOVE);
10236 if (err) goto leave;
10238 raw = NULL;
10240 leave:
10241 if (err) {
10242 isds_message_free(message);
10245 free(encoded_structure);
10246 xmlXPathFreeObject(result);
10247 xmlXPathFreeContext(xpath_ctx);
10248 free(raw);
10250 free(code);
10251 free(status_message);
10252 xmlFreeDoc(response);
10254 if (!err)
10255 isds_log(ILF_ISDS, ILL_DEBUG,
10256 (outgoing) ?
10257 _("SignedSentMessageDownload request processed by server "
10258 "successfully.\n") :
10259 _("SignedMessageDownload request processed by server "
10260 "successfully.\n")
10262 #else /* not HAVE_LIBCURL */
10263 err = IE_NOTSUP;
10264 #endif
10265 return err;
10269 /* Download signed incoming message identified by ID.
10270 * @context is session context
10271 * @message_id is message identifier (you can get them from
10272 * isds_get_list_of_received_messages())
10273 * @message is automatically reallocated message retrieved from ISDS. The raw
10274 * member will be filled with PKCS#7 structure in DER format. */
10275 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10276 const char *message_id, struct isds_message **message) {
10277 return isds_get_signed_message(context, 0, message_id, message);
10281 /* Download signed outgoing message identified by ID.
10282 * @context is session context
10283 * @message_id is message identifier (you can get them from
10284 * isds_get_list_of_sent_messages())
10285 * @message is automatically reallocated message retrieved from ISDS. The raw
10286 * member will be filled with PKCS#7 structure in DER format. */
10287 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10288 const char *message_id, struct isds_message **message) {
10289 return isds_get_signed_message(context, 1, message_id, message);
10293 /* Get type and name of user who sent a message identified by ID.
10294 * @context is session context
10295 * @message_id is message identifier
10296 * @sender_type is pointer to automatically allocated type of sender detected
10297 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10298 * library or to the server, NULL will be returned. Pass NULL if you don't
10299 * care about it.
10300 * @raw_sender_type is automatically reallocated UTF-8 string describing
10301 * sender type or NULL if not known to server. Pass NULL if you don't care.
10302 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10303 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10304 isds_error isds_get_message_sender(struct isds_ctx *context,
10305 const char *message_id, isds_sender_type **sender_type,
10306 char **raw_sender_type, char **sender_name) {
10307 isds_error err = IE_SUCCESS;
10308 #if HAVE_LIBCURL
10309 xmlDocPtr response = NULL;
10310 xmlChar *code = NULL, *status_message = NULL;
10311 xmlXPathContextPtr xpath_ctx = NULL;
10312 xmlXPathObjectPtr result = NULL;
10313 char *type_string = NULL;
10314 #endif
10316 if (!context) return IE_INVALID_CONTEXT;
10317 zfree(context->long_message);
10318 if (sender_type) zfree(*sender_type);
10319 if (raw_sender_type) zfree(*raw_sender_type);
10320 if (sender_name) zfree(*sender_name);
10321 if (!message_id) return IE_INVAL;
10323 #if HAVE_LIBCURL
10324 /* Do request and check for success */
10325 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10326 BAD_CAST "GetMessageAuthor",
10327 message_id, &response, NULL, NULL, &code, &status_message);
10328 if (err) goto leave;
10330 /* Extract data */
10331 xpath_ctx = xmlXPathNewContext(response);
10332 if (!xpath_ctx) {
10333 err = IE_ERROR;
10334 goto leave;
10336 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10337 err = IE_ERROR;
10338 goto leave;
10340 result = xmlXPathEvalExpression(
10341 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10342 if (!result) {
10343 err = IE_ERROR;
10344 goto leave;
10346 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10347 isds_log_message(context,
10348 _("Missing GetMessageAuthorResponse element"));
10349 err = IE_ISDS;
10350 goto leave;
10352 if (result->nodesetval->nodeNr > 1) {
10353 isds_log_message(context,
10354 _("Multiple GetMessageAuthorResponse element"));
10355 err = IE_ISDS;
10356 goto leave;
10358 xpath_ctx->node = result->nodesetval->nodeTab[0];
10359 xmlXPathFreeObject(result); result = NULL;
10361 /* Fill output arguments in */
10362 EXTRACT_STRING("isds:userType", type_string);
10363 if (NULL != type_string) {
10364 if (NULL != sender_type) {
10365 *sender_type = calloc(1, sizeof(**sender_type));
10366 if (NULL == *sender_type) {
10367 err = IE_NOMEM;
10368 goto leave;
10371 err = string2isds_sender_type((xmlChar *)type_string,
10372 *sender_type);
10373 if (err) {
10374 zfree(*sender_type);
10375 if (err == IE_ENUM) {
10376 err = IE_SUCCESS;
10377 char *type_string_locale = _isds_utf82locale(type_string);
10378 isds_log(ILF_ISDS, ILL_WARNING,
10379 _("Unknown isds:userType value: %s"),
10380 type_string_locale);
10381 free(type_string_locale);
10386 if (NULL != sender_name)
10387 EXTRACT_STRING("isds:authorName", *sender_name);
10389 leave:
10390 if (err) {
10391 if (NULL != sender_type) zfree(*sender_type);
10392 zfree(type_string);
10393 if (NULL != sender_name) zfree(*sender_name);
10395 if (NULL != raw_sender_type) *raw_sender_type = type_string;
10397 xmlXPathFreeObject(result);
10398 xmlXPathFreeContext(xpath_ctx);
10400 free(code);
10401 free(status_message);
10402 xmlFreeDoc(response);
10404 if (!err)
10405 isds_log(ILF_ISDS, ILL_DEBUG,
10406 _("GetMessageAuthor request processed by server "
10407 "successfully.\n"));
10408 #else /* not HAVE_LIBCURL */
10409 err = IE_NOTSUP;
10410 #endif
10411 return err;
10415 /* Retrieve hash of message identified by ID stored in ISDS.
10416 * @context is session context
10417 * @message_id is message identifier
10418 * @hash is automatically reallocated message hash downloaded from ISDS.
10419 * Message must exist in system and must not be deleted. */
10420 isds_error isds_download_message_hash(struct isds_ctx *context,
10421 const char *message_id, struct isds_hash **hash) {
10423 isds_error err = IE_SUCCESS;
10424 #if HAVE_LIBCURL
10425 xmlDocPtr response = NULL;
10426 xmlChar *code = NULL, *status_message = NULL;
10427 xmlXPathContextPtr xpath_ctx = NULL;
10428 xmlXPathObjectPtr result = NULL;
10429 #endif
10431 if (!context) return IE_INVALID_CONTEXT;
10432 zfree(context->long_message);
10434 isds_hash_free(hash);
10436 #if HAVE_LIBCURL
10437 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10438 BAD_CAST "VerifyMessage", message_id,
10439 &response, NULL, NULL, &code, &status_message);
10440 if (err) goto leave;
10443 /* Extract data */
10444 xpath_ctx = xmlXPathNewContext(response);
10445 if (!xpath_ctx) {
10446 err = IE_ERROR;
10447 goto leave;
10449 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10450 err = IE_ERROR;
10451 goto leave;
10453 result = xmlXPathEvalExpression(
10454 BAD_CAST "/isds:VerifyMessageResponse",
10455 xpath_ctx);
10456 if (!result) {
10457 err = IE_ERROR;
10458 goto leave;
10460 /* Empty response */
10461 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10462 char *message_id_locale = _isds_utf82locale((char*) message_id);
10463 isds_printf_message(context,
10464 _("Server did not return any response for ID `%s' "
10465 "on VerifyMessage request"), message_id_locale);
10466 free(message_id_locale);
10467 err = IE_ISDS;
10468 goto leave;
10470 /* More responses */
10471 if (result->nodesetval->nodeNr > 1) {
10472 char *message_id_locale = _isds_utf82locale((char*) message_id);
10473 isds_printf_message(context,
10474 _("Server did return more responses for ID `%s' "
10475 "on VerifyMessage request"), message_id_locale);
10476 free(message_id_locale);
10477 err = IE_ISDS;
10478 goto leave;
10480 /* One response */
10481 xpath_ctx->node = result->nodesetval->nodeTab[0];
10483 /* Extract the hash */
10484 err = find_and_extract_DmHash(context, hash, xpath_ctx);
10486 leave:
10487 if (err) {
10488 isds_hash_free(hash);
10491 xmlXPathFreeObject(result);
10492 xmlXPathFreeContext(xpath_ctx);
10494 free(code);
10495 free(status_message);
10496 xmlFreeDoc(response);
10498 if (!err)
10499 isds_log(ILF_ISDS, ILL_DEBUG,
10500 _("VerifyMessage request processed by server "
10501 "successfully.\n")
10503 #else /* not HAVE_LIBCURL */
10504 err = IE_NOTSUP;
10505 #endif
10506 return err;
10510 /* Erase message specified by @message_id from long term storage. Other
10511 * message cannot be erased on user request.
10512 * @context is session context
10513 * @message_id is message identifier.
10514 * @incoming is true for incoming message, false for outgoing message.
10515 * @return
10516 * IE_SUCCESS if message has ben removed
10517 * IE_INVAL if message does not exist in long term storage or message
10518 * belongs to different box
10519 * TODO: IE_NOEPRM if user has no permission to erase a message */
10520 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
10521 const char *message_id, _Bool incoming) {
10522 isds_error err = IE_SUCCESS;
10523 #if HAVE_LIBCURL
10524 xmlNodePtr request = NULL, node;
10525 xmlNsPtr isds_ns = NULL;
10526 xmlDocPtr response = NULL;
10527 xmlChar *code = NULL, *status_message = NULL;
10528 #endif
10530 if (!context) return IE_INVALID_CONTEXT;
10531 zfree(context->long_message);
10532 if (NULL == message_id) return IE_INVAL;
10534 /* Check if connection is established
10535 * TODO: This check should be done downstairs. */
10536 if (!context->curl) return IE_CONNECTION_CLOSED;
10538 #if HAVE_LIBCURL
10539 /* Build request */
10540 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
10541 if (!request) {
10542 isds_log_message(context,
10543 _("Could build EraseMessage request"));
10544 return IE_ERROR;
10546 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10547 if(!isds_ns) {
10548 isds_log_message(context, _("Could not create ISDS name space"));
10549 xmlFreeNode(request);
10550 return IE_ERROR;
10552 xmlSetNs(request, isds_ns);
10554 err = validate_message_id_length(context, (xmlChar *) message_id);
10555 if (err) goto leave;
10556 INSERT_STRING(request, "dmID", message_id);
10558 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
10561 /* Send request */
10562 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
10563 "message ID %s to ISDS\n"), message_id);
10564 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
10565 xmlFreeNode(request); request = NULL;
10567 if (err) {
10568 isds_log(ILF_ISDS, ILL_DEBUG,
10569 _("Processing ISDS response on EraseMessage request "
10570 "failed\n"));
10571 goto leave;
10574 /* Check for response status */
10575 err = isds_response_status(context, SERVICE_DM_INFO, response,
10576 &code, &status_message, NULL);
10577 if (err) {
10578 isds_log(ILF_ISDS, ILL_DEBUG,
10579 _("ISDS response on EraseMessage request is missing "
10580 "status\n"));
10581 goto leave;
10584 /* Check server status code */
10585 if (!xmlStrcmp(code, BAD_CAST "1211")) {
10586 isds_log_message(context, _("Message to erase belongs to other box"));
10587 err = IE_INVAL;
10588 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
10589 isds_log_message(context, _("Message to erase is not saved in "
10590 "long term storage or the direction does not match"));
10591 err = IE_INVAL;
10592 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
10593 char *code_locale = _isds_utf82locale((char*) code);
10594 char *message_locale = _isds_utf82locale((char*) status_message);
10595 isds_log(ILF_ISDS, ILL_DEBUG,
10596 _("Server refused EraseMessage request "
10597 "(code=%s, message=%s)\n"),
10598 code_locale, message_locale);
10599 isds_log_message(context, message_locale);
10600 free(code_locale);
10601 free(message_locale);
10602 err = IE_ISDS;
10603 goto leave;
10606 leave:
10607 free(code);
10608 free(status_message);
10609 xmlFreeDoc(response);
10610 xmlFreeNode(request);
10612 if (!err)
10613 isds_log(ILF_ISDS, ILL_DEBUG,
10614 _("EraseMessage request processed by server "
10615 "successfully.\n")
10617 #else /* not HAVE_LIBCURL */
10618 err = IE_NOTSUP;
10619 #endif
10620 return err;
10624 /* Mark message as read. This is a transactional commit function to acknowledge
10625 * to ISDS the message has been downloaded and processed by client properly.
10626 * @context is session context
10627 * @message_id is message identifier. */
10628 isds_error isds_mark_message_read(struct isds_ctx *context,
10629 const char *message_id) {
10631 isds_error err = IE_SUCCESS;
10632 #if HAVE_LIBCURL
10633 xmlDocPtr response = NULL;
10634 xmlChar *code = NULL, *status_message = NULL;
10635 #endif
10637 if (!context) return IE_INVALID_CONTEXT;
10638 zfree(context->long_message);
10640 #if HAVE_LIBCURL
10641 /* Do request and check for success */
10642 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10643 BAD_CAST "MarkMessageAsDownloaded", message_id,
10644 &response, NULL, NULL, &code, &status_message);
10646 free(code);
10647 free(status_message);
10648 xmlFreeDoc(response);
10650 if (!err)
10651 isds_log(ILF_ISDS, ILL_DEBUG,
10652 _("MarkMessageAsDownloaded request processed by server "
10653 "successfully.\n")
10655 #else /* not HAVE_LIBCURL */
10656 err = IE_NOTSUP;
10657 #endif
10658 return err;
10662 /* Mark message as received by recipient. This is applicable only to
10663 * commercial message. Use envelope->dmType message member to distinguish
10664 * commercial message from government message. Government message is
10665 * received automatically (by law), commercial message on recipient request.
10666 * @context is session context
10667 * @message_id is message identifier. */
10668 isds_error isds_mark_message_received(struct isds_ctx *context,
10669 const char *message_id) {
10671 isds_error err = IE_SUCCESS;
10672 #if HAVE_LIBCURL
10673 xmlDocPtr response = NULL;
10674 xmlChar *code = NULL, *status_message = NULL;
10675 #endif
10677 if (!context) return IE_INVALID_CONTEXT;
10678 zfree(context->long_message);
10680 #if HAVE_LIBCURL
10681 /* Do request and check for success */
10682 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10683 BAD_CAST "ConfirmDelivery", message_id,
10684 &response, NULL, NULL, &code, &status_message);
10686 free(code);
10687 free(status_message);
10688 xmlFreeDoc(response);
10690 if (!err)
10691 isds_log(ILF_ISDS, ILL_DEBUG,
10692 _("ConfirmDelivery request processed by server "
10693 "successfully.\n")
10695 #else /* not HAVE_LIBCURL */
10696 err = IE_NOTSUP;
10697 #endif
10698 return err;
10702 /* Send document for authorized conversion into Czech POINT system.
10703 * This is public anonymous service, no log-in necessary. Special context is
10704 * used to reuse keep-a-live HTTPS connection.
10705 * @context is Czech POINT session context. DO NOT use context connected to
10706 * ISDS server. Use new context or context used by this function previously.
10707 * @document is document to convert. Only data, data_length, dmFileDescr and
10708 * is_xml members are significant. Be ware that not all document formats can be
10709 * converted (signed PDF 1.3 and higher only (2010-02 state)).
10710 * @id is reallocated identifier assigned by Czech POINT system to
10711 * your document on submit. Use is to tell it to Czech POINT officer.
10712 * @date is reallocated document submit date (submitted documents
10713 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
10714 * value. */
10715 isds_error czp_convert_document(struct isds_ctx *context,
10716 const struct isds_document *document,
10717 char **id, struct tm **date) {
10718 isds_error err = IE_SUCCESS;
10719 #if HAVE_LIBCURL
10720 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
10721 xmlNodePtr request = NULL, node;
10722 xmlDocPtr response = NULL;
10724 xmlXPathContextPtr xpath_ctx = NULL;
10725 xmlXPathObjectPtr result = NULL;
10726 long int status = -1;
10727 long int *status_ptr = &status;
10728 char *string = NULL;
10729 #endif
10732 if (!context) return IE_INVALID_CONTEXT;
10733 zfree(context->long_message);
10734 if (!document || !id || !date) return IE_INVAL;
10736 if (document->is_xml) {
10737 isds_log_message(context,
10738 _("XML documents cannot be submitted to conversion"));
10739 return IE_NOTSUP;
10742 /* Free output arguments */
10743 zfree(*id);
10744 zfree(*date);
10746 #if HAVE_LIBCURL
10747 /* Store configuration */
10748 context->type = CTX_TYPE_CZP;
10749 free(context->url);
10750 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
10751 if (!(context->url))
10752 return IE_NOMEM;
10754 /* Prepare CURL handle if not yet connected */
10755 if (!context->curl) {
10756 context->curl = curl_easy_init();
10757 if (!(context->curl))
10758 return IE_ERROR;
10761 /* Build conversion request */
10762 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
10763 if (!request) {
10764 isds_log_message(context,
10765 _("Could not build Czech POINT conversion request"));
10766 return IE_ERROR;
10768 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
10769 if(!deposit_ns) {
10770 isds_log_message(context,
10771 _("Could not create Czech POINT deposit name space"));
10772 xmlFreeNode(request);
10773 return IE_ERROR;
10775 xmlSetNs(request, deposit_ns);
10777 /* Insert children. They are in empty namespace! */
10778 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
10779 if(!empty_ns) {
10780 isds_log_message(context, _("Could not create empty name space"));
10781 err = IE_ERROR;
10782 goto leave;
10784 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
10785 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
10786 document->dmFileDescr);
10788 /* Document encoded in Base64 */
10789 err = insert_base64_encoded_string(context, request, empty_ns, "document",
10790 document->data, document->data_length);
10791 if (err) goto leave;
10793 isds_log(ILF_ISDS, ILL_DEBUG,
10794 _("Submitting document for conversion into Czech POINT deposit"));
10796 /* Send conversion request */
10797 err = _czp_czpdeposit(context, request, &response);
10798 xmlFreeNode(request); request = NULL;
10800 if (err) {
10801 czp_do_close_connection(context);
10802 goto leave;
10806 /* Extract response */
10807 xpath_ctx = xmlXPathNewContext(response);
10808 if (!xpath_ctx) {
10809 err = IE_ERROR;
10810 goto leave;
10812 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10813 err = IE_ERROR;
10814 goto leave;
10816 result = xmlXPathEvalExpression(
10817 BAD_CAST "/deposit:saveDocumentResponse/return",
10818 xpath_ctx);
10819 if (!result) {
10820 err = IE_ERROR;
10821 goto leave;
10823 /* Empty response */
10824 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10825 isds_printf_message(context,
10826 _("Missing `return' element in Czech POINT deposit response"));
10827 err = IE_ISDS;
10828 goto leave;
10830 /* More responses */
10831 if (result->nodesetval->nodeNr > 1) {
10832 isds_printf_message(context,
10833 _("Multiple `return' element in Czech POINT deposit response"));
10834 err = IE_ISDS;
10835 goto leave;
10837 /* One response */
10838 xpath_ctx->node = result->nodesetval->nodeTab[0];
10840 /* Get status */
10841 EXTRACT_LONGINT("status", status_ptr, 1);
10842 if (status) {
10843 EXTRACT_STRING("statusMsg", string);
10844 char *string_locale = _isds_utf82locale(string);
10845 isds_printf_message(context,
10846 _("Czech POINT deposit refused document for conversion "
10847 "(code=%ld, message=%s)"),
10848 status, string_locale);
10849 free(string_locale);
10850 err = IE_ISDS;
10851 goto leave;
10854 /* Get document ID */
10855 EXTRACT_STRING("documentID", *id);
10857 /* Get submit date */
10858 EXTRACT_STRING("dateInserted", string);
10859 if (string) {
10860 *date = calloc(1, sizeof(**date));
10861 if (!*date) {
10862 err = IE_NOMEM;
10863 goto leave;
10865 err = _isds_datestring2tm((xmlChar *)string, *date);
10866 if (err) {
10867 if (err == IE_NOTSUP) {
10868 err = IE_ISDS;
10869 char *string_locale = _isds_utf82locale(string);
10870 isds_printf_message(context,
10871 _("Invalid dateInserted value: %s"), string_locale);
10872 free(string_locale);
10874 goto leave;
10878 leave:
10879 free(string);
10880 xmlXPathFreeObject(result);
10881 xmlXPathFreeContext(xpath_ctx);
10883 xmlFreeDoc(response);
10884 xmlFreeNode(request);
10886 if (!err) {
10887 char *id_locale = _isds_utf82locale((char *) *id);
10888 isds_log(ILF_ISDS, ILL_DEBUG,
10889 _("Document %s has been submitted for conversion "
10890 "to server successfully\n"), id_locale);
10891 free(id_locale);
10893 #else /* not HAVE_LIBCURL */
10894 err = IE_NOTSUP;
10895 #endif
10896 return err;
10900 /* Close possibly opened connection to Czech POINT document deposit.
10901 * @context is Czech POINT session context. */
10902 isds_error czp_close_connection(struct isds_ctx *context) {
10903 if (!context) return IE_INVALID_CONTEXT;
10904 zfree(context->long_message);
10905 #if HAVE_LIBCURL
10906 return czp_do_close_connection(context);
10907 #else
10908 return IE_NOTSUP;
10909 #endif
10913 /* Send request for new box creation in testing ISDS instance.
10914 * It's not possible to request for a production box currently, as it
10915 * communicates via e-mail.
10916 * XXX: This function does not work either. Server complains about invalid
10917 * e-mail address.
10918 * XXX: Remove context->type hacks in isds.c and validator.c when removing
10919 * this function
10920 * @context is special session context for box creation request. DO NOT use
10921 * standard context as it could reveal your password. Use fresh new context or
10922 * context previously used by this function.
10923 * @box is box description to create including single primary user (in case of
10924 * FO box type). It outputs box ID assigned by ISDS in dbID element.
10925 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
10926 * box, or contact address of PFO box owner). The email member is mandatory as
10927 * it will be used to deliver credentials.
10928 * @former_names is former name of box owner. Pass NULL if you don't care.
10929 * @approval is optional external approval of box manipulation
10930 * @refnumber is reallocated serial number of request assigned by ISDS. Use
10931 * NULL, if you don't care.*/
10932 isds_error isds_request_new_testing_box(struct isds_ctx *context,
10933 struct isds_DbOwnerInfo *box, const struct isds_list *users,
10934 const char *former_names, const struct isds_approval *approval,
10935 char **refnumber) {
10936 isds_error err = IE_SUCCESS;
10937 #if HAVE_LIBCURL
10938 xmlNodePtr request = NULL;
10939 xmlDocPtr response = NULL;
10940 xmlXPathContextPtr xpath_ctx = NULL;
10941 xmlXPathObjectPtr result = NULL;
10942 #endif
10945 if (!context) return IE_INVALID_CONTEXT;
10946 zfree(context->long_message);
10947 if (!box) return IE_INVAL;
10949 #if HAVE_LIBCURL
10950 if (!box->email || box->email[0] == '\0') {
10951 isds_log_message(context, _("E-mail field is mandatory"));
10952 return IE_INVAL;
10955 /* Scratch box ID */
10956 zfree(box->dbID);
10958 /* Store configuration */
10959 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
10960 free(context->url);
10961 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
10962 if (!(context->url))
10963 return IE_NOMEM;
10965 /* Prepare CURL handle if not yet connected */
10966 if (!context->curl) {
10967 context->curl = curl_easy_init();
10968 if (!(context->curl))
10969 return IE_ERROR;
10972 /* Build CreateDataBox request */
10973 err = build_CreateDBInput_request(context,
10974 &request, BAD_CAST "CreateDataBox",
10975 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
10976 if (err) goto leave;
10978 /* Send it to server and process response */
10979 err = send_destroy_request_check_response(context,
10980 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
10981 &response, (xmlChar **) refnumber, NULL);
10982 if (err) goto leave;
10984 /* Extract box ID */
10985 xpath_ctx = xmlXPathNewContext(response);
10986 if (!xpath_ctx) {
10987 err = IE_ERROR;
10988 goto leave;
10990 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10991 err = IE_ERROR;
10992 goto leave;
10994 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
10996 leave:
10997 xmlXPathFreeObject(result);
10998 xmlXPathFreeContext(xpath_ctx);
10999 xmlFreeDoc(response);
11000 xmlFreeNode(request);
11002 if (!err) {
11003 isds_log(ILF_ISDS, ILL_DEBUG,
11004 _("CreateDataBox request processed by server successfully.\n"));
11006 #else /* not HAVE_LIBCURL */
11007 err = IE_NOTSUP;
11008 #endif
11010 return err;
11014 /* Submit CMS signed message to ISDS to verify its originality. This is
11015 * stronger form of isds_verify_message_hash() because ISDS does more checks
11016 * than simple one (potentialy old weak) hash comparison.
11017 * @context is session context
11018 * @message is memory with raw CMS signed message bit stream
11019 * @length is @message size in bytes
11020 * @return
11021 * IE_SUCCESS if message originates in ISDS
11022 * IE_NOTEQUAL if message is unknown to ISDS
11023 * other code for other errors */
11024 isds_error isds_authenticate_message(struct isds_ctx *context,
11025 const void *message, size_t length) {
11026 isds_error err = IE_SUCCESS;
11027 #if HAVE_LIBCURL
11028 xmlNsPtr isds_ns = NULL;
11029 xmlNodePtr request = NULL;
11030 xmlDocPtr response = NULL;
11031 xmlXPathContextPtr xpath_ctx = NULL;
11032 xmlXPathObjectPtr result = NULL;
11033 _Bool *authentic = NULL;
11034 #endif
11036 if (!context) return IE_INVALID_CONTEXT;
11037 zfree(context->long_message);
11038 if (!message || length == 0) return IE_INVAL;
11040 #if HAVE_LIBCURL
11041 /* Check if connection is established
11042 * TODO: This check should be done downstairs. */
11043 if (!context->curl) return IE_CONNECTION_CLOSED;
11046 /* Build AuthenticateMessage request */
11047 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11048 if (!request) {
11049 isds_log_message(context,
11050 _("Could not build AuthenticateMessage request"));
11051 return IE_ERROR;
11053 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11054 if(!isds_ns) {
11055 isds_log_message(context, _("Could not create ISDS name space"));
11056 xmlFreeNode(request);
11057 return IE_ERROR;
11059 xmlSetNs(request, isds_ns);
11061 /* Insert Base64 encoded message */
11062 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11063 message, length);
11064 if (err) goto leave;
11066 /* Send request to server and process response */
11067 err = send_destroy_request_check_response(context,
11068 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11069 &response, NULL, NULL);
11070 if (err) goto leave;
11073 /* ISDS has decided */
11074 xpath_ctx = xmlXPathNewContext(response);
11075 if (!xpath_ctx) {
11076 err = IE_ERROR;
11077 goto leave;
11079 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11080 err = IE_ERROR;
11081 goto leave;
11084 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11086 if (!authentic) {
11087 isds_log_message(context,
11088 _("Server did not return any response on "
11089 "AuthenticateMessage request"));
11090 err = IE_ISDS;
11091 goto leave;
11093 if (*authentic) {
11094 isds_log(ILF_ISDS, ILL_DEBUG,
11095 _("ISDS authenticated the message successfully\n"));
11096 } else {
11097 isds_log_message(context, _("ISDS does not know the message"));
11098 err = IE_NOTEQUAL;
11102 leave:
11103 free(authentic);
11104 xmlXPathFreeObject(result);
11105 xmlXPathFreeContext(xpath_ctx);
11107 xmlFreeDoc(response);
11108 xmlFreeNode(request);
11109 #else /* not HAVE_LIBCURL */
11110 err = IE_NOTSUP;
11111 #endif
11113 return err;
11117 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11118 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11119 * be re-signed.
11120 * @context is session context
11121 * @input_data is memory with raw CMS signed message or delivery info bit
11122 * stream to re-sign
11123 * @input_length is @input_data size in bytes
11124 * @output_data is pointer to auto-allocated memory where to store re-signed
11125 * input data blob. Caller must free it.
11126 * @output_data is pointer where to store @output_data size in bytes
11127 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11128 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11129 * @return
11130 * IE_SUCCESS if CMS blob has been re-signed successfully
11131 * other code for other errors */
11132 isds_error isds_resign_message(struct isds_ctx *context,
11133 const void *input_data, size_t input_length,
11134 void **output_data, size_t *output_length, struct tm **valid_to) {
11135 isds_error err = IE_SUCCESS;
11136 #if HAVE_LIBCURL
11137 xmlNsPtr isds_ns = NULL;
11138 xmlNodePtr request = NULL;
11139 xmlDocPtr response = NULL;
11140 xmlXPathContextPtr xpath_ctx = NULL;
11141 xmlXPathObjectPtr result = NULL;
11142 char *string = NULL;
11143 const xmlChar *codes[] = {
11144 BAD_CAST "2200",
11145 BAD_CAST "2201",
11146 BAD_CAST "2204",
11147 BAD_CAST "2207",
11148 NULL
11150 const char *meanings[] = {
11151 "Message is bad",
11152 "Message is not original",
11153 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11154 "Time stamp could not been generated in time"
11156 const isds_error errors[] = {
11157 IE_INVAL,
11158 IE_NOTUNIQ,
11159 IE_INVAL,
11160 IE_ISDS,
11162 struct code_map_isds_error map = {
11163 .codes = codes,
11164 .meanings = meanings,
11165 .errors = errors
11167 #endif
11169 if (NULL != output_data) *output_data = NULL;
11170 if (NULL != output_length) *output_length = 0;
11171 if (NULL != valid_to) *valid_to = NULL;
11173 if (NULL == context) return IE_INVALID_CONTEXT;
11174 zfree(context->long_message);
11175 if (NULL == input_data || 0 == input_length) {
11176 isds_log_message(context, _("Empty CMS blob on input"));
11177 return IE_INVAL;
11179 if (NULL == output_data || NULL == output_length) {
11180 isds_log_message(context,
11181 _("NULL pointer provided for output CMS blob"));
11182 return IE_INVAL;
11185 #if HAVE_LIBCURL
11186 /* Check if connection is established
11187 * TODO: This check should be done downstairs. */
11188 if (!context->curl) return IE_CONNECTION_CLOSED;
11191 /* Build Re-signISDSDocument request */
11192 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11193 if (!request) {
11194 isds_log_message(context,
11195 _("Could not build Re-signISDSDocument request"));
11196 return IE_ERROR;
11198 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11199 if(!isds_ns) {
11200 isds_log_message(context, _("Could not create ISDS name space"));
11201 xmlFreeNode(request);
11202 return IE_ERROR;
11204 xmlSetNs(request, isds_ns);
11206 /* Insert Base64 encoded CMS blob */
11207 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11208 input_data, input_length);
11209 if (err) goto leave;
11211 /* Send request to server and process response */
11212 err = send_destroy_request_check_response(context,
11213 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11214 &response, NULL, &map);
11215 if (err) goto leave;
11218 /* Extract re-signed data */
11219 xpath_ctx = xmlXPathNewContext(response);
11220 if (!xpath_ctx) {
11221 err = IE_ERROR;
11222 goto leave;
11224 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11225 err = IE_ERROR;
11226 goto leave;
11228 result = xmlXPathEvalExpression(
11229 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11230 if (!result) {
11231 err = IE_ERROR;
11232 goto leave;
11234 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11235 isds_log_message(context,
11236 _("Missing Re-signISDSDocumentResponse element"));
11237 err = IE_ISDS;
11238 goto leave;
11240 if (result->nodesetval->nodeNr > 1) {
11241 isds_log_message(context,
11242 _("Multiple Re-signISDSDocumentResponse element"));
11243 err = IE_ISDS;
11244 goto leave;
11246 xpath_ctx->node = result->nodesetval->nodeTab[0];
11247 xmlXPathFreeObject(result); result = NULL;
11249 EXTRACT_STRING("isds:dmResultDoc", string);
11250 /* Decode non-empty data */
11251 if (NULL != string && string[0] != '\0') {
11252 *output_length = _isds_b64decode(string, output_data);
11253 if (*output_length == (size_t) -1) {
11254 isds_log_message(context,
11255 _("Error while Base64-decoding re-signed data"));
11256 err = IE_ERROR;
11257 goto leave;
11259 } else {
11260 isds_log_message(context, _("Server did not send re-signed data"));
11261 err = IE_ISDS;
11262 goto leave;
11264 zfree(string);
11266 if (NULL != valid_to) {
11267 /* Get time stamp expiration date */
11268 EXTRACT_STRING("isds:dmValidTo", string);
11269 if (NULL != string) {
11270 *valid_to = calloc(1, sizeof(**valid_to));
11271 if (!*valid_to) {
11272 err = IE_NOMEM;
11273 goto leave;
11275 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11276 if (err) {
11277 if (err == IE_NOTSUP) {
11278 err = IE_ISDS;
11279 char *string_locale = _isds_utf82locale(string);
11280 isds_printf_message(context,
11281 _("Invalid dmValidTo value: %s"), string_locale);
11282 free(string_locale);
11284 goto leave;
11289 leave:
11290 free(string);
11292 xmlXPathFreeObject(result);
11293 xmlXPathFreeContext(xpath_ctx);
11295 xmlFreeDoc(response);
11296 xmlFreeNode(request);
11297 #else /* not HAVE_LIBCURL */
11298 err = IE_NOTSUP;
11299 #endif
11301 return err;
11304 #undef INSERT_ELEMENT
11305 #undef CHECK_FOR_STRING_LENGTH
11306 #undef INSERT_STRING_ATTRIBUTE
11307 #undef INSERT_ULONGINTNOPTR
11308 #undef INSERT_ULONGINT
11309 #undef INSERT_LONGINT
11310 #undef INSERT_BOOLEAN
11311 #undef INSERT_SCALAR_BOOLEAN
11312 #undef INSERT_STRING
11313 #undef INSERT_STRING_WITH_NS
11314 #undef EXTRACT_STRING_ATTRIBUTE
11315 #undef EXTRACT_ULONGINT
11316 #undef EXTRACT_LONGINT
11317 #undef EXTRACT_BOOLEAN
11318 #undef EXTRACT_STRING
11321 /* Compute hash of message from raw representation and store it into envelope.
11322 * Original hash structure will be destroyed in envelope.
11323 * @context is session context
11324 * @message is message carrying raw XML message blob
11325 * @algorithm is desired hash algorithm to use */
11326 isds_error isds_compute_message_hash(struct isds_ctx *context,
11327 struct isds_message *message, const isds_hash_algorithm algorithm) {
11328 isds_error err = IE_SUCCESS;
11329 const char *nsuri;
11330 void *xml_stream = NULL;
11331 size_t xml_stream_length;
11332 size_t phys_start, phys_end;
11333 char *phys_path = NULL;
11334 struct isds_hash *new_hash = NULL;
11337 if (!context) return IE_INVALID_CONTEXT;
11338 zfree(context->long_message);
11339 if (!message) return IE_INVAL;
11341 if (!message->raw) {
11342 isds_log_message(context,
11343 _("Message does not carry raw representation"));
11344 return IE_INVAL;
11347 switch (message->raw_type) {
11348 case RAWTYPE_INCOMING_MESSAGE:
11349 nsuri = ISDS_NS;
11350 xml_stream = message->raw;
11351 xml_stream_length = message->raw_length;
11352 break;
11354 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11355 nsuri = SISDS_INCOMING_NS;
11356 xml_stream = message->raw;
11357 xml_stream_length = message->raw_length;
11358 break;
11360 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11361 nsuri = SISDS_INCOMING_NS;
11362 err = _isds_extract_cms_data(context,
11363 message->raw, message->raw_length,
11364 &xml_stream, &xml_stream_length);
11365 if (err) goto leave;
11366 break;
11368 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11369 nsuri = SISDS_OUTGOING_NS;
11370 xml_stream = message->raw;
11371 xml_stream_length = message->raw_length;
11372 break;
11374 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11375 nsuri = SISDS_OUTGOING_NS;
11376 err = _isds_extract_cms_data(context,
11377 message->raw, message->raw_length,
11378 &xml_stream, &xml_stream_length);
11379 if (err) goto leave;
11380 break;
11382 default:
11383 isds_log_message(context, _("Bad raw representation type"));
11384 return IE_INVAL;
11385 break;
11389 /* XXX: Hash is computed from original string representing isds:dmDm
11390 * subtree. That means no encoding, white space, xmlns attributes changes.
11391 * In other words, input for hash can be invalid XML stream. */
11392 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
11393 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11394 PHYSXML_ELEMENT_SEPARATOR,
11395 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
11396 PHYSXML_ELEMENT_SEPARATOR
11397 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
11398 err = IE_NOMEM;
11399 goto leave;
11401 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11402 phys_path, &phys_start, &phys_end);
11403 zfree(phys_path);
11404 if (err) {
11405 isds_log_message(context,
11406 _("Substring with isds:dmDM element could not be located "
11407 "in raw message"));
11408 goto leave;
11412 /* Compute hash */
11413 new_hash = calloc(1, sizeof(*new_hash));
11414 if (!new_hash) {
11415 err = IE_NOMEM;
11416 goto leave;
11418 new_hash->algorithm = algorithm;
11419 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
11420 new_hash);
11421 if (err) {
11422 isds_log_message(context, _("Could not compute message hash"));
11423 goto leave;
11426 /* Save computed hash */
11427 if (!message->envelope) {
11428 message->envelope = calloc(1, sizeof(*message->envelope));
11429 if (!message->envelope) {
11430 err = IE_NOMEM;
11431 goto leave;
11434 isds_hash_free(&message->envelope->hash);
11435 message->envelope->hash = new_hash;
11437 leave:
11438 if (err) {
11439 isds_hash_free(&new_hash);
11442 free(phys_path);
11443 if (xml_stream != message->raw) free(xml_stream);
11444 return err;
11448 /* Compare two hashes.
11449 * @h1 is first hash
11450 * @h2 is another hash
11451 * @return
11452 * IE_SUCCESS if hashes equal
11453 * IE_NOTUNIQ if hashes are comparable, but they don't equal
11454 * IE_ENUM if not comparable, but both structures defined
11455 * IE_INVAL if some of the structures are undefined (NULL)
11456 * IE_ERROR if internal error occurs */
11457 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
11458 if (h1 == NULL || h2 == NULL) return IE_INVAL;
11459 if (h1->algorithm != h2->algorithm) return IE_ENUM;
11460 if (h1->length != h2->length) return IE_ERROR;
11461 if (h1->length > 0 && !h1->value) return IE_ERROR;
11462 if (h2->length > 0 && !h2->value) return IE_ERROR;
11464 for (int i = 0; i < h1->length; i++) {
11465 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
11466 return IE_NOTEQUAL;
11468 return IE_SUCCESS;
11472 /* Check message has gone through ISDS by comparing message hash stored in
11473 * ISDS and locally computed hash. You must provide message with valid raw
11474 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
11475 * This is convenient wrapper for isds_download_message_hash(),
11476 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
11477 * @context is session context
11478 * @message is message with valid raw and envelope member; envelope->hash
11479 * member will be changed during function run. Use envelope on heap only.
11480 * @return
11481 * IE_SUCCESS if message originates in ISDS
11482 * IE_NOTEQUAL if message is unknown to ISDS
11483 * other code for other errors */
11484 isds_error isds_verify_message_hash(struct isds_ctx *context,
11485 struct isds_message *message) {
11486 isds_error err = IE_SUCCESS;
11487 struct isds_hash *downloaded_hash = NULL;
11489 if (!context) return IE_INVALID_CONTEXT;
11490 zfree(context->long_message);
11491 if (!message) return IE_INVAL;
11493 if (!message->envelope) {
11494 isds_log_message(context,
11495 _("Given message structure is missing envelope"));
11496 return IE_INVAL;
11498 if (!message->raw) {
11499 isds_log_message(context,
11500 _("Given message structure is missing raw representation"));
11501 return IE_INVAL;
11504 err = isds_download_message_hash(context, message->envelope->dmID,
11505 &downloaded_hash);
11506 if (err) goto leave;
11508 err = isds_compute_message_hash(context, message,
11509 downloaded_hash->algorithm);
11510 if (err) goto leave;
11512 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
11514 leave:
11515 isds_hash_free(&downloaded_hash);
11516 return err;
11520 /* Search for document by document ID in list of documents. IDs are compared
11521 * as UTF-8 string.
11522 * @documents is list of isds_documents
11523 * @id is document identifier
11524 * @return first matching document or NULL. */
11525 const struct isds_document *isds_find_document_by_id(
11526 const struct isds_list *documents, const char *id) {
11527 const struct isds_list *item;
11528 const struct isds_document *document;
11530 for (item = documents; item; item = item->next) {
11531 document = (struct isds_document *) item->data;
11532 if (!document) continue;
11534 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
11535 return document;
11538 return NULL;
11542 /* Normalize @mime_type to be proper MIME type.
11543 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
11544 * guess regular MIME type (e.g. "application/pdf").
11545 * @mime_type is UTF-8 encoded MIME type to fix
11546 * @return original @mime_type if no better interpretation exists, or
11547 * constant static UTF-8 encoded string with proper MIME type. */
11548 const char *isds_normalize_mime_type(const char *mime_type) {
11549 if (!mime_type) return NULL;
11551 for (int offset = 0;
11552 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
11553 offset += 2) {
11554 if (!xmlStrcasecmp((const xmlChar*) mime_type,
11555 extension_map_mime[offset]))
11556 return (const char *) extension_map_mime[offset + 1];
11559 return mime_type;
11563 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
11564 struct isds_message **message);
11565 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
11566 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
11567 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
11568 struct isds_address **address);
11570 int isds_message_free(struct isds_message **message);
11571 int isds_address_free(struct isds_address **address);
11575 /* Makes known all relevant namespaces to given XPath context
11576 * @xpath_ctx is XPath context
11577 * @message_ns selects proper message name space. Unsigned and signed
11578 * messages and delivery info's differ in prefix and URI. */
11579 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
11580 const message_ns_type message_ns) {
11581 const xmlChar *message_namespace = NULL;
11583 if (!xpath_ctx) return IE_ERROR;
11585 switch(message_ns) {
11586 case MESSAGE_NS_1:
11587 message_namespace = BAD_CAST ISDS1_NS; break;
11588 case MESSAGE_NS_UNSIGNED:
11589 message_namespace = BAD_CAST ISDS_NS; break;
11590 case MESSAGE_NS_SIGNED_INCOMING:
11591 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
11592 case MESSAGE_NS_SIGNED_OUTGOING:
11593 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
11594 case MESSAGE_NS_SIGNED_DELIVERY:
11595 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
11596 default:
11597 return IE_ENUM;
11600 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
11601 return IE_ERROR;
11602 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
11603 return IE_ERROR;
11604 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
11605 return IE_ERROR;
11606 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
11607 return IE_ERROR;
11608 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
11609 return IE_ERROR;
11610 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
11611 return IE_ERROR;
11612 return IE_SUCCESS;