Removed global variable definition from header file.
[libisds.git] / src / isds.c
blob4314208e365623466b2c95b53557c21e415e4bcc
1 #include "isds_priv.h"
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include <stdint.h>
8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
9 #include "utils.h"
10 #if HAVE_LIBCURL
11 #include "soap.h"
12 #endif
13 #include "validator.h"
14 #include "crypto.h"
15 #include <gpg-error.h> /* Because of ksba or gpgme */
16 #include "physxml.h"
17 #include "system.h"
19 /* Global variables.
20 * Allocated in isds_init() and deallocated in isds_cleanup(). */
21 unsigned int log_facilities;
22 isds_log_level log_level;
23 isds_log_callback log_callback;
24 void *log_callback_data;
25 const char *version_gpgme;
26 const char *version_gcrypt;
27 const char *version_expat;
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 /* Initialize gpg-error because of gpgme and ksba */
694 if (gpg_err_init()) {
695 isds_log(ILF_ISDS, ILL_CRIT,
696 _("gpg-error library initialization failed\n"));
697 return IE_ERROR;
700 /* Initialize GPGME */
701 if (_isds_init_gpgme(&version_gpgme)) {
702 isds_log(ILF_ISDS, ILL_CRIT,
703 _("GPGME library initialization failed\n"));
704 return IE_ERROR;
707 /* Initialize gcrypt */
708 if (_isds_init_gcrypt(&version_gcrypt)) {
709 isds_log(ILF_ISDS, ILL_CRIT,
710 _("gcrypt library initialization failed\n"));
711 return IE_ERROR;
714 /* This can _exit() current program. Find not so assertive check. */
715 LIBXML_TEST_VERSION;
716 xmlSetGenericErrorFunc(NULL, log_xml);
718 /* Check expat */
719 if (_isds_init_expat(&version_expat)) {
720 isds_log(ILF_ISDS, ILL_CRIT,
721 _("expat library initialization failed\n"));
722 return IE_ERROR;
725 /* Allocate global variables */
728 return IE_SUCCESS;
732 /* Deinitialize ISDS library.
733 * Global function, must be called as last library function. */
734 isds_error isds_cleanup(void) {
735 /* XML */
736 xmlCleanupParser();
738 #if HAVE_LIBCURL
739 /* Curl */
740 curl_global_cleanup();
741 #endif
743 return IE_SUCCESS;
747 /* Return version string of this library. Version of dependencies can be
748 * embedded. Do no try to parse it. You must free it. */
749 char *isds_version(void) {
750 char *buffer = NULL;
752 isds_asprintf(&buffer,
753 #if HAVE_LIBCURL
754 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
755 #else
756 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
757 #endif
758 PACKAGE_VERSION,
759 #if HAVE_LIBCURL
760 curl_version(),
761 #endif
762 version_gpgme, version_gcrypt,
763 version_expat, xmlParserVersion);
764 return buffer;
768 /* Return text description of ISDS error */
769 const char *isds_strerror(const isds_error error) {
770 switch (error) {
771 case IE_SUCCESS:
772 return(_("Success")); break;
773 case IE_ERROR:
774 return(_("Unspecified error")); break;
775 case IE_NOTSUP:
776 return(_("Not supported")); break;
777 case IE_INVAL:
778 return(_("Invalid value")); break;
779 case IE_INVALID_CONTEXT:
780 return(_("Invalid context")); break;
781 case IE_NOT_LOGGED_IN:
782 return(_("Not logged in")); break;
783 case IE_CONNECTION_CLOSED:
784 return(_("Connection closed")); break;
785 case IE_TIMED_OUT:
786 return(_("Timed out")); break;
787 case IE_NOEXIST:
788 return(_("Not exist")); break;
789 case IE_NOMEM:
790 return(_("Out of memory")); break;
791 case IE_NETWORK:
792 return(_("Network problem")); break;
793 case IE_HTTP:
794 return(_("HTTP problem")); break;
795 case IE_SOAP:
796 return(_("SOAP problem")); break;
797 case IE_XML:
798 return(_("XML problem")); break;
799 case IE_ISDS:
800 return(_("ISDS server problem")); break;
801 case IE_ENUM:
802 return(_("Invalid enum value")); break;
803 case IE_DATE:
804 return(_("Invalid date value")); break;
805 case IE_2BIG:
806 return(_("Too big")); break;
807 case IE_2SMALL:
808 return(_("Too small")); break;
809 case IE_NOTUNIQ:
810 return(_("Value not unique")); break;
811 case IE_NOTEQUAL:
812 return(_("Values not equal")); break;
813 case IE_PARTIAL_SUCCESS:
814 return(_("Some suboperations failed")); break;
815 case IE_ABORTED:
816 return(_("Operation aborted")); break;
817 case IE_SECURITY:
818 return(_("Security problem")); break;
819 default:
820 return(_("Unknown error"));
825 /* Create ISDS context.
826 * Each context can be used for different sessions to (possibly) different
827 * ISDS server with different credentials. */
828 struct isds_ctx *isds_ctx_create(void) {
829 struct isds_ctx *context;
830 context = malloc(sizeof(*context));
831 if (context) memset(context, 0, sizeof(*context));
832 return context;
835 #if HAVE_LIBCURL
836 /* Close possibly opened connection to Czech POINT document deposit without
837 * resetting long_message buffer.
838 * XXX: Do not use czp_close_connection() if you do not want to destroy log
839 * message.
840 * @context is Czech POINT session context. */
841 static isds_error czp_do_close_connection(struct isds_ctx *context) {
842 if (!context) return IE_INVALID_CONTEXT;
843 _isds_close_connection(context);
844 return IE_SUCCESS;
848 /* Discard credentials.
849 * @context is ISDS context
850 * @discard_saved_username is true for removing saved username, false for
851 * keeping it.
852 * Only that. It does not cause log out, connection close or similar. */
853 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context,
854 _Bool discard_saved_username) {
855 if(!context) return IE_INVALID_CONTEXT;
857 if (context->username) {
858 memset(context->username, 0, strlen(context->username));
859 zfree(context->username);
861 if (context->password) {
862 memset(context->password, 0, strlen(context->password));
863 zfree(context->password);
865 isds_pki_credentials_free(&context->pki_credentials);
866 if (discard_saved_username && context->saved_username) {
867 memset(context->saved_username, 0, strlen(context->saved_username));
868 zfree(context->saved_username);
871 return IE_SUCCESS;
873 #endif /* HAVE_LIBCURL */
876 /* Destroy ISDS context and free memory.
877 * @context will be NULLed on success. */
878 isds_error isds_ctx_free(struct isds_ctx **context) {
879 if (!context || !*context) {
880 return IE_INVALID_CONTEXT;
883 #if HAVE_LIBCURL
884 /* Discard credentials and close connection */
885 switch ((*context)->type) {
886 case CTX_TYPE_NONE: break;
887 case CTX_TYPE_ISDS: isds_logout(*context); break;
888 case CTX_TYPE_CZP:
889 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
890 czp_do_close_connection(*context); break;
893 /* For sure */
894 _isds_discard_credentials(*context, 1);
896 /* Free other structures */
897 free((*context)->tls_verify_server);
898 free((*context)->tls_ca_file);
899 free((*context)->tls_ca_dir);
900 free((*context)->tls_crl_file);
901 #endif /* HAVE_LIBCURL */
902 free((*context)->long_message);
904 free(*context);
905 *context = NULL;
906 return IE_SUCCESS;
910 /* Return long message text produced by library function, e.g. detailed error
911 * message. Returned pointer is only valid until new library function is
912 * called for the same context. Could be NULL, especially if NULL context is
913 * supplied. Return string is locale encoded. */
914 char *isds_long_message(const struct isds_ctx *context) {
915 if (!context) return NULL;
916 return context->long_message;
920 /* Stores message into context' long_message buffer.
921 * Application can pick the message up using isds_long_message().
922 * NULL @message truncates the buffer but does not deallocate it.
923 * @message is coded in locale encoding */
924 _hidden isds_error isds_log_message(struct isds_ctx *context,
925 const char *message) {
926 char *buffer;
927 size_t length;
929 if (!context) return IE_INVALID_CONTEXT;
931 /* FIXME: Check for integer overflow */
932 length = 1 + ((message) ? strlen(message) : 0);
933 buffer = realloc(context->long_message, length);
934 if (!buffer) return IE_NOMEM;
936 if (message)
937 strcpy(buffer, message);
938 else
939 *buffer = '\0';
941 context->long_message = buffer;
942 return IE_SUCCESS;
946 /* Appends message into context' long_message buffer.
947 * Application can pick the message up using isds_long_message().
948 * NULL message has void effect. */
949 _hidden isds_error isds_append_message(struct isds_ctx *context,
950 const char *message) {
951 char *buffer;
952 size_t old_length, length;
954 if (!context) return IE_INVALID_CONTEXT;
955 if (!message) return IE_SUCCESS;
956 if (!context->long_message)
957 return isds_log_message(context, message);
959 old_length = strlen(context->long_message);
960 /* FIXME: Check for integer overflow */
961 length = 1 + old_length + strlen(message);
962 buffer = realloc(context->long_message, length);
963 if (!buffer) return IE_NOMEM;
965 strcpy(buffer + old_length, message);
967 context->long_message = buffer;
968 return IE_SUCCESS;
972 /* Stores formatted message into context' long_message buffer.
973 * Application can pick the message up using isds_long_message(). */
974 _hidden isds_error isds_printf_message(struct isds_ctx *context,
975 const char *format, ...) {
976 va_list ap;
977 int length;
979 if (!context) return IE_INVALID_CONTEXT;
980 va_start(ap, format);
981 length = isds_vasprintf(&(context->long_message), format, ap);
982 va_end(ap);
984 return (length < 0) ? IE_ERROR: IE_SUCCESS;
988 /* Set logging up.
989 * @facilities is bit mask of isds_log_facility values,
990 * @level is verbosity level. */
991 void isds_set_logging(const unsigned int facilities,
992 const isds_log_level level) {
993 log_facilities = facilities;
994 log_level = level;
998 /* Register callback function libisds calls when new global log message is
999 * produced by library. Library logs to stderr by default.
1000 * @callback is function provided by application libisds will call. See type
1001 * definition for @callback argument explanation. Pass NULL to revert logging to
1002 * default behaviour.
1003 * @data is application specific data @callback gets as last argument */
1004 void isds_set_log_callback(isds_log_callback callback, void *data) {
1005 log_callback = callback;
1006 log_callback_data = data;
1010 /* Log @message in class @facility with log @level into global log. @message
1011 * is printf(3) formatting string, variadic arguments may be necessary.
1012 * For debugging purposes. */
1013 _hidden isds_error isds_log(const isds_log_facility facility,
1014 const isds_log_level level, const char *message, ...) {
1015 va_list ap;
1016 char *buffer = NULL;
1017 int length;
1019 if (level > log_level) return IE_SUCCESS;
1020 if (!(log_facilities & facility)) return IE_SUCCESS;
1021 if (!message) return IE_INVAL;
1023 if (log_callback) {
1024 /* Pass message to application supplied callback function */
1025 va_start(ap, message);
1026 length = isds_vasprintf(&buffer, message, ap);
1027 va_end(ap);
1029 if (length == -1) {
1030 return IE_ERROR;
1032 if (length > 0) {
1033 log_callback(facility, level, buffer, length, log_callback_data);
1035 free(buffer);
1036 } else {
1037 /* Default: Log it to stderr */
1038 va_start(ap, message);
1039 vfprintf(stderr, message, ap);
1040 va_end(ap);
1041 /* Line buffered printf is default.
1042 * fflush(stderr);*/
1045 return IE_SUCCESS;
1049 /* Set timeout in milliseconds for each network job like connecting to server
1050 * or sending message. Use 0 to disable timeout limits. */
1051 isds_error isds_set_timeout(struct isds_ctx *context,
1052 const unsigned int timeout) {
1053 if (!context) return IE_INVALID_CONTEXT;
1054 zfree(context->long_message);
1056 #if HAVE_LIBCURL
1057 context->timeout = timeout;
1059 if (context->curl) {
1060 CURLcode curl_err;
1062 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
1063 if (!curl_err)
1064 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1065 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
1066 context->timeout);
1067 #else
1068 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
1069 context->timeout / 1000);
1070 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1071 if (curl_err) return IE_ERROR;
1074 return IE_SUCCESS;
1075 #else /* not HAVE_LIBCURL */
1076 return IE_NOTSUP;
1077 #endif
1081 /* Register callback function libisds calls periodically during HTTP data
1082 * transfer.
1083 * @context is session context
1084 * @callback is function provided by application libisds will call. See type
1085 * definition for @callback argument explanation.
1086 * @data is application specific data @callback gets as last argument */
1087 isds_error isds_set_progress_callback(struct isds_ctx *context,
1088 isds_progress_callback callback, void *data) {
1089 if (!context) return IE_INVALID_CONTEXT;
1090 zfree(context->long_message);
1092 #if HAVE_LIBCURL
1093 context->progress_callback = callback;
1094 context->progress_callback_data = data;
1096 return IE_SUCCESS;
1097 #else /* not HAVE_LIBCURL */
1098 return IE_NOTSUP;
1099 #endif
1103 /* Change context settings.
1104 * @context is context which setting will be applied to
1105 * @option is name of option. It determines the type of last argument. See
1106 * isds_option definition for more info.
1107 * @... is value of new setting. Type is determined by @option
1108 * */
1109 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1110 ...) {
1111 isds_error err = IE_SUCCESS;
1112 va_list ap;
1113 #if HAVE_LIBCURL
1114 char *pointer, *string;
1115 #endif
1117 if (!context) return IE_INVALID_CONTEXT;
1118 zfree(context->long_message);
1120 va_start(ap, option);
1122 #define REPLACE_VA_BOOLEAN(destination) { \
1123 if (!(destination)) { \
1124 (destination) = malloc(sizeof(*(destination))); \
1125 if (!(destination)) { \
1126 err = IE_NOMEM; goto leave; \
1129 *(destination) = (_Bool) !!va_arg(ap, int); \
1132 #define REPLACE_VA_STRING(destination) { \
1133 string = va_arg(ap, char *); \
1134 if (string) { \
1135 pointer = realloc((destination), 1 + strlen(string)); \
1136 if (!pointer) { err = IE_NOMEM; goto leave; } \
1137 strcpy(pointer, string); \
1138 (destination) = pointer; \
1139 } else { \
1140 free(destination); \
1141 (destination) = NULL; \
1145 switch (option) {
1146 case IOPT_TLS_VERIFY_SERVER:
1147 #if HAVE_LIBCURL
1148 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1149 #else
1150 err = IE_NOTSUP; goto leave;
1151 #endif
1152 break;
1153 case IOPT_TLS_CA_FILE:
1154 #if HAVE_LIBCURL
1155 REPLACE_VA_STRING(context->tls_ca_file);
1156 #else
1157 err = IE_NOTSUP; goto leave;
1158 #endif
1159 break;
1160 case IOPT_TLS_CA_DIRECTORY:
1161 #if HAVE_LIBCURL
1162 REPLACE_VA_STRING(context->tls_ca_dir);
1163 #else
1164 err = IE_NOTSUP; goto leave;
1165 #endif
1166 break;
1167 case IOPT_TLS_CRL_FILE:
1168 #if HAVE_LIBCURL
1169 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1170 REPLACE_VA_STRING(context->tls_crl_file);
1171 #else
1172 isds_log_message(context,
1173 _("Curl library does not support CRL definition"));
1174 err = IE_NOTSUP;
1175 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1176 #else
1177 err = IE_NOTSUP; goto leave;
1178 #endif /* not HAVE_LIBCURL */
1179 break;
1180 case IOPT_NORMALIZE_MIME_TYPE:
1181 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1182 break;
1184 default:
1185 err = IE_ENUM; goto leave;
1188 #undef REPLACE_VA_STRING
1189 #undef REPLACE_VA_BOOLEAN
1191 leave:
1192 va_end(ap);
1193 return err;
1197 #if HAVE_LIBCURL
1198 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1199 * Destination for NULL argument will not be touched.
1200 * Destination pointers must be freed before calling this function.
1201 * If @username is @context->saved_username, the saved_username will not be
1202 * replaced. The saved_username is clobbered only if context has set otp
1203 * member.
1204 * Return IE_SUCCESS on success. */
1205 static isds_error _isds_store_credentials(struct isds_ctx *context,
1206 const char *username, const char *password,
1207 const struct isds_pki_credentials *pki_credentials) {
1208 if (NULL == context) return IE_INVALID_CONTEXT;
1210 /* FIXME: mlock password
1211 * (I have a library) */
1213 if (username) {
1214 context->username = strdup(username);
1215 if (context->otp && context->saved_username != username)
1216 context->saved_username = strdup(username);
1218 if (password) {
1219 if (NULL == context->otp_credentials)
1220 context->password = strdup(password);
1221 else
1222 context->password = _isds_astrcat(password,
1223 context->otp_credentials->otp_code);
1225 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1227 if ((NULL != username && NULL == context->username) ||
1228 (NULL != password && NULL == context->password) ||
1229 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1230 (context->otp && NULL != context->username &&
1231 NULL == context->saved_username)) {
1232 return IE_NOMEM;
1235 return IE_SUCCESS;
1237 #endif
1240 /* Connect and log into ISDS server.
1241 * All required arguments will be copied, you do not have to keep them after
1242 * that.
1243 * ISDS supports six different authentication methods. Exact method is
1244 * selected on @username, @password, @pki_credentials, and @otp arguments:
1245 * - If @pki_credentials == NULL, @username and @password must be supplied
1246 * and then
1247 * - If @otp == NULL, simple authentication by username and password will
1248 * be proceeded.
1249 * - If @otp != NULL, authentication by username and password and OTP
1250 * will be used.
1251 * - If @pki_credentials != NULL, then
1252 * - If @username == NULL, only certificate will be used
1253 * - If @username != NULL, then
1254 * - If @password == NULL, then certificate will be used and
1255 * @username shifts meaning to box ID. This is used for hosted
1256 * services.
1257 * - Otherwise all three arguments will be used.
1258 * Please note, that different cases require different certificate type
1259 * (system qualified one or commercial non qualified one). This library
1260 * does not check such political issues. Please see ISDS Specification
1261 * for more details.
1262 * @url is base address of ISDS web service. Pass extern isds_locator
1263 * variable to use production ISDS instance without client certificate
1264 * authentication (or extern isds_cert_locator with client certificate
1265 * authentication or extern isds_otp_locators with OTP authentication).
1266 * Passing NULL has the same effect, autoselection between isds_locator,
1267 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1268 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1269 * isds_otp_testing_locator) variable to select testing instance.
1270 * @username is user name of ISDS user or box ID
1271 * @password is user's secret password
1272 * @pki_credentials defines public key cryptographic material to use in client
1273 * authentication.
1274 * @otp selects one-time password authentication method to use, defines OTP
1275 * code (if known) and returns fine grade resolution of OTP procedure.
1276 * @return:
1277 * IE_SUCCESS if authentication succeeds
1278 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1279 * requested, fine grade reason will be set into @otp->resolution. Error
1280 * message from server can be obtained by isds_long_message() call.
1281 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1282 * server has sent OTP code through side channel. Application is expected to
1283 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1284 * this call to complete second phase of TOTP authentication;
1285 * or other appropriate error. */
1286 isds_error isds_login(struct isds_ctx *context, const char *url,
1287 const char *username, const char *password,
1288 const struct isds_pki_credentials *pki_credentials,
1289 struct isds_otp *otp) {
1290 #if HAVE_LIBCURL
1291 isds_error err = IE_NOT_LOGGED_IN;
1292 isds_error soap_err;
1293 xmlNsPtr isds_ns = NULL;
1294 xmlNodePtr request = NULL;
1295 xmlNodePtr response = NULL;
1296 #endif /* HAVE_LIBCURL */
1298 if (!context) return IE_INVALID_CONTEXT;
1299 zfree(context->long_message);
1301 #if HAVE_LIBCURL
1302 /* Close connection if already logged in */
1303 if (context->curl) {
1304 _isds_close_connection(context);
1307 /* Store configuration */
1308 context->type = CTX_TYPE_ISDS;
1309 zfree(context->url);
1311 /* Mangle base URI according to requested authentication method */
1312 if (NULL == pki_credentials) {
1313 isds_log(ILF_SEC, ILL_INFO,
1314 _("Selected authentication method: no certificate, "
1315 "username and password\n"));
1316 if (!username || !password) {
1317 isds_log_message(context,
1318 _("Both username and password must be supplied"));
1319 return IE_INVAL;
1321 context->otp_credentials = otp;
1322 context->otp = (NULL != context->otp_credentials);
1324 if (!context->otp) {
1325 /* Default locator is official system (without certificate or
1326 * OTP) */
1327 context->url = strdup((NULL != url) ? url : isds_locator);
1328 } else {
1329 const char *authenticator_uri = NULL;
1330 if (!url) url = isds_otp_locator;
1331 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1332 switch (context->otp_credentials->method) {
1333 case OTP_HMAC:
1334 isds_log(ILF_SEC, ILL_INFO,
1335 _("Selected authentication method: "
1336 "HMAC-based one-time password\n"));
1337 authenticator_uri =
1338 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1339 break;
1340 case OTP_TIME:
1341 isds_log(ILF_SEC, ILL_INFO,
1342 _("Selected authentication method: "
1343 "Time-based one-time password\n"));
1344 if (context->otp_credentials->otp_code == NULL) {
1345 isds_log(ILF_SEC, ILL_INFO,
1346 _("OTP code has not been provided by "
1347 "application, requesting server for "
1348 "new one.\n"));
1349 authenticator_uri =
1350 "%1$sas/processLogin?type=totp&sendSms=true&"
1351 "uri=%1$sapps/";
1352 } else {
1353 isds_log(ILF_SEC, ILL_INFO,
1354 _("OTP code has been provided by "
1355 "application, not requesting server "
1356 "for new one.\n"));
1357 authenticator_uri =
1358 "%1$sas/processLogin?type=totp&"
1359 "uri=%1$sapps/";
1361 break;
1362 default:
1363 isds_log_message(context,
1364 _("Unknown one-time password authentication "
1365 "method requested by application"));
1366 return IE_ENUM;
1368 if (-1 == isds_asprintf(&context->url, authenticator_uri, url))
1369 return IE_NOMEM;
1371 } else {
1372 /* Default locator is official system (with client certificate) */
1373 context->otp = 0;
1374 context->otp_credentials = NULL;
1375 if (!url) url = isds_cert_locator;
1377 if (!username) {
1378 isds_log(ILF_SEC, ILL_INFO,
1379 _("Selected authentication method: system certificate, "
1380 "no username and no password\n"));
1381 password = NULL;
1382 context->url = _isds_astrcat(url, "cert/");
1383 } else {
1384 if (!password) {
1385 isds_log(ILF_SEC, ILL_INFO,
1386 _("Selected authentication method: system certificate, "
1387 "box ID and no password\n"));
1388 context->url = _isds_astrcat(url, "hspis/");
1389 } else {
1390 isds_log(ILF_SEC, ILL_INFO,
1391 _("Selected authentication method: commercial "
1392 "certificate, username and password\n"));
1393 context->url = _isds_astrcat(url, "certds/");
1397 if (!(context->url))
1398 return IE_NOMEM;
1400 /* Prepare CURL handle */
1401 context->curl = curl_easy_init();
1402 if (!(context->curl))
1403 return IE_ERROR;
1405 /* Build log-in request */
1406 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1407 if (!request) {
1408 isds_log_message(context, _("Could not build ISDS log-in request"));
1409 return IE_ERROR;
1411 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1412 if(!isds_ns) {
1413 isds_log_message(context, _("Could not create ISDS name space"));
1414 xmlFreeNode(request);
1415 return IE_ERROR;
1417 xmlSetNs(request, isds_ns);
1419 /* Store credentials */
1420 _isds_discard_credentials(context, 1);
1421 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1422 _isds_discard_credentials(context, 1);
1423 xmlFreeNode(request);
1424 return IE_NOMEM;
1427 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1428 username, url);
1430 /* Send log-in request */
1431 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1433 if (context->otp) {
1434 /* Revert context URL from OTP authentication service URL to OTP web
1435 * service base URL for subsequent calls. Potenial isds_login() retry
1436 * will re-set context URL again. */
1437 zfree(context->url);
1438 context->url = _isds_astrcat(url, "apps/");
1439 if (context->url == NULL) {
1440 soap_err = IE_NOMEM;
1442 /* Detach pointer to OTP credentials from context */
1443 context->otp_credentials = NULL;
1446 /* Remove credentials */
1447 _isds_discard_credentials(context, 0);
1449 /* Destroy log-in request */
1450 xmlFreeNode(request);
1452 if (soap_err) {
1453 xmlFreeNodeList(response);
1454 _isds_close_connection(context);
1455 return soap_err;
1458 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1459 * authentication succeeded if soap_err == IE_SUCCESS */
1460 err = IE_SUCCESS;
1462 xmlFreeNodeList(response);
1464 if (!err)
1465 isds_log(ILF_ISDS, ILL_DEBUG,
1466 _("User %s has been logged into server %s successfully\n"),
1467 username, url);
1468 return err;
1469 #else /* not HAVE_LIBCURL */
1470 return IE_NOTSUP;
1471 #endif
1475 /* Log out from ISDS server discards credentials and connection configuration. */
1476 isds_error isds_logout(struct isds_ctx *context) {
1477 if (!context) return IE_INVALID_CONTEXT;
1478 zfree(context->long_message);
1480 #if HAVE_LIBCURL
1481 if (context->curl) {
1482 if (context->otp) {
1483 isds_error err = _isds_invalidate_otp_cookie(context);
1484 if (err) return err;
1487 /* Close connection */
1488 _isds_close_connection(context);
1490 /* Discard credentials for sure. They should not survive isds_login(),
1491 * even successful .*/
1492 _isds_discard_credentials(context, 1);
1493 zfree(context->url);
1495 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1496 } else {
1497 _isds_discard_credentials(context, 1);
1499 return IE_SUCCESS;
1500 #else /* not HAVE_LIBCURL */
1501 return IE_NOTSUP;
1502 #endif
1506 /* Verify connection to ISDS is alive and server is responding.
1507 * Send dummy request to ISDS and expect dummy response. */
1508 isds_error isds_ping(struct isds_ctx *context) {
1509 #if HAVE_LIBCURL
1510 isds_error soap_err;
1511 xmlNsPtr isds_ns = NULL;
1512 xmlNodePtr request = NULL;
1513 xmlNodePtr response = NULL;
1514 #endif /* HAVE_LIBCURL */
1516 if (!context) return IE_INVALID_CONTEXT;
1517 zfree(context->long_message);
1519 #if HAVE_LIBCURL
1520 /* Check if connection is established */
1521 if (!context->curl) return IE_CONNECTION_CLOSED;
1524 /* Build dummy request */
1525 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1526 if (!request) {
1527 isds_log_message(context, _("Could build ISDS dummy request"));
1528 return IE_ERROR;
1530 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1531 if(!isds_ns) {
1532 isds_log_message(context, _("Could not create ISDS name space"));
1533 xmlFreeNode(request);
1534 return IE_ERROR;
1536 xmlSetNs(request, isds_ns);
1538 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1540 /* Sent dummy request */
1541 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1543 /* Destroy log-in request */
1544 xmlFreeNode(request);
1546 if (soap_err) {
1547 isds_log(ILF_ISDS, ILL_DEBUG,
1548 _("ISDS server could not be contacted\n"));
1549 xmlFreeNodeList(response);
1550 return soap_err;
1553 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1554 * authentication succeeded if soap_err == IE_SUCCESS */
1555 /* TODO: ISDS documentation does not specify response body.
1556 * However real server sends back DummyOperationResponse */
1559 xmlFreeNodeList(response);
1561 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1563 return IE_SUCCESS;
1564 #else /* not HAVE_LIBCURL */
1565 return IE_NOTSUP;
1566 #endif
1570 /* Send bogus request to ISDS.
1571 * Just for test purposes */
1572 isds_error isds_bogus_request(struct isds_ctx *context) {
1573 #if HAVE_LIBCURL
1574 isds_error err;
1575 xmlNsPtr isds_ns = NULL;
1576 xmlNodePtr request = NULL;
1577 xmlDocPtr response = NULL;
1578 xmlChar *code = NULL, *message = NULL;
1579 #endif
1581 if (!context) return IE_INVALID_CONTEXT;
1582 zfree(context->long_message);
1584 #if HAVE_LIBCURL
1585 /* Check if connection is established */
1586 if (!context->curl) {
1587 /* Testing printf message */
1588 isds_printf_message(context, "%s", _("I said connection closed"));
1589 return IE_CONNECTION_CLOSED;
1593 /* Build dummy request */
1594 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1595 if (!request) {
1596 isds_log_message(context, _("Could build ISDS bogus request"));
1597 return IE_ERROR;
1599 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1600 if(!isds_ns) {
1601 isds_log_message(context, _("Could not create ISDS name space"));
1602 xmlFreeNode(request);
1603 return IE_ERROR;
1605 xmlSetNs(request, isds_ns);
1607 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1609 /* Sent bogus request */
1610 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1612 /* Destroy request */
1613 xmlFreeNode(request);
1615 if (err) {
1616 isds_log(ILF_ISDS, ILL_DEBUG,
1617 _("Processing ISDS response on bogus request failed\n"));
1618 xmlFreeDoc(response);
1619 return err;
1622 /* Check for response status */
1623 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1624 &code, &message, NULL);
1625 if (err) {
1626 isds_log(ILF_ISDS, ILL_DEBUG,
1627 _("ISDS response on bogus request is missing status\n"));
1628 free(code);
1629 free(message);
1630 xmlFreeDoc(response);
1631 return err;
1633 if (xmlStrcmp(code, BAD_CAST "0000")) {
1634 char *code_locale = _isds_utf82locale((char*)code);
1635 char *message_locale = _isds_utf82locale((char*)message);
1636 isds_log(ILF_ISDS, ILL_DEBUG,
1637 _("Server refused bogus request (code=%s, message=%s)\n"),
1638 code_locale, message_locale);
1639 /* XXX: Literal error messages from ISDS are Czech messages
1640 * (English sometimes) in UTF-8. It's hard to catch them for
1641 * translation. Successfully gettextized would return in locale
1642 * encoding, unsuccessfully translated would pass in UTF-8. */
1643 isds_log_message(context, message_locale);
1644 free(code_locale);
1645 free(message_locale);
1646 free(code);
1647 free(message);
1648 xmlFreeDoc(response);
1649 return IE_ISDS;
1653 free(code);
1654 free(message);
1655 xmlFreeDoc(response);
1657 isds_log(ILF_ISDS, ILL_DEBUG,
1658 _("Bogus message accepted by server. This should not happen.\n"));
1660 return IE_SUCCESS;
1661 #else /* not HAVE_LIBCURL */
1662 return IE_NOTSUP;
1663 #endif
1667 #if HAVE_LIBCURL
1668 /* Serialize XML subtree to buffer preserving XML indentation.
1669 * @context is session context
1670 * @subtree is XML element to be serialized (with children)
1671 * @buffer is automatically reallocated buffer where serialize to
1672 * @length is size of serialized stream in bytes
1673 * @return standard error code, free @buffer in case of error */
1674 static isds_error serialize_subtree(struct isds_ctx *context,
1675 xmlNodePtr subtree, void **buffer, size_t *length) {
1676 isds_error err = IE_SUCCESS;
1677 xmlBufferPtr xml_buffer = NULL;
1678 xmlSaveCtxtPtr save_ctx = NULL;
1679 xmlDocPtr subtree_doc = NULL;
1680 xmlNodePtr subtree_copy;
1681 xmlNsPtr isds_ns;
1682 void *new_buffer;
1684 if (!context) return IE_INVALID_CONTEXT;
1685 if (!buffer) return IE_INVAL;
1686 zfree(*buffer);
1687 if (!subtree || !length) return IE_INVAL;
1689 /* Make temporary XML document with @subtree root element */
1690 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1691 * It can result in not well-formed on invalid XML tree (e.g. name space
1692 * prefix definition can miss. */
1693 /*FIXME */
1695 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1696 if (!subtree_doc) {
1697 isds_log_message(context, _("Could not build temporary document"));
1698 err = IE_ERROR;
1699 goto leave;
1702 /* XXX: Copy subtree and attach the copy to document.
1703 * One node can not bee attached into more document at the same time.
1704 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1705 * automatically.
1706 * XXX: Check xmlSaveTree() too. */
1707 subtree_copy = xmlCopyNodeList(subtree);
1708 if (!subtree_copy) {
1709 isds_log_message(context, _("Could not copy subtree"));
1710 err = IE_ERROR;
1711 goto leave;
1713 xmlDocSetRootElement(subtree_doc, subtree_copy);
1715 /* Only this way we get namespace definition as @xmlns:isds,
1716 * otherwise we get namespace prefix without definition */
1717 /* FIXME: Don't overwrite original default namespace */
1718 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1719 if(!isds_ns) {
1720 isds_log_message(context, _("Could not create ISDS name space"));
1721 err = IE_ERROR;
1722 goto leave;
1724 xmlSetNs(subtree_copy, isds_ns);
1727 /* Serialize the document into buffer */
1728 xml_buffer = xmlBufferCreate();
1729 if (!xml_buffer) {
1730 isds_log_message(context, _("Could not create xmlBuffer"));
1731 err = IE_ERROR;
1732 goto leave;
1734 /* Last argument 0 means to not format the XML tree */
1735 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1736 if (!save_ctx) {
1737 isds_log_message(context, _("Could not create XML serializer"));
1738 err = IE_ERROR;
1739 goto leave;
1741 /* XXX: According LibXML documentation, this function does not return
1742 * meaningful value yet */
1743 xmlSaveDoc(save_ctx, subtree_doc);
1744 if (-1 == xmlSaveFlush(save_ctx)) {
1745 isds_log_message(context,
1746 _("Could not serialize XML subtree"));
1747 err = IE_ERROR;
1748 goto leave;
1750 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1751 * even after xmlSaveFlush(). Thus close it here */
1752 xmlSaveClose(save_ctx); save_ctx = NULL;
1755 /* Store and detach buffer from xml_buffer */
1756 *buffer = xml_buffer->content;
1757 *length = xml_buffer->use;
1758 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1760 /* Shrink buffer */
1761 new_buffer = realloc(*buffer, *length);
1762 if (new_buffer) *buffer = new_buffer;
1764 leave:
1765 if (err) {
1766 zfree(*buffer);
1767 *length = 0;
1770 xmlSaveClose(save_ctx);
1771 xmlBufferFree(xml_buffer);
1772 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1773 return err;
1775 #endif /* HAVE_LIBCURL */
1778 #if 0
1779 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1780 * @context is session context
1781 * @document is original document where @nodeset points to
1782 * @nodeset is XPath node set to dump (recursively)
1783 * @buffer is automatically reallocated buffer where serialize to
1784 * @length is size of serialized stream in bytes
1785 * @return standard error code, free @buffer in case of error */
1786 static isds_error dump_nodeset(struct isds_ctx *context,
1787 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1788 void **buffer, size_t *length) {
1789 isds_error err = IE_SUCCESS;
1790 xmlBufferPtr xml_buffer = NULL;
1791 void *new_buffer;
1793 if (!context) return IE_INVALID_CONTEXT;
1794 if (!buffer) return IE_INVAL;
1795 zfree(*buffer);
1796 if (!document || !nodeset || !length) return IE_INVAL;
1797 *length = 0;
1799 /* Empty node set results into NULL buffer */
1800 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1801 goto leave;
1804 /* Resulting the document into buffer */
1805 xml_buffer = xmlBufferCreate();
1806 if (!xml_buffer) {
1807 isds_log_message(context, _("Could not create xmlBuffer"));
1808 err = IE_ERROR;
1809 goto leave;
1812 /* Iterate over all nodes */
1813 for (int i = 0; i < nodeset->nodeNr; i++) {
1814 /* Serialize node.
1815 * XXX: xmlNodeDump() appends to xml_buffer. */
1816 if (-1 ==
1817 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1818 isds_log_message(context, _("Could not dump XML node"));
1819 err = IE_ERROR;
1820 goto leave;
1824 /* Store and detach buffer from xml_buffer */
1825 *buffer = xml_buffer->content;
1826 *length = xml_buffer->use;
1827 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1829 /* Shrink buffer */
1830 new_buffer = realloc(*buffer, *length);
1831 if (new_buffer) *buffer = new_buffer;
1834 leave:
1835 if (err) {
1836 zfree(*buffer);
1837 *length = 0;
1840 xmlBufferFree(xml_buffer);
1841 return err;
1843 #endif
1845 #if 0
1846 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1847 * @context is session context
1848 * @document is original document where @nodeset points to
1849 * @nodeset is XPath node set to dump (recursively)
1850 * @buffer is automatically reallocated buffer where serialize to
1851 * @length is size of serialized stream in bytes
1852 * @return standard error code, free @buffer in case of error */
1853 static isds_error dump_nodeset(struct isds_ctx *context,
1854 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1855 void **buffer, size_t *length) {
1856 isds_error err = IE_SUCCESS;
1857 xmlBufferPtr xml_buffer = NULL;
1858 xmlSaveCtxtPtr save_ctx = NULL;
1859 void *new_buffer;
1861 if (!context) return IE_INVALID_CONTEXT;
1862 if (!buffer) return IE_INVAL;
1863 zfree(*buffer);
1864 if (!document || !nodeset || !length) return IE_INVAL;
1865 *length = 0;
1867 /* Empty node set results into NULL buffer */
1868 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1869 goto leave;
1872 /* Resulting the document into buffer */
1873 xml_buffer = xmlBufferCreate();
1874 if (!xml_buffer) {
1875 isds_log_message(context, _("Could not create xmlBuffer"));
1876 err = IE_ERROR;
1877 goto leave;
1879 if (xmlSubstituteEntitiesDefault(1)) {
1880 isds_log_message(context, _("Could not disable attribute escaping"));
1881 err = IE_ERROR;
1882 goto leave;
1884 /* Last argument means:
1885 * 0 to not format the XML tree
1886 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1887 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1888 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1889 if (!save_ctx) {
1890 isds_log_message(context, _("Could not create XML serializer"));
1891 err = IE_ERROR;
1892 goto leave;
1894 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1895 isds_log_message(context, _("Could not disable attribute escaping"));
1896 err = IE_ERROR;
1897 goto leave;
1901 /* Iterate over all nodes */
1902 for (int i = 0; i < nodeset->nodeNr; i++) {
1903 /* Serialize node.
1904 * XXX: xmlNodeDump() appends to xml_buffer. */
1905 /*if (-1 ==
1906 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1908 /* XXX: According LibXML documentation, this function does not return
1909 * meaningful value yet */
1910 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1911 if (-1 == xmlSaveFlush(save_ctx)) {
1912 isds_log_message(context,
1913 _("Could not serialize XML subtree"));
1914 err = IE_ERROR;
1915 goto leave;
1919 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1920 * even after xmlSaveFlush(). Thus close it here */
1921 xmlSaveClose(save_ctx); save_ctx = NULL;
1923 /* Store and detach buffer from xml_buffer */
1924 *buffer = xml_buffer->content;
1925 *length = xml_buffer->use;
1926 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1928 /* Shrink buffer */
1929 new_buffer = realloc(*buffer, *length);
1930 if (new_buffer) *buffer = new_buffer;
1932 leave:
1933 if (err) {
1934 zfree(*buffer);
1935 *length = 0;
1938 xmlSaveClose(save_ctx);
1939 xmlBufferFree(xml_buffer);
1940 return err;
1942 #endif
1945 #if HAVE_LIBCURL
1946 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1947 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1948 if (!string || !type) return IE_INVAL;
1950 if (!xmlStrcmp(string, BAD_CAST "FO"))
1951 *type = DBTYPE_FO;
1952 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1953 *type = DBTYPE_PFO;
1954 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1955 *type = DBTYPE_PFO_ADVOK;
1956 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1957 *type = DBTYPE_PFO_DANPOR;
1958 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1959 *type = DBTYPE_PFO_INSSPR;
1960 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1961 *type = DBTYPE_PO;
1962 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1963 *type = DBTYPE_PO_ZAK;
1964 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1965 *type = DBTYPE_PO_REQ;
1966 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1967 *type = DBTYPE_OVM;
1968 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1969 *type = DBTYPE_OVM_NOTAR;
1970 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1971 *type = DBTYPE_OVM_EXEKUT;
1972 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1973 *type = DBTYPE_OVM_REQ;
1974 else
1975 return IE_ENUM;
1976 return IE_SUCCESS;
1980 /* Convert ISDS dbType enum @type to UTF-8 string.
1981 * @Return pointer to static string, or NULL if unknown enum value */
1982 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1983 switch(type) {
1984 /* DBTYPE_SYSTEM is invalid value from point of view of public
1985 * SOAP interface. */
1986 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1987 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1988 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1989 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1990 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1991 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1992 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1993 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1994 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1995 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1996 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1997 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1998 default: return NULL; break;
2003 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2004 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
2005 if (!string || !type) return IE_INVAL;
2007 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2008 *type = USERTYPE_PRIMARY;
2009 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2010 *type = USERTYPE_ENTRUSTED;
2011 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2012 *type = USERTYPE_ADMINISTRATOR;
2013 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2014 *type = USERTYPE_OFFICIAL;
2015 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2016 *type = USERTYPE_OFFICIAL_CERT;
2017 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2018 *type = USERTYPE_LIQUIDATOR;
2019 else
2020 return IE_ENUM;
2021 return IE_SUCCESS;
2025 /* Convert ISDS userType enum @type to UTF-8 string.
2026 * @Return pointer to static string, or NULL if unknown enum value */
2027 static const xmlChar *isds_UserType2string(const isds_UserType type) {
2028 switch(type) {
2029 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
2030 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
2031 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
2032 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
2033 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
2034 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
2035 default: return NULL; break;
2040 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2041 static isds_error string2isds_sender_type(const xmlChar *string,
2042 isds_sender_type *type) {
2043 if (!string || !type) return IE_INVAL;
2045 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2046 *type = SENDERTYPE_PRIMARY;
2047 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2048 *type = SENDERTYPE_ENTRUSTED;
2049 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2050 *type = SENDERTYPE_ADMINISTRATOR;
2051 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2052 *type = SENDERTYPE_OFFICIAL;
2053 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2054 *type = SENDERTYPE_VIRTUAL;
2055 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2056 *type = SENDERTYPE_OFFICIAL_CERT;
2057 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2058 *type = SENDERTYPE_LIQUIDATOR;
2059 else
2060 return IE_ENUM;
2061 return IE_SUCCESS;
2065 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2066 static isds_error string2isds_payment_type(const xmlChar *string,
2067 isds_payment_type *type) {
2068 if (!string || !type) return IE_INVAL;
2070 if (!xmlStrcmp(string, BAD_CAST "K"))
2071 *type = PAYMENT_SENDER;
2072 else if (!xmlStrcmp(string, BAD_CAST "O"))
2073 *type = PAYMENT_RESPONSE;
2074 else if (!xmlStrcmp(string, BAD_CAST "G"))
2075 *type = PAYMENT_SPONSOR;
2076 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2077 *type = PAYMENT_SPONSOR_LIMITED;
2078 else if (!xmlStrcmp(string, BAD_CAST "D"))
2079 *type = PAYMENT_SPONSOR_EXTERNAL;
2080 else if (!xmlStrcmp(string, BAD_CAST "E"))
2081 *type = PAYMENT_STAMP;
2082 else
2083 return IE_ENUM;
2084 return IE_SUCCESS;
2088 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2089 * ciEventType is integer but we convert it from string representation
2090 * directly. */
2091 static isds_error string2isds_credit_event_type(const xmlChar *string,
2092 isds_credit_event_type *type) {
2093 if (!string || !type) return IE_INVAL;
2095 if (!xmlStrcmp(string, BAD_CAST "1"))
2096 *type = ISDS_CREDIT_CHARGED;
2097 else if (!xmlStrcmp(string, BAD_CAST "2"))
2098 *type = ISDS_CREDIT_DISCHARGED;
2099 else if (!xmlStrcmp(string, BAD_CAST "3"))
2100 *type = ISDS_CREDIT_MESSAGE_SENT;
2101 else if (!xmlStrcmp(string, BAD_CAST "4"))
2102 *type = ISDS_CREDIT_STORAGE_SET;
2103 else if (!xmlStrcmp(string, BAD_CAST "5"))
2104 *type = ISDS_CREDIT_EXPIRED;
2105 else
2106 return IE_ENUM;
2107 return IE_SUCCESS;
2111 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2112 * @Return pointer to static string, or NULL if unknown enum value */
2113 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2114 switch(type) {
2115 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2116 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2117 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2118 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2119 default: return NULL; break;
2122 #endif /* HAVE_LIBCURL */
2125 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2126 * @Return IE_ENUM if @string is not valid enum member */
2127 static isds_error string2isds_FileMetaType(const xmlChar *string,
2128 isds_FileMetaType *type) {
2129 if (!string || !type) return IE_INVAL;
2131 if (!xmlStrcmp(string, BAD_CAST "main"))
2132 *type = FILEMETATYPE_MAIN;
2133 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2134 *type = FILEMETATYPE_ENCLOSURE;
2135 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2136 *type = FILEMETATYPE_SIGNATURE;
2137 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2138 *type = FILEMETATYPE_META;
2139 else
2140 return IE_ENUM;
2141 return IE_SUCCESS;
2145 /* Convert UTF-8 @string to ISDS hash @algorithm.
2146 * @Return IE_ENUM if @string is not valid enum member */
2147 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2148 isds_hash_algorithm *algorithm) {
2149 if (!string || !algorithm) return IE_INVAL;
2151 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2152 *algorithm = HASH_ALGORITHM_MD5;
2153 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2154 *algorithm = HASH_ALGORITHM_SHA_1;
2155 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2156 *algorithm = HASH_ALGORITHM_SHA_224;
2157 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2158 *algorithm = HASH_ALGORITHM_SHA_256;
2159 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2160 *algorithm = HASH_ALGORITHM_SHA_384;
2161 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2162 *algorithm = HASH_ALGORITHM_SHA_512;
2163 else
2164 return IE_ENUM;
2165 return IE_SUCCESS;
2169 #if HAVE_LIBCURL
2170 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2171 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2172 if (!time || !string) return IE_INVAL;
2174 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2175 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2176 return IE_ERROR;
2178 return IE_SUCCESS;
2182 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2183 * respects the @time microseconds too. */
2184 static isds_error timeval2timestring(const struct timeval *time,
2185 xmlChar **string) {
2186 struct tm broken;
2188 if (!time || !string) return IE_INVAL;
2190 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
2191 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2193 /* TODO: small negative year should be formatted as "-0012". This is not
2194 * true for glibc "%04d". We should implement it.
2195 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
2196 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2197 if (-1 == isds_asprintf((char **) string,
2198 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
2199 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2200 broken.tm_hour, broken.tm_min, broken.tm_sec,
2201 time->tv_usec))
2202 return IE_ERROR;
2204 return IE_SUCCESS;
2206 #endif /* HAVE_LIBCURL */
2209 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2210 * It respects microseconds too.
2211 * In case of error, @time will be freed. */
2212 static isds_error timestring2timeval(const xmlChar *string,
2213 struct timeval **time) {
2214 struct tm broken;
2215 char *offset, *delim, *endptr;
2216 char subseconds[7];
2217 int offset_hours, offset_minutes;
2218 int i;
2219 #ifdef _WIN32
2220 int tmp;
2221 #endif
2223 if (!time) return IE_INVAL;
2224 if (!string) {
2225 zfree(*time);
2226 return IE_INVAL;
2229 memset(&broken, 0, sizeof(broken));
2231 if (!*time) {
2232 *time = calloc(1, sizeof(**time));
2233 if (!*time) return IE_NOMEM;
2234 } else {
2235 memset(*time, 0, sizeof(**time));
2239 /* xsd:date is ISO 8601 string, thus ASCII */
2240 /*TODO: negative year */
2242 #ifdef _WIN32
2243 i = 0;
2244 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2245 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2246 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2247 &i)) < 6) {
2248 zfree(*time);
2249 return IE_DATE;
2252 broken.tm_year -= 1900;
2253 broken.tm_mon--;
2254 offset = (char*)string + i;
2255 #else
2256 /* Parse date and time without subseconds and offset */
2257 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2258 if (!offset) {
2259 zfree(*time);
2260 return IE_DATE;
2262 #endif
2264 /* Get subseconds */
2265 if (*offset == '.' ) {
2266 offset++;
2268 /* Copy first 6 digits, pad it with zeros.
2269 * XXX: It truncates longer number, no round.
2270 * Current server implementation uses only millisecond resolution. */
2271 /* TODO: isdigit() is locale sensitive */
2272 for (i = 0;
2273 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
2274 i++, offset++) {
2275 subseconds[i] = *offset;
2277 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
2278 subseconds[i] = '0';
2280 subseconds[6] = '\0';
2282 /* Convert it into integer */
2283 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
2284 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
2285 (*time)->tv_usec == LONG_MAX) {
2286 zfree(*time);
2287 return IE_DATE;
2290 /* move to the zone offset delimiter or signal NULL*/
2291 delim = strchr(offset, '-');
2292 if (!delim)
2293 delim = strchr(offset, '+');
2294 if (!delim)
2295 delim = strchr(offset, 'Z');
2296 offset = delim;
2299 /* Get zone offset */
2300 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2301 * "" equals to "Z" and it means UTC zone. */
2302 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2303 * colon separator */
2304 if (offset && (*offset == '-' || *offset == '+')) {
2305 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2306 zfree(*time);
2307 return IE_DATE;
2309 if (*offset == '+') {
2310 broken.tm_hour -= offset_hours;
2311 broken.tm_min -= offset_minutes;
2312 } else {
2313 broken.tm_hour += offset_hours;
2314 broken.tm_min += offset_minutes;
2318 /* Convert to time_t */
2319 (*time)->tv_sec = _isds_timegm(&broken);
2320 if ((*time)->tv_sec == (time_t) -1) {
2321 zfree(*time);
2322 return IE_DATE;
2325 return IE_SUCCESS;
2329 /* Convert unsigned int into isds_message_status.
2330 * @context is session context
2331 * @number is pointer to number value. NULL will be treated as invalid value.
2332 * @status is automatically reallocated status
2333 * @return IE_SUCCESS, or error code and free status */
2334 static isds_error uint2isds_message_status(struct isds_ctx *context,
2335 const unsigned long int *number, isds_message_status **status) {
2336 if (!context) return IE_INVALID_CONTEXT;
2337 if (!status) return IE_INVAL;
2339 free(*status); *status = NULL;
2340 if (!number) return IE_INVAL;
2342 if (*number < 1 || *number > 10) {
2343 isds_printf_message(context, _("Invalid message status value: %lu"),
2344 *number);
2345 return IE_ENUM;
2348 *status = malloc(sizeof(**status));
2349 if (!*status) return IE_NOMEM;
2351 **status = 1 << *number;
2352 return IE_SUCCESS;
2356 /* Convert event description string into isds_event members type and
2357 * description
2358 * @string is raw event description starting with event prefix
2359 * @event is structure where to store type and stripped description to
2360 * @return standard error code, unknown prefix is not classified as an error.
2361 * */
2362 static isds_error eventstring2event(const xmlChar *string,
2363 struct isds_event* event) {
2364 const xmlChar *known_prefixes[] = {
2365 BAD_CAST "EV0:",
2366 BAD_CAST "EV1:",
2367 BAD_CAST "EV2:",
2368 BAD_CAST "EV3:",
2369 BAD_CAST "EV4:",
2370 BAD_CAST "EV5:",
2371 BAD_CAST "EV11:",
2372 BAD_CAST "EV12:",
2373 BAD_CAST "EV13:"
2375 const isds_event_type types[] = {
2376 EVENT_ENTERED_SYSTEM,
2377 EVENT_ACCEPTED_BY_RECIPIENT,
2378 EVENT_ACCEPTED_BY_FICTION,
2379 EVENT_UNDELIVERABLE,
2380 EVENT_COMMERCIAL_ACCEPTED,
2381 EVENT_DELIVERED,
2382 EVENT_PRIMARY_LOGIN,
2383 EVENT_ENTRUSTED_LOGIN,
2384 EVENT_SYSCERT_LOGIN
2386 unsigned int index;
2387 size_t length;
2389 if (!string || !event) return IE_INVAL;
2391 if (!event->type) {
2392 event->type = malloc(sizeof(*event->type));
2393 if (!(event->type)) return IE_NOMEM;
2395 zfree(event->description);
2397 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2398 index++) {
2399 length = xmlUTF8Strlen(known_prefixes[index]);
2401 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2402 /* Prefix is known */
2403 *event->type = types[index];
2405 /* Strip prefix from description and spaces */
2406 /* TODO: Recognize all white spaces from UCS blank class and
2407 * operate on UTF-8 chars. */
2408 for (; string[length] != '\0' && string[length] == ' '; length++);
2409 event->description = strdup((char *) (string + length));
2410 if (!(event->description)) return IE_NOMEM;
2412 return IE_SUCCESS;
2416 /* Unknown event prefix.
2417 * XSD allows any string */
2418 char *string_locale = _isds_utf82locale((char *) string);
2419 isds_log(ILF_ISDS, ILL_WARNING,
2420 _("Unknown delivery info event prefix: %s\n"), string_locale);
2421 free(string_locale);
2423 *event->type = EVENT_UKNOWN;
2424 event->description = strdup((char *) string);
2425 if (!(event->description)) return IE_NOMEM;
2427 return IE_SUCCESS;
2431 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2432 * and leave label */
2433 #define EXTRACT_STRING(element, string) { \
2434 xmlXPathFreeObject(result); \
2435 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2436 if (NULL == (result)) { \
2437 err = IE_ERROR; \
2438 goto leave; \
2440 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2441 if (result->nodesetval->nodeNr > 1) { \
2442 isds_printf_message(context, _("Multiple %s element"), element); \
2443 err = IE_ERROR; \
2444 goto leave; \
2446 (string) = (char *) \
2447 xmlXPathCastNodeSetToString(result->nodesetval); \
2448 if (NULL == (string)) { \
2449 err = IE_ERROR; \
2450 goto leave; \
2455 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2457 char *string = NULL; \
2458 EXTRACT_STRING(element, string); \
2460 if (string) { \
2461 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2462 if (!(booleanPtr)) { \
2463 free(string); \
2464 err = IE_NOMEM; \
2465 goto leave; \
2468 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2469 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2470 *(booleanPtr) = 1; \
2471 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2472 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2473 *(booleanPtr) = 0; \
2474 else { \
2475 char *string_locale = _isds_utf82locale((char*)string); \
2476 isds_printf_message(context, \
2477 _("%s value is not valid boolean: %s"), \
2478 element, string_locale); \
2479 free(string_locale); \
2480 free(string); \
2481 err = IE_ERROR; \
2482 goto leave; \
2485 free(string); \
2489 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2491 char *string = NULL; \
2492 EXTRACT_STRING(element, string); \
2493 if (string) { \
2494 long int number; \
2495 char *endptr; \
2497 number = strtol((char*)string, &endptr, 10); \
2499 if (*endptr != '\0') { \
2500 char *string_locale = _isds_utf82locale((char *)string); \
2501 isds_printf_message(context, \
2502 _("%s is not valid integer: %s"), \
2503 element, string_locale); \
2504 free(string_locale); \
2505 free(string); \
2506 err = IE_ISDS; \
2507 goto leave; \
2510 if (number == LONG_MIN || number == LONG_MAX) { \
2511 char *string_locale = _isds_utf82locale((char *)string); \
2512 isds_printf_message(context, \
2513 _("%s value out of range of long int: %s"), \
2514 element, string_locale); \
2515 free(string_locale); \
2516 free(string); \
2517 err = IE_ERROR; \
2518 goto leave; \
2521 free(string); string = NULL; \
2523 if (!(preallocated)) { \
2524 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2525 if (!(longintPtr)) { \
2526 err = IE_NOMEM; \
2527 goto leave; \
2530 *(longintPtr) = number; \
2534 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2536 char *string = NULL; \
2537 EXTRACT_STRING(element, string); \
2538 if (string) { \
2539 long int number; \
2540 char *endptr; \
2542 number = strtol((char*)string, &endptr, 10); \
2544 if (*endptr != '\0') { \
2545 char *string_locale = _isds_utf82locale((char *)string); \
2546 isds_printf_message(context, \
2547 _("%s is not valid integer: %s"), \
2548 element, string_locale); \
2549 free(string_locale); \
2550 free(string); \
2551 err = IE_ISDS; \
2552 goto leave; \
2555 if (number == LONG_MIN || number == LONG_MAX) { \
2556 char *string_locale = _isds_utf82locale((char *)string); \
2557 isds_printf_message(context, \
2558 _("%s value out of range of long int: %s"), \
2559 element, string_locale); \
2560 free(string_locale); \
2561 free(string); \
2562 err = IE_ERROR; \
2563 goto leave; \
2566 free(string); string = NULL; \
2567 if (number < 0) { \
2568 isds_printf_message(context, \
2569 _("%s value is negative: %ld"), element, number); \
2570 err = IE_ERROR; \
2571 goto leave; \
2574 if (!(preallocated)) { \
2575 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2576 if (!(ulongintPtr)) { \
2577 err = IE_NOMEM; \
2578 goto leave; \
2581 *(ulongintPtr) = number; \
2585 #define EXTRACT_DATE(element, tmPtr) { \
2586 char *string = NULL; \
2587 EXTRACT_STRING(element, string); \
2588 if (NULL != string) { \
2589 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2590 if (NULL == (tmPtr)) { \
2591 free(string); \
2592 err = IE_NOMEM; \
2593 goto leave; \
2595 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2596 if (err) { \
2597 if (err == IE_NOTSUP) { \
2598 err = IE_ISDS; \
2599 char *string_locale = _isds_utf82locale(string); \
2600 char *element_locale = _isds_utf82locale(element); \
2601 isds_printf_message(context, _("Invalid %s value: %s"), \
2602 element_locale, string_locale); \
2603 free(string_locale); \
2604 free(element_locale); \
2606 free(string); \
2607 goto leave; \
2609 free(string); \
2613 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2614 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2615 NULL); \
2616 if ((required) && (!string)) { \
2617 char *attribute_locale = _isds_utf82locale(attribute); \
2618 char *element_locale = \
2619 _isds_utf82locale((char *)xpath_ctx->node->name); \
2620 isds_printf_message(context, \
2621 _("Could not extract required %s attribute value from " \
2622 "%s element"), attribute_locale, element_locale); \
2623 free(element_locale); \
2624 free(attribute_locale); \
2625 err = IE_ERROR; \
2626 goto leave; \
2631 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2633 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2634 (xmlChar *) (string)); \
2635 if (!node) { \
2636 isds_printf_message(context, \
2637 _("Could not add %s child to %s element"), \
2638 element, (parent)->name); \
2639 err = IE_ERROR; \
2640 goto leave; \
2644 #define INSERT_STRING(parent, element, string) \
2645 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2647 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2649 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2650 else { INSERT_STRING(parent, element, "false"); } \
2653 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2655 if (booleanPtr) { \
2656 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2657 } else { \
2658 INSERT_STRING(parent, element, NULL); \
2662 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2663 if ((longintPtr)) { \
2664 /* FIXME: locale sensitive */ \
2665 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2666 err = IE_NOMEM; \
2667 goto leave; \
2669 INSERT_STRING(parent, element, buffer) \
2670 free(buffer); (buffer) = NULL; \
2671 } else { INSERT_STRING(parent, element, NULL) } \
2674 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2675 if ((ulongintPtr)) { \
2676 /* FIXME: locale sensitive */ \
2677 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2678 err = IE_NOMEM; \
2679 goto leave; \
2681 INSERT_STRING(parent, element, buffer) \
2682 free(buffer); (buffer) = NULL; \
2683 } else { INSERT_STRING(parent, element, NULL) } \
2686 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2688 /* FIXME: locale sensitive */ \
2689 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2690 err = IE_NOMEM; \
2691 goto leave; \
2693 INSERT_STRING(parent, element, buffer) \
2694 free(buffer); (buffer) = NULL; \
2697 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2698 * new attribute. */
2699 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2701 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2702 (xmlChar *) (string)); \
2703 if (!attribute_node) { \
2704 isds_printf_message(context, _("Could not add %s " \
2705 "attribute to %s element"), \
2706 (attribute), (parent)->name); \
2707 err = IE_ERROR; \
2708 goto leave; \
2712 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2713 if (string) { \
2714 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2715 if (length > (maximum)) { \
2716 isds_printf_message(context, \
2717 ngettext("%s has more than %d characters", \
2718 "%s has more than %d characters", (maximum)), \
2719 (name), (maximum)); \
2720 err = IE_2BIG; \
2721 goto leave; \
2723 if (length < (minimum)) { \
2724 isds_printf_message(context, \
2725 ngettext("%s has less than %d characters", \
2726 "%s has less than %d characters", (minimum)), \
2727 (name), (minimum)); \
2728 err = IE_2SMALL; \
2729 goto leave; \
2734 #define INSERT_ELEMENT(child, parent, element) \
2736 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2737 if (!(child)) { \
2738 isds_printf_message(context, \
2739 _("Could not add %s child to %s element"), \
2740 (element), (parent)->name); \
2741 err = IE_ERROR; \
2742 goto leave; \
2747 /* Find child element by name in given XPath context and switch context onto
2748 * it. The child must be uniq and must exist. Otherwise fails.
2749 * @context is ISDS context
2750 * @child is child element name
2751 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2752 * into it child. In error case, the @xpath_ctx keeps original value. */
2753 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2754 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2755 isds_error err = IE_SUCCESS;
2756 xmlXPathObjectPtr result = NULL;
2758 if (!context) return IE_INVALID_CONTEXT;
2759 if (!child || !xpath_ctx) return IE_INVAL;
2761 /* Find child */
2762 result = xmlXPathEvalExpression(child, xpath_ctx);
2763 if (!result) {
2764 err = IE_XML;
2765 goto leave;
2768 /* No match */
2769 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2770 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2771 char *child_locale = _isds_utf82locale((char*) child);
2772 isds_printf_message(context,
2773 _("%s element does not contain %s child"),
2774 parent_locale, child_locale);
2775 free(child_locale);
2776 free(parent_locale);
2777 err = IE_NOEXIST;
2778 goto leave;
2781 /* More matches */
2782 if (result->nodesetval->nodeNr > 1) {
2783 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2784 char *child_locale = _isds_utf82locale((char*) child);
2785 isds_printf_message(context,
2786 _("%s element contains multiple %s children"),
2787 parent_locale, child_locale);
2788 free(child_locale);
2789 free(parent_locale);
2790 err = IE_NOTUNIQ;
2791 goto leave;
2794 /* Switch context */
2795 xpath_ctx->node = result->nodesetval->nodeTab[0];
2797 leave:
2798 xmlXPathFreeObject(result);
2799 return err;
2804 #if HAVE_LIBCURL
2805 /* Find and convert XSD:gPersonName group in current node into structure
2806 * @context is ISDS context
2807 * @personName is automatically reallocated person name structure. If no member
2808 * value is found, will be freed.
2809 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2810 * elements
2811 * In case of error @personName will be freed. */
2812 static isds_error extract_gPersonName(struct isds_ctx *context,
2813 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2814 isds_error err = IE_SUCCESS;
2815 xmlXPathObjectPtr result = NULL;
2817 if (!context) return IE_INVALID_CONTEXT;
2818 if (!personName) return IE_INVAL;
2819 isds_PersonName_free(personName);
2820 if (!xpath_ctx) return IE_INVAL;
2823 *personName = calloc(1, sizeof(**personName));
2824 if (!*personName) {
2825 err = IE_NOMEM;
2826 goto leave;
2829 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2830 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2831 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2832 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2834 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2835 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2836 isds_PersonName_free(personName);
2838 leave:
2839 if (err) isds_PersonName_free(personName);
2840 xmlXPathFreeObject(result);
2841 return err;
2845 /* Find and convert XSD:gAddress group in current node into structure
2846 * @context is ISDS context
2847 * @address is automatically reallocated address structure. If no member
2848 * value is found, will be freed.
2849 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2850 * elements
2851 * In case of error @address will be freed. */
2852 static isds_error extract_gAddress(struct isds_ctx *context,
2853 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2854 isds_error err = IE_SUCCESS;
2855 xmlXPathObjectPtr result = NULL;
2857 if (!context) return IE_INVALID_CONTEXT;
2858 if (!address) return IE_INVAL;
2859 isds_Address_free(address);
2860 if (!xpath_ctx) return IE_INVAL;
2863 *address = calloc(1, sizeof(**address));
2864 if (!*address) {
2865 err = IE_NOMEM;
2866 goto leave;
2869 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2870 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2871 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2872 EXTRACT_STRING("isds:adNumberInMunicipality",
2873 (*address)->adNumberInMunicipality);
2874 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2875 EXTRACT_STRING("isds:adState", (*address)->adState);
2877 if (!(*address)->adCity && !(*address)->adStreet &&
2878 !(*address)->adNumberInStreet &&
2879 !(*address)->adNumberInMunicipality &&
2880 !(*address)->adZipCode && !(*address)->adState)
2881 isds_Address_free(address);
2883 leave:
2884 if (err) isds_Address_free(address);
2885 xmlXPathFreeObject(result);
2886 return err;
2890 /* Find and convert isds:biDate element in current node into structure
2891 * @context is ISDS context
2892 * @biDate is automatically reallocated birth date structure. If no member
2893 * value is found, will be freed.
2894 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2895 * element
2896 * In case of error @biDate will be freed. */
2897 static isds_error extract_BiDate(struct isds_ctx *context,
2898 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2899 isds_error err = IE_SUCCESS;
2900 xmlXPathObjectPtr result = NULL;
2901 char *string = NULL;
2903 if (!context) return IE_INVALID_CONTEXT;
2904 if (!biDate) return IE_INVAL;
2905 zfree(*biDate);
2906 if (!xpath_ctx) return IE_INVAL;
2908 EXTRACT_STRING("isds:biDate", string);
2909 if (string) {
2910 *biDate = calloc(1, sizeof(**biDate));
2911 if (!*biDate) {
2912 err = IE_NOMEM;
2913 goto leave;
2915 err = _isds_datestring2tm((xmlChar *)string, *biDate);
2916 if (err) {
2917 if (err == IE_NOTSUP) {
2918 err = IE_ISDS;
2919 char *string_locale = _isds_utf82locale(string);
2920 isds_printf_message(context,
2921 _("Invalid isds:biDate value: %s"), string_locale);
2922 free(string_locale);
2924 goto leave;
2928 leave:
2929 if (err) zfree(*biDate);
2930 free(string);
2931 xmlXPathFreeObject(result);
2932 return err;
2936 /* Convert isds:dBOwnerInfo XML tree into structure
2937 * @context is ISDS context
2938 * @db_owner_info is automatically reallocated box owner info structure
2939 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2940 * In case of error @db_owner_info will be freed. */
2941 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2942 struct isds_DbOwnerInfo **db_owner_info,
2943 xmlXPathContextPtr xpath_ctx) {
2944 isds_error err = IE_SUCCESS;
2945 xmlXPathObjectPtr result = NULL;
2946 char *string = NULL;
2948 if (!context) return IE_INVALID_CONTEXT;
2949 if (!db_owner_info) return IE_INVAL;
2950 isds_DbOwnerInfo_free(db_owner_info);
2951 if (!xpath_ctx) return IE_INVAL;
2954 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2955 if (!*db_owner_info) {
2956 err = IE_NOMEM;
2957 goto leave;
2960 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2962 EXTRACT_STRING("isds:dbType", string);
2963 if (string) {
2964 (*db_owner_info)->dbType =
2965 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2966 if (!(*db_owner_info)->dbType) {
2967 err = IE_NOMEM;
2968 goto leave;
2970 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2971 if (err) {
2972 zfree((*db_owner_info)->dbType);
2973 if (err == IE_ENUM) {
2974 err = IE_ISDS;
2975 char *string_locale = _isds_utf82locale(string);
2976 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2977 string_locale);
2978 free(string_locale);
2980 goto leave;
2982 zfree(string);
2985 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2987 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2988 xpath_ctx);
2989 if (err) goto leave;
2991 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2993 (*db_owner_info)->birthInfo =
2994 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2995 if (!(*db_owner_info)->birthInfo) {
2996 err = IE_NOMEM;
2997 goto leave;
2999 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
3000 xpath_ctx);
3001 if (err) goto leave;
3002 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3003 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3004 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3005 if (!(*db_owner_info)->birthInfo->biDate &&
3006 !(*db_owner_info)->birthInfo->biCity &&
3007 !(*db_owner_info)->birthInfo->biCounty &&
3008 !(*db_owner_info)->birthInfo->biState)
3009 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3011 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3012 if (err) goto leave;
3014 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3015 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3016 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3017 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3018 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3020 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3022 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3023 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3024 (*db_owner_info)->dbOpenAddressing);
3026 leave:
3027 if (err) isds_DbOwnerInfo_free(db_owner_info);
3028 free(string);
3029 xmlXPathFreeObject(result);
3030 return err;
3034 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3035 * @context is session context
3036 * @owner is libisds structure with box description
3037 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3038 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3039 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
3041 isds_error err = IE_SUCCESS;
3042 xmlNodePtr node;
3043 xmlChar *string = NULL;
3045 if (!context) return IE_INVALID_CONTEXT;
3046 if (!owner || !db_owner_info) return IE_INVAL;
3049 /* Build XSD:tDbOwnerInfo */
3050 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3051 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3053 /* dbType */
3054 if (owner->dbType) {
3055 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
3056 if (!type_string) {
3057 isds_printf_message(context, _("Invalid dbType value: %d"),
3058 *(owner->dbType));
3059 err = IE_ENUM;
3060 goto leave;
3062 INSERT_STRING(db_owner_info, "dbType", type_string);
3064 INSERT_STRING(db_owner_info, "ic", owner->ic);
3065 if (owner->personName) {
3066 INSERT_STRING(db_owner_info, "pnFirstName",
3067 owner->personName->pnFirstName);
3068 INSERT_STRING(db_owner_info, "pnMiddleName",
3069 owner->personName->pnMiddleName);
3070 INSERT_STRING(db_owner_info, "pnLastName",
3071 owner->personName->pnLastName);
3072 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3073 owner->personName->pnLastNameAtBirth);
3075 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3076 if (owner->birthInfo) {
3077 if (owner->birthInfo->biDate) {
3078 if (!tm2datestring(owner->birthInfo->biDate, &string))
3079 INSERT_STRING(db_owner_info, "biDate", string);
3080 free(string); string = NULL;
3082 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
3083 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
3084 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
3086 if (owner->address) {
3087 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
3088 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
3089 INSERT_STRING(db_owner_info, "adNumberInStreet",
3090 owner->address->adNumberInStreet);
3091 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3092 owner->address->adNumberInMunicipality);
3093 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
3094 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
3096 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3097 INSERT_STRING(db_owner_info, "email", owner->email);
3098 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3100 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3101 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3103 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3104 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3106 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3108 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3109 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3110 owner->dbOpenAddressing);
3112 leave:
3113 free(string);
3114 return err;
3118 /* Convert XSD:tDbUserInfo XML tree into structure
3119 * @context is ISDS context
3120 * @db_user_info is automatically reallocated user info structure
3121 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3122 * In case of error @db_user_info will be freed. */
3123 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3124 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3125 isds_error err = IE_SUCCESS;
3126 xmlXPathObjectPtr result = NULL;
3127 char *string = NULL;
3129 if (!context) return IE_INVALID_CONTEXT;
3130 if (!db_user_info) return IE_INVAL;
3131 isds_DbUserInfo_free(db_user_info);
3132 if (!xpath_ctx) return IE_INVAL;
3135 *db_user_info = calloc(1, sizeof(**db_user_info));
3136 if (!*db_user_info) {
3137 err = IE_NOMEM;
3138 goto leave;
3141 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3143 EXTRACT_STRING("isds:userType", string);
3144 if (string) {
3145 (*db_user_info)->userType =
3146 calloc(1, sizeof(*((*db_user_info)->userType)));
3147 if (!(*db_user_info)->userType) {
3148 err = IE_NOMEM;
3149 goto leave;
3151 err = string2isds_UserType((xmlChar *)string,
3152 (*db_user_info)->userType);
3153 if (err) {
3154 zfree((*db_user_info)->userType);
3155 if (err == IE_ENUM) {
3156 err = IE_ISDS;
3157 char *string_locale = _isds_utf82locale(string);
3158 isds_printf_message(context,
3159 _("Unknown isds:userType value: %s"), string_locale);
3160 free(string_locale);
3162 goto leave;
3164 zfree(string);
3167 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3169 (*db_user_info)->personName =
3170 calloc(1, sizeof(*((*db_user_info)->personName)));
3171 if (!(*db_user_info)->personName) {
3172 err = IE_NOMEM;
3173 goto leave;
3176 err = extract_gPersonName(context, &(*db_user_info)->personName,
3177 xpath_ctx);
3178 if (err) goto leave;
3180 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3181 if (err) goto leave;
3183 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3184 if (err) goto leave;
3186 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3187 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3189 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3190 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3191 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3193 /* ???: Default value is "CZ" according specification. Should we provide
3194 * it? */
3195 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3197 leave:
3198 if (err) isds_DbUserInfo_free(db_user_info);
3199 free(string);
3200 xmlXPathFreeObject(result);
3201 return err;
3205 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3206 * @context is session context
3207 * @user is libisds structure with user description
3208 * @db_user_info is XML element of XSD:tDbUserInfo */
3209 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3210 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3212 isds_error err = IE_SUCCESS;
3213 xmlNodePtr node;
3214 xmlChar *string = NULL;
3216 if (!context) return IE_INVALID_CONTEXT;
3217 if (!user || !db_user_info) return IE_INVAL;
3219 /* Build XSD:tDbUserInfo */
3220 if (user->personName) {
3221 INSERT_STRING(db_user_info, "pnFirstName",
3222 user->personName->pnFirstName);
3223 INSERT_STRING(db_user_info, "pnMiddleName",
3224 user->personName->pnMiddleName);
3225 INSERT_STRING(db_user_info, "pnLastName",
3226 user->personName->pnLastName);
3227 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3228 user->personName->pnLastNameAtBirth);
3230 if (user->address) {
3231 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3232 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3233 INSERT_STRING(db_user_info, "adNumberInStreet",
3234 user->address->adNumberInStreet);
3235 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3236 user->address->adNumberInMunicipality);
3237 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3238 INSERT_STRING(db_user_info, "adState", user->address->adState);
3240 if (user->biDate) {
3241 if (!tm2datestring(user->biDate, &string))
3242 INSERT_STRING(db_user_info, "biDate", string);
3243 zfree(string);
3245 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3246 INSERT_STRING(db_user_info, "userID", user->userID);
3248 /* userType */
3249 if (user->userType) {
3250 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3251 if (!type_string) {
3252 isds_printf_message(context, _("Invalid userType value: %d"),
3253 *(user->userType));
3254 err = IE_ENUM;
3255 goto leave;
3257 INSERT_STRING(db_user_info, "userType", type_string);
3260 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3261 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3262 INSERT_STRING(db_user_info, "ic", user->ic);
3263 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3264 INSERT_STRING(db_user_info, "firmName", user->firmName);
3265 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3266 INSERT_STRING(db_user_info, "caCity", user->caCity);
3267 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3268 INSERT_STRING(db_user_info, "caState", user->caState);
3270 leave:
3271 free(string);
3272 return err;
3276 /* Convert XSD:tPDZRec XML tree into structure
3277 * @context is ISDS context
3278 * @permission is automatically reallocated commercial permission structure
3279 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3280 * In case of error @permission will be freed. */
3281 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3282 struct isds_commercial_permission **permission,
3283 xmlXPathContextPtr xpath_ctx) {
3284 isds_error err = IE_SUCCESS;
3285 xmlXPathObjectPtr result = NULL;
3286 char *string = NULL;
3288 if (!context) return IE_INVALID_CONTEXT;
3289 if (!permission) return IE_INVAL;
3290 isds_commercial_permission_free(permission);
3291 if (!xpath_ctx) return IE_INVAL;
3294 *permission = calloc(1, sizeof(**permission));
3295 if (!*permission) {
3296 err = IE_NOMEM;
3297 goto leave;
3300 EXTRACT_STRING("isds:PDZType", string);
3301 if (string) {
3302 err = string2isds_payment_type((xmlChar *)string,
3303 &(*permission)->type);
3304 if (err) {
3305 if (err == IE_ENUM) {
3306 err = IE_ISDS;
3307 char *string_locale = _isds_utf82locale(string);
3308 isds_printf_message(context,
3309 _("Unknown isds:PDZType value: %s"), string_locale);
3310 free(string_locale);
3312 goto leave;
3314 zfree(string);
3317 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3318 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3320 EXTRACT_STRING("isds:PDZExpire", string);
3321 if (string) {
3322 err = timestring2timeval((xmlChar *) string,
3323 &((*permission)->expiration));
3324 if (err) {
3325 char *string_locale = _isds_utf82locale(string);
3326 if (err == IE_DATE) err = IE_ISDS;
3327 isds_printf_message(context,
3328 _("Could not convert PDZExpire as ISO time: %s"),
3329 string_locale);
3330 free(string_locale);
3331 goto leave;
3333 zfree(string);
3336 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3337 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3339 leave:
3340 if (err) isds_commercial_permission_free(permission);
3341 free(string);
3342 xmlXPathFreeObject(result);
3343 return err;
3347 /* Convert XSD:tCiRecord XML tree into structure
3348 * @context is ISDS context
3349 * @event is automatically reallocated commercial credit event structure
3350 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3351 * In case of error @event will be freed. */
3352 static isds_error extract_CiRecord(struct isds_ctx *context,
3353 struct isds_credit_event **event,
3354 xmlXPathContextPtr xpath_ctx) {
3355 isds_error err = IE_SUCCESS;
3356 xmlXPathObjectPtr result = NULL;
3357 char *string = NULL;
3358 long int *number_ptr;
3360 if (!context) return IE_INVALID_CONTEXT;
3361 if (!event) return IE_INVAL;
3362 isds_credit_event_free(event);
3363 if (!xpath_ctx) return IE_INVAL;
3366 *event = calloc(1, sizeof(**event));
3367 if (!*event) {
3368 err = IE_NOMEM;
3369 goto leave;
3372 EXTRACT_STRING("isds:ciEventTime", string);
3373 if (string) {
3374 err = timestring2timeval((xmlChar *) string,
3375 &(*event)->time);
3376 if (err) {
3377 char *string_locale = _isds_utf82locale(string);
3378 if (err == IE_DATE) err = IE_ISDS;
3379 isds_printf_message(context,
3380 _("Could not convert ciEventTime as ISO time: %s"),
3381 string_locale);
3382 free(string_locale);
3383 goto leave;
3385 zfree(string);
3388 EXTRACT_STRING("isds:ciEventType", string);
3389 if (string) {
3390 err = string2isds_credit_event_type((xmlChar *)string,
3391 &(*event)->type);
3392 if (err) {
3393 if (err == IE_ENUM) {
3394 err = IE_ISDS;
3395 char *string_locale = _isds_utf82locale(string);
3396 isds_printf_message(context,
3397 _("Unknown isds:ciEventType value: %s"), string_locale);
3398 free(string_locale);
3400 goto leave;
3402 zfree(string);
3405 number_ptr = &((*event)->credit_change);
3406 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3407 number_ptr = &(*event)->new_credit;
3408 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3410 switch((*event)->type) {
3411 case ISDS_CREDIT_CHARGED:
3412 EXTRACT_STRING("isds:ciTransID",
3413 (*event)->details.charged.transaction);
3414 break;
3415 case ISDS_CREDIT_DISCHARGED:
3416 EXTRACT_STRING("isds:ciTransID",
3417 (*event)->details.discharged.transaction);
3418 break;
3419 case ISDS_CREDIT_MESSAGE_SENT:
3420 EXTRACT_STRING("isds:ciRecipientID",
3421 (*event)->details.message_sent.recipient);
3422 EXTRACT_STRING("isds:ciPDZID",
3423 (*event)->details.message_sent.message_id);
3424 break;
3425 case ISDS_CREDIT_STORAGE_SET:
3426 number_ptr = &((*event)->details.storage_set.new_capacity);
3427 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3428 EXTRACT_DATE("isds:ciNewFrom",
3429 (*event)->details.storage_set.new_valid_from);
3430 EXTRACT_DATE("isds:ciNewTo",
3431 (*event)->details.storage_set.new_valid_to);
3432 EXTRACT_LONGINT("isds:ciOldCapacity",
3433 (*event)->details.storage_set.old_capacity, 0);
3434 EXTRACT_DATE("isds:ciOldFrom",
3435 (*event)->details.storage_set.old_valid_from);
3436 EXTRACT_DATE("isds:ciOldTo",
3437 (*event)->details.storage_set.old_valid_to);
3438 EXTRACT_STRING("isds:ciDoneBy",
3439 (*event)->details.storage_set.initiator);
3440 break;
3441 case ISDS_CREDIT_EXPIRED:
3442 break;
3445 leave:
3446 if (err) isds_credit_event_free(event);
3447 free(string);
3448 xmlXPathFreeObject(result);
3449 return err;
3453 #endif /* HAVE_LIBCURL */
3456 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3457 * isds_envelope structure. The envelope is automatically allocated but not
3458 * reallocated. The date are just appended into envelope structure.
3459 * @context is ISDS context
3460 * @envelope is automatically allocated message envelope structure
3461 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3462 * In case of error @envelope will be freed. */
3463 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3464 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3465 isds_error err = IE_SUCCESS;
3466 xmlXPathObjectPtr result = NULL;
3468 if (!context) return IE_INVALID_CONTEXT;
3469 if (!envelope) return IE_INVAL;
3470 if (!xpath_ctx) return IE_INVAL;
3473 if (!*envelope) {
3474 /* Allocate envelope */
3475 *envelope = calloc(1, sizeof(**envelope));
3476 if (!*envelope) {
3477 err = IE_NOMEM;
3478 goto leave;
3480 } else {
3481 /* Else free former data */
3482 zfree((*envelope)->dmSenderOrgUnit);
3483 zfree((*envelope)->dmSenderOrgUnitNum);
3484 zfree((*envelope)->dbIDRecipient);
3485 zfree((*envelope)->dmRecipientOrgUnit);
3486 zfree((*envelope)->dmRecipientOrgUnitNum);
3487 zfree((*envelope)->dmToHands);
3488 zfree((*envelope)->dmAnnotation);
3489 zfree((*envelope)->dmRecipientRefNumber);
3490 zfree((*envelope)->dmSenderRefNumber);
3491 zfree((*envelope)->dmRecipientIdent);
3492 zfree((*envelope)->dmSenderIdent);
3493 zfree((*envelope)->dmLegalTitleLaw);
3494 zfree((*envelope)->dmLegalTitleYear);
3495 zfree((*envelope)->dmLegalTitleSect);
3496 zfree((*envelope)->dmLegalTitlePar);
3497 zfree((*envelope)->dmLegalTitlePoint);
3498 zfree((*envelope)->dmPersonalDelivery);
3499 zfree((*envelope)->dmAllowSubstDelivery);
3502 /* Extract envelope elements added by sender or ISDS
3503 * (XSD: gMessageEnvelopeSub type) */
3504 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3505 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3506 (*envelope)->dmSenderOrgUnitNum, 0);
3507 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3508 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3509 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3510 (*envelope)->dmRecipientOrgUnitNum, 0);
3511 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3512 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3513 EXTRACT_STRING("isds:dmRecipientRefNumber",
3514 (*envelope)->dmRecipientRefNumber);
3515 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3516 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3517 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3519 /* Extract envelope elements regarding law reference */
3520 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3521 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3522 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3523 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3524 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3526 /* Extract envelope other elements */
3527 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3528 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3529 (*envelope)->dmAllowSubstDelivery);
3531 leave:
3532 if (err) isds_envelope_free(envelope);
3533 xmlXPathFreeObject(result);
3534 return err;
3539 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3540 * isds_envelope structure. The envelope is automatically allocated but not
3541 * reallocated. The date are just appended into envelope structure.
3542 * @context is ISDS context
3543 * @envelope is automatically allocated message envelope structure
3544 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3545 * In case of error @envelope will be freed. */
3546 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3547 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3548 isds_error err = IE_SUCCESS;
3549 xmlXPathObjectPtr result = NULL;
3551 if (!context) return IE_INVALID_CONTEXT;
3552 if (!envelope) return IE_INVAL;
3553 if (!xpath_ctx) return IE_INVAL;
3556 if (!*envelope) {
3557 /* Allocate envelope */
3558 *envelope = calloc(1, sizeof(**envelope));
3559 if (!*envelope) {
3560 err = IE_NOMEM;
3561 goto leave;
3563 } else {
3564 /* Else free former data */
3565 zfree((*envelope)->dmID);
3566 zfree((*envelope)->dbIDSender);
3567 zfree((*envelope)->dmSender);
3568 zfree((*envelope)->dmSenderAddress);
3569 zfree((*envelope)->dmSenderType);
3570 zfree((*envelope)->dmRecipient);
3571 zfree((*envelope)->dmRecipientAddress);
3572 zfree((*envelope)->dmAmbiguousRecipient);
3575 /* Extract envelope elements added by ISDS
3576 * (XSD: gMessageEnvelope type) */
3577 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3578 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3579 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3580 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3581 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3582 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3583 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3584 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3585 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3586 (*envelope)->dmAmbiguousRecipient);
3588 /* Extract envelope elements added by sender and ISDS
3589 * (XSD: gMessageEnvelope type) */
3590 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3591 if (err) goto leave;
3593 leave:
3594 if (err) isds_envelope_free(envelope);
3595 xmlXPathFreeObject(result);
3596 return err;
3600 /* Convert other envelope elements from XML tree into isds_envelope structure:
3601 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3602 * The envelope is automatically allocated but not reallocated.
3603 * The data are just appended into envelope structure.
3604 * @context is ISDS context
3605 * @envelope is automatically allocated message envelope structure
3606 * @xpath_ctx is XPath context with current node as parent desired elements
3607 * In case of error @envelope will be freed. */
3608 static isds_error append_status_size_times(struct isds_ctx *context,
3609 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3610 isds_error err = IE_SUCCESS;
3611 xmlXPathObjectPtr result = NULL;
3612 char *string = NULL;
3613 unsigned long int *unumber = NULL;
3615 if (!context) return IE_INVALID_CONTEXT;
3616 if (!envelope) return IE_INVAL;
3617 if (!xpath_ctx) return IE_INVAL;
3620 if (!*envelope) {
3621 /* Allocate new */
3622 *envelope = calloc(1, sizeof(**envelope));
3623 if (!*envelope) {
3624 err = IE_NOMEM;
3625 goto leave;
3627 } else {
3628 /* Free old data */
3629 zfree((*envelope)->dmMessageStatus);
3630 zfree((*envelope)->dmAttachmentSize);
3631 zfree((*envelope)->dmDeliveryTime);
3632 zfree((*envelope)->dmAcceptanceTime);
3636 /* dmMessageStatus element is mandatory */
3637 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3638 if (!unumber) {
3639 isds_log_message(context,
3640 _("Missing mandatory sisds:dmMessageStatus integer"));
3641 err = IE_ISDS;
3642 goto leave;
3644 err = uint2isds_message_status(context, unumber,
3645 &((*envelope)->dmMessageStatus));
3646 if (err) {
3647 if (err == IE_ENUM) err = IE_ISDS;
3648 goto leave;
3650 free(unumber); unumber = NULL;
3652 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3655 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3656 if (string) {
3657 err = timestring2timeval((xmlChar *) string,
3658 &((*envelope)->dmDeliveryTime));
3659 if (err) {
3660 char *string_locale = _isds_utf82locale(string);
3661 if (err == IE_DATE) err = IE_ISDS;
3662 isds_printf_message(context,
3663 _("Could not convert dmDeliveryTime as ISO time: %s"),
3664 string_locale);
3665 free(string_locale);
3666 goto leave;
3668 zfree(string);
3671 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3672 if (string) {
3673 err = timestring2timeval((xmlChar *) string,
3674 &((*envelope)->dmAcceptanceTime));
3675 if (err) {
3676 char *string_locale = _isds_utf82locale(string);
3677 if (err == IE_DATE) err = IE_ISDS;
3678 isds_printf_message(context,
3679 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3680 string_locale);
3681 free(string_locale);
3682 goto leave;
3684 zfree(string);
3687 leave:
3688 if (err) isds_envelope_free(envelope);
3689 free(unumber);
3690 free(string);
3691 xmlXPathFreeObject(result);
3692 return err;
3696 /* Convert message type attribute of current element into isds_envelope
3697 * structure.
3698 * TODO: This function can be incorporated into append_status_size_times() as
3699 * they are called always together.
3700 * The envelope is automatically allocated but not reallocated.
3701 * The data are just appended into envelope structure.
3702 * @context is ISDS context
3703 * @envelope is automatically allocated message envelope structure
3704 * @xpath_ctx is XPath context with current node as parent of attribute
3705 * carrying message type
3706 * In case of error @envelope will be freed. */
3707 static isds_error append_message_type(struct isds_ctx *context,
3708 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3709 isds_error err = IE_SUCCESS;
3711 if (!context) return IE_INVALID_CONTEXT;
3712 if (!envelope) return IE_INVAL;
3713 if (!xpath_ctx) return IE_INVAL;
3716 if (!*envelope) {
3717 /* Allocate new */
3718 *envelope = calloc(1, sizeof(**envelope));
3719 if (!*envelope) {
3720 err = IE_NOMEM;
3721 goto leave;
3723 } else {
3724 /* Free old data */
3725 zfree((*envelope)->dmType);
3729 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3731 if (!(*envelope)->dmType) {
3732 /* Use default value */
3733 (*envelope)->dmType = strdup("V");
3734 if (!(*envelope)->dmType) {
3735 err = IE_NOMEM;
3736 goto leave;
3738 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3739 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3740 isds_printf_message(context,
3741 _("Message type in dmType attribute is not 1 character long: "
3742 "%s"),
3743 type_locale);
3744 free(type_locale);
3745 err = IE_ISDS;
3746 goto leave;
3749 leave:
3750 if (err) isds_envelope_free(envelope);
3751 return err;
3755 #if HAVE_LIBCURL
3756 /* Convert dmType isds_envelope member into XML attribute and append it to
3757 * current node.
3758 * @context is ISDS context
3759 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3760 * @dm_envelope is XML element the resulting attribute will be appended to.
3761 * @return error code, in case of error context' message is filled. */
3762 static isds_error insert_message_type(struct isds_ctx *context,
3763 const char *type, xmlNodePtr dm_envelope) {
3764 isds_error err = IE_SUCCESS;
3765 xmlAttrPtr attribute_node;
3767 if (!context) return IE_INVALID_CONTEXT;
3768 if (!dm_envelope) return IE_INVAL;
3770 /* Insert optional message type */
3771 if (type) {
3772 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3773 char *type_locale = _isds_utf82locale(type);
3774 isds_printf_message(context,
3775 _("Message type in envelope is not 1 character long: %s"),
3776 type_locale);
3777 free(type_locale);
3778 err = IE_INVAL;
3779 goto leave;
3781 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3784 leave:
3785 return err;
3787 #endif /* HAVE_LIBCURL */
3790 /* Extract message document into reallocated document structure
3791 * @context is ISDS context
3792 * @document is automatically reallocated message documents structure
3793 * @xpath_ctx is XPath context with current node as isds:dmFile
3794 * In case of error @document will be freed. */
3795 static isds_error extract_document(struct isds_ctx *context,
3796 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3797 isds_error err = IE_SUCCESS;
3798 xmlXPathObjectPtr result = NULL;
3799 xmlNodePtr file_node;
3800 char *string = NULL;
3802 if (!context) return IE_INVALID_CONTEXT;
3803 if (!document) return IE_INVAL;
3804 isds_document_free(document);
3805 if (!xpath_ctx) return IE_INVAL;
3806 file_node = xpath_ctx->node;
3808 *document = calloc(1, sizeof(**document));
3809 if (!*document) {
3810 err = IE_NOMEM;
3811 goto leave;
3814 /* Extract document meta data */
3815 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3816 if (context->normalize_mime_type) {
3817 const char *normalized_type =
3818 isds_normalize_mime_type((*document)->dmMimeType);
3819 if (NULL != normalized_type &&
3820 normalized_type != (*document)->dmMimeType) {
3821 char *new_type = strdup(normalized_type);
3822 if (NULL == new_type) {
3823 isds_printf_message(context,
3824 _("Not enough memory to normalize document MIME type"));
3825 err = IE_NOMEM;
3826 goto leave;
3828 free((*document)->dmMimeType);
3829 (*document)->dmMimeType = new_type;
3833 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3834 err = string2isds_FileMetaType((xmlChar*)string,
3835 &((*document)->dmFileMetaType));
3836 if (err) {
3837 char *meta_type_locale = _isds_utf82locale(string);
3838 isds_printf_message(context,
3839 _("Document has invalid dmFileMetaType attribute value: %s"),
3840 meta_type_locale);
3841 free(meta_type_locale);
3842 err = IE_ISDS;
3843 goto leave;
3845 zfree(string);
3847 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3848 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3849 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3850 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3853 /* Extract document data.
3854 * Base64 encoded blob or XML subtree must be presented. */
3856 /* Check for dmEncodedContent */
3857 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3858 xpath_ctx);
3859 if (!result) {
3860 err = IE_XML;
3861 goto leave;
3864 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3865 /* Here we have Base64 blob */
3866 (*document)->is_xml = 0;
3868 if (result->nodesetval->nodeNr > 1) {
3869 isds_printf_message(context,
3870 _("Document has more dmEncodedContent elements"));
3871 err = IE_ISDS;
3872 goto leave;
3875 xmlXPathFreeObject(result); result = NULL;
3876 EXTRACT_STRING("isds:dmEncodedContent", string);
3878 /* Decode non-empty document */
3879 if (string && string[0] != '\0') {
3880 (*document)->data_length =
3881 _isds_b64decode(string, &((*document)->data));
3882 if ((*document)->data_length == (size_t) -1) {
3883 isds_printf_message(context,
3884 _("Error while Base64-decoding document content"));
3885 err = IE_ERROR;
3886 goto leave;
3889 } else {
3890 /* No Base64 blob, try XML document */
3891 xmlXPathFreeObject(result); result = NULL;
3892 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3893 xpath_ctx);
3894 if (!result) {
3895 err = IE_XML;
3896 goto leave;
3899 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3900 /* Here we have XML document */
3901 (*document)->is_xml = 1;
3903 if (result->nodesetval->nodeNr > 1) {
3904 isds_printf_message(context,
3905 _("Document has more dmXMLContent elements"));
3906 err = IE_ISDS;
3907 goto leave;
3910 /* XXX: We cannot serialize the content simply because:
3911 * - XML document may point out of its scope (e.g. to message
3912 * envelope)
3913 * - isds:dmXMLContent can contain more elements, no element,
3914 * a text node only
3915 * - it's not the XML way
3916 * Thus we provide the only right solution: XML DOM. Let's
3917 * application to cope with this hot potato :) */
3918 (*document)->xml_node_list =
3919 result->nodesetval->nodeTab[0]->children;
3920 } else {
3921 /* No base64 blob, nor XML document */
3922 isds_printf_message(context,
3923 _("Document has no dmEncodedContent, nor dmXMLContent "
3924 "element"));
3925 err = IE_ISDS;
3926 goto leave;
3931 leave:
3932 if (err) isds_document_free(document);
3933 free(string);
3934 xmlXPathFreeObject(result);
3935 xpath_ctx->node = file_node;
3936 return err;
3941 /* Extract message documents into reallocated list of documents
3942 * @context is ISDS context
3943 * @documents is automatically reallocated message documents list structure
3944 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3945 * In case of error @documents will be freed. */
3946 static isds_error extract_documents(struct isds_ctx *context,
3947 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
3948 isds_error err = IE_SUCCESS;
3949 xmlXPathObjectPtr result = NULL;
3950 xmlNodePtr files_node;
3951 struct isds_list *document, *prev_document = NULL;
3953 if (!context) return IE_INVALID_CONTEXT;
3954 if (!documents) return IE_INVAL;
3955 isds_list_free(documents);
3956 if (!xpath_ctx) return IE_INVAL;
3957 files_node = xpath_ctx->node;
3959 /* Find documents */
3960 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
3961 if (!result) {
3962 err = IE_XML;
3963 goto leave;
3966 /* No match */
3967 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3968 isds_printf_message(context,
3969 _("Message does not contain any document"));
3970 err = IE_ISDS;
3971 goto leave;
3975 /* Iterate over documents */
3976 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3978 /* Allocate and append list item */
3979 document = calloc(1, sizeof(*document));
3980 if (!document) {
3981 err = IE_NOMEM;
3982 goto leave;
3984 document->destructor = (void (*)(void **))isds_document_free;
3985 if (i == 0) *documents = document;
3986 else prev_document->next = document;
3987 prev_document = document;
3989 /* Extract document */
3990 xpath_ctx->node = result->nodesetval->nodeTab[i];
3991 err = extract_document(context,
3992 (struct isds_document **) &(document->data), xpath_ctx);
3993 if (err) goto leave;
3997 leave:
3998 if (err) isds_list_free(documents);
3999 xmlXPathFreeObject(result);
4000 xpath_ctx->node = files_node;
4001 return err;
4005 #if HAVE_LIBCURL
4006 /* Convert isds:dmRecord XML tree into structure
4007 * @context is ISDS context
4008 * @envelope is automatically reallocated message envelope structure
4009 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4010 * In case of error @envelope will be freed. */
4011 static isds_error extract_DmRecord(struct isds_ctx *context,
4012 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4013 isds_error err = IE_SUCCESS;
4014 xmlXPathObjectPtr result = NULL;
4016 if (!context) return IE_INVALID_CONTEXT;
4017 if (!envelope) return IE_INVAL;
4018 isds_envelope_free(envelope);
4019 if (!xpath_ctx) return IE_INVAL;
4022 *envelope = calloc(1, sizeof(**envelope));
4023 if (!*envelope) {
4024 err = IE_NOMEM;
4025 goto leave;
4029 /* Extract tRecord data */
4030 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4032 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4033 * dmAcceptanceTime. */
4034 err = append_status_size_times(context, envelope, xpath_ctx);
4035 if (err) goto leave;
4037 /* Extract envelope elements added by sender and ISDS
4038 * (XSD: gMessageEnvelope type) */
4039 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4040 if (err) goto leave;
4042 /* Get message type */
4043 err = append_message_type(context, envelope, xpath_ctx);
4044 if (err) goto leave;
4047 leave:
4048 if (err) isds_envelope_free(envelope);
4049 xmlXPathFreeObject(result);
4050 return err;
4054 /* Convert XSD:tStateChangesRecord type XML tree into structure
4055 * @context is ISDS context
4056 * @changed_status is automatically reallocated message state change structure
4057 * @xpath_ctx is XPath context with current node as element of
4058 * XSD:tStateChangesRecord type
4059 * In case of error @changed_status will be freed. */
4060 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4061 struct isds_message_status_change **changed_status,
4062 xmlXPathContextPtr xpath_ctx) {
4063 isds_error err = IE_SUCCESS;
4064 xmlXPathObjectPtr result = NULL;
4065 unsigned long int *unumber = NULL;
4066 char *string = NULL;
4068 if (!context) return IE_INVALID_CONTEXT;
4069 if (!changed_status) return IE_INVAL;
4070 isds_message_status_change_free(changed_status);
4071 if (!xpath_ctx) return IE_INVAL;
4074 *changed_status = calloc(1, sizeof(**changed_status));
4075 if (!*changed_status) {
4076 err = IE_NOMEM;
4077 goto leave;
4081 /* Extract tGetStateChangesInput data */
4082 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4084 /* dmEventTime is mandatory */
4085 EXTRACT_STRING("isds:dmEventTime", string);
4086 if (string) {
4087 err = timestring2timeval((xmlChar *) string,
4088 &((*changed_status)->time));
4089 if (err) {
4090 char *string_locale = _isds_utf82locale(string);
4091 if (err == IE_DATE) err = IE_ISDS;
4092 isds_printf_message(context,
4093 _("Could not convert dmEventTime as ISO time: %s"),
4094 string_locale);
4095 free(string_locale);
4096 goto leave;
4098 zfree(string);
4101 /* dmMessageStatus element is mandatory */
4102 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4103 if (!unumber) {
4104 isds_log_message(context,
4105 _("Missing mandatory isds:dmMessageStatus integer"));
4106 err = IE_ISDS;
4107 goto leave;
4109 err = uint2isds_message_status(context, unumber,
4110 &((*changed_status)->dmMessageStatus));
4111 if (err) {
4112 if (err == IE_ENUM) err = IE_ISDS;
4113 goto leave;
4115 zfree(unumber);
4118 leave:
4119 free(unumber);
4120 free(string);
4121 if (err) isds_message_status_change_free(changed_status);
4122 xmlXPathFreeObject(result);
4123 return err;
4125 #endif /* HAVE_LIBCURL */
4128 /* Find and convert isds:dmHash XML tree into structure
4129 * @context is ISDS context
4130 * @envelope is automatically reallocated message hash structure
4131 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4132 * In case of error @hash will be freed. */
4133 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4134 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4135 isds_error err = IE_SUCCESS;
4136 xmlNodePtr old_ctx_node;
4137 xmlXPathObjectPtr result = NULL;
4138 char *string = NULL;
4140 if (!context) return IE_INVALID_CONTEXT;
4141 if (!hash) return IE_INVAL;
4142 isds_hash_free(hash);
4143 if (!xpath_ctx) return IE_INVAL;
4145 old_ctx_node = xpath_ctx->node;
4147 *hash = calloc(1, sizeof(**hash));
4148 if (!*hash) {
4149 err = IE_NOMEM;
4150 goto leave;
4153 /* Locate dmHash */
4154 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4155 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4156 err = IE_ISDS;
4157 goto leave;
4159 if (err) {
4160 err = IE_ERROR;
4161 goto leave;
4164 /* Get hash algorithm */
4165 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4166 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4167 if (err) {
4168 if (err == IE_ENUM) {
4169 char *string_locale = _isds_utf82locale(string);
4170 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4171 string_locale);
4172 free(string_locale);
4174 goto leave;
4176 zfree(string);
4178 /* Get hash value */
4179 EXTRACT_STRING(".", string);
4180 if (!string) {
4181 isds_printf_message(context,
4182 _("sisds:dmHash element is missing hash value"));
4183 err = IE_ISDS;
4184 goto leave;
4186 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4187 if ((*hash)->length == (size_t) -1) {
4188 isds_printf_message(context,
4189 _("Error while Base64-decoding hash value"));
4190 err = IE_ERROR;
4191 goto leave;
4194 leave:
4195 if (err) isds_hash_free(hash);
4196 free(string);
4197 xmlXPathFreeObject(result);
4198 xpath_ctx->node = old_ctx_node;
4199 return err;
4203 /* Find and append isds:dmQTimestamp XML tree into envelope.
4204 * Because one service is allowed to miss time-stamp content, and we think
4205 * other could too (flaw in specification), this function is deliberated and
4206 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4207 * @context is ISDS context
4208 * @envelope is automatically allocated envelope structure
4209 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4210 * child
4211 * In case of error @envelope will be freed. */
4212 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4213 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4214 isds_error err = IE_SUCCESS;
4215 xmlXPathObjectPtr result = NULL;
4216 char *string = NULL;
4218 if (!context) return IE_INVALID_CONTEXT;
4219 if (!envelope) return IE_INVAL;
4220 if (!xpath_ctx) {
4221 isds_envelope_free(envelope);
4222 return IE_INVAL;
4225 if (!*envelope) {
4226 *envelope = calloc(1, sizeof(**envelope));
4227 if (!*envelope) {
4228 err = IE_NOMEM;
4229 goto leave;
4231 } else {
4232 zfree((*envelope)->timestamp);
4233 (*envelope)->timestamp_length = 0;
4236 /* Get dmQTimestamp */
4237 EXTRACT_STRING("sisds:dmQTimestamp", string);
4238 if (!string) {
4239 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4240 goto leave;
4242 (*envelope)->timestamp_length =
4243 _isds_b64decode(string, &((*envelope)->timestamp));
4244 if ((*envelope)->timestamp_length == (size_t) -1) {
4245 isds_printf_message(context,
4246 _("Error while Base64-decoding time stamp value"));
4247 err = IE_ERROR;
4248 goto leave;
4251 leave:
4252 if (err) isds_envelope_free(envelope);
4253 free(string);
4254 xmlXPathFreeObject(result);
4255 return err;
4259 /* Convert XSD tReturnedMessage XML tree into message structure.
4260 * It does not store serialized XML tree into message->raw.
4261 * It does store (pointer to) parsed XML tree into message->xml if needed.
4262 * @context is ISDS context
4263 * @include_documents Use true if documents must be extracted
4264 * (tReturnedMessage XSD type), use false if documents shall be omitted
4265 * (tReturnedMessageEnvelope).
4266 * @message is automatically reallocated message structure
4267 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4268 * type
4269 * In case of error @message will be freed. */
4270 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4271 const _Bool include_documents, struct isds_message **message,
4272 xmlXPathContextPtr xpath_ctx) {
4273 isds_error err = IE_SUCCESS;
4274 xmlNodePtr message_node;
4276 if (!context) return IE_INVALID_CONTEXT;
4277 if (!message) return IE_INVAL;
4278 isds_message_free(message);
4279 if (!xpath_ctx) return IE_INVAL;
4282 *message = calloc(1, sizeof(**message));
4283 if (!*message) {
4284 err = IE_NOMEM;
4285 goto leave;
4288 /* Save message XPATH context node */
4289 message_node = xpath_ctx->node;
4292 /* Extract dmDM */
4293 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4294 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4295 if (err) { err = IE_ERROR; goto leave; }
4296 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4297 if (err) goto leave;
4299 if (include_documents) {
4300 struct isds_list *item;
4302 /* Extract dmFiles */
4303 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4304 xpath_ctx);
4305 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4306 err = IE_ISDS; goto leave;
4308 if (err) { err = IE_ERROR; goto leave; }
4309 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4310 if (err) goto leave;
4312 /* Store xmlDoc of this message if needed */
4313 /* Only if we got a XML document in all the documents. */
4314 for (item = (*message)->documents; item; item = item->next) {
4315 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4316 (*message)->xml = xpath_ctx->doc;
4317 break;
4323 /* Restore context to message */
4324 xpath_ctx->node = message_node;
4326 /* Extract dmHash */
4327 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4328 xpath_ctx);
4329 if (err) goto leave;
4331 /* Extract dmQTimestamp, */
4332 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4333 xpath_ctx);
4334 if (err) goto leave;
4336 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4337 * dmAcceptanceTime. */
4338 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4339 if (err) goto leave;
4341 /* Get message type */
4342 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4343 if (err) goto leave;
4345 leave:
4346 if (err) isds_message_free(message);
4347 return err;
4351 /* Extract message event into reallocated isds_event structure
4352 * @context is ISDS context
4353 * @event is automatically reallocated message event structure
4354 * @xpath_ctx is XPath context with current node as isds:dmEvent
4355 * In case of error @event will be freed. */
4356 static isds_error extract_event(struct isds_ctx *context,
4357 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4358 isds_error err = IE_SUCCESS;
4359 xmlXPathObjectPtr result = NULL;
4360 xmlNodePtr event_node;
4361 char *string = NULL;
4363 if (!context) return IE_INVALID_CONTEXT;
4364 if (!event) return IE_INVAL;
4365 isds_event_free(event);
4366 if (!xpath_ctx) return IE_INVAL;
4367 event_node = xpath_ctx->node;
4369 *event = calloc(1, sizeof(**event));
4370 if (!*event) {
4371 err = IE_NOMEM;
4372 goto leave;
4375 /* Extract event data.
4376 * All elements are optional according XSD. That's funny. */
4377 EXTRACT_STRING("sisds:dmEventTime", string);
4378 if (string) {
4379 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4380 if (err) {
4381 char *string_locale = _isds_utf82locale(string);
4382 if (err == IE_DATE) err = IE_ISDS;
4383 isds_printf_message(context,
4384 _("Could not convert dmEventTime as ISO time: %s"),
4385 string_locale);
4386 free(string_locale);
4387 goto leave;
4389 zfree(string);
4392 /* dmEventDescr element has prefix and the rest */
4393 EXTRACT_STRING("sisds:dmEventDescr", string);
4394 if (string) {
4395 err = eventstring2event((xmlChar *) string, *event);
4396 if (err) goto leave;
4397 zfree(string);
4400 leave:
4401 if (err) isds_event_free(event);
4402 free(string);
4403 xmlXPathFreeObject(result);
4404 xpath_ctx->node = event_node;
4405 return err;
4409 /* Convert element of XSD tEventsArray type from XML tree into
4410 * isds_list of isds_event's structure. The list is automatically reallocated.
4411 * @context is ISDS context
4412 * @events is automatically reallocated list of event structures
4413 * @xpath_ctx is XPath context with current node as tEventsArray
4414 * In case of error @events will be freed. */
4415 static isds_error extract_events(struct isds_ctx *context,
4416 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4417 isds_error err = IE_SUCCESS;
4418 xmlXPathObjectPtr result = NULL;
4419 xmlNodePtr events_node;
4420 struct isds_list *event, *prev_event = NULL;
4422 if (!context) return IE_INVALID_CONTEXT;
4423 if (!events) return IE_INVAL;
4424 if (!xpath_ctx) return IE_INVAL;
4425 events_node = xpath_ctx->node;
4427 /* Free old list */
4428 isds_list_free(events);
4430 /* Find events */
4431 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4432 if (!result) {
4433 err = IE_XML;
4434 goto leave;
4437 /* No match */
4438 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4439 isds_printf_message(context,
4440 _("Delivery info does not contain any event"));
4441 err = IE_ISDS;
4442 goto leave;
4446 /* Iterate over events */
4447 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4449 /* Allocate and append list item */
4450 event = calloc(1, sizeof(*event));
4451 if (!event) {
4452 err = IE_NOMEM;
4453 goto leave;
4455 event->destructor = (void (*)(void **))isds_event_free;
4456 if (i == 0) *events = event;
4457 else prev_event->next = event;
4458 prev_event = event;
4460 /* Extract event */
4461 xpath_ctx->node = result->nodesetval->nodeTab[i];
4462 err = extract_event(context,
4463 (struct isds_event **) &(event->data), xpath_ctx);
4464 if (err) goto leave;
4468 leave:
4469 if (err) isds_list_free(events);
4470 xmlXPathFreeObject(result);
4471 xpath_ctx->node = events_node;
4472 return err;
4476 #if HAVE_LIBCURL
4477 /* Insert Base64 encoded data as element with text child.
4478 * @context is session context
4479 * @parent is XML node to append @element with @data as child
4480 * @ns is XML namespace of @element, use NULL to inherit from @parent
4481 * @element is UTF-8 encoded name of new element
4482 * @data is bit stream to encode into @element
4483 * @length is size of @data in bytes
4484 * @return standard error code and fill long error message if needed */
4485 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4486 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4487 const void *data, size_t length) {
4488 isds_error err = IE_SUCCESS;
4489 xmlNodePtr node;
4491 if (!context) return IE_INVALID_CONTEXT;
4492 if (!data && length > 0) return IE_INVAL;
4493 if (!parent || !element) return IE_INVAL;
4495 xmlChar *base64data = NULL;
4496 base64data = (xmlChar *) _isds_b64encode(data, length);
4497 if (!base64data) {
4498 isds_printf_message(context,
4499 ngettext("Not enough memory to encode %zd byte into Base64",
4500 "Not enough memory to encode %zd bytes into Base64",
4501 length),
4502 length);
4503 err = IE_NOMEM;
4504 goto leave;
4506 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4508 leave:
4509 free(base64data);
4510 return err;
4514 /* Convert isds_document structure into XML tree and append to dmFiles node.
4515 * @context is session context
4516 * @document is ISDS document
4517 * @dm_files is XML element the resulting tree will be appended to as a child.
4518 * @return error code, in case of error context' message is filled. */
4519 static isds_error insert_document(struct isds_ctx *context,
4520 struct isds_document *document, xmlNodePtr dm_files) {
4521 isds_error err = IE_SUCCESS;
4522 xmlNodePtr new_file = NULL, file = NULL, node;
4523 xmlAttrPtr attribute_node;
4525 if (!context) return IE_INVALID_CONTEXT;
4526 if (!document || !dm_files) return IE_INVAL;
4528 /* Allocate new dmFile */
4529 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4530 if (!new_file) {
4531 isds_printf_message(context, _("Could not allocate main dmFile"));
4532 err = IE_ERROR;
4533 goto leave;
4535 /* Append the new dmFile.
4536 * XXX: Main document must go first */
4537 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4538 file = xmlAddPrevSibling(dm_files->children, new_file);
4539 else
4540 file = xmlAddChild(dm_files, new_file);
4542 if (!file) {
4543 xmlFreeNode(new_file); new_file = NULL;
4544 isds_printf_message(context, _("Could not add dmFile child to "
4545 "%s element"), dm_files->name);
4546 err = IE_ERROR;
4547 goto leave;
4550 /* @dmMimeType is required */
4551 if (!document->dmMimeType) {
4552 isds_log_message(context,
4553 _("Document is missing mandatory MIME type definition"));
4554 err = IE_INVAL;
4555 goto leave;
4557 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4559 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4560 if (!string) {
4561 isds_printf_message(context,
4562 _("Document has unknown dmFileMetaType: %ld"),
4563 document->dmFileMetaType);
4564 err = IE_ENUM;
4565 goto leave;
4567 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4569 if (document->dmFileGuid) {
4570 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4572 if (document->dmUpFileGuid) {
4573 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4576 /* @dmFileDescr is required */
4577 if (!document->dmFileDescr) {
4578 isds_log_message(context,
4579 _("Document is missing mandatory description (title)"));
4580 err = IE_INVAL;
4581 goto leave;
4583 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4585 if (document->dmFormat) {
4586 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4590 /* Insert content (body) of the document. */
4591 if (document->is_xml) {
4592 /* XML document requested */
4594 /* Allocate new dmXMLContent */
4595 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4596 if (!xmlcontent) {
4597 isds_printf_message(context,
4598 _("Could not allocate dmXMLContent element"));
4599 err = IE_ERROR;
4600 goto leave;
4602 /* Append it */
4603 node = xmlAddChild(file, xmlcontent);
4604 if (!node) {
4605 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4606 isds_printf_message(context,
4607 _("Could not add dmXMLContent child to %s element"),
4608 file->name);
4609 err = IE_ERROR;
4610 goto leave;
4613 /* Copy non-empty node list */
4614 if (document->xml_node_list) {
4615 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4616 document->xml_node_list);
4617 if (!content) {
4618 isds_printf_message(context,
4619 _("Not enough memory to copy XML document"));
4620 err = IE_NOMEM;
4621 goto leave;
4624 if (!xmlAddChildList(node, content)) {
4625 xmlFreeNodeList(content);
4626 isds_printf_message(context,
4627 _("Error while adding XML document into dmXMLContent"));
4628 err = IE_XML;
4629 goto leave;
4631 /* XXX: We cannot free the content here because it's part of node's
4632 * document since now. It will be freed with it automatically. */
4634 } else {
4635 /* Binary document requested */
4636 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4637 document->data, document->data_length);
4638 if (err) goto leave;
4641 leave:
4642 return err;
4646 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4647 * The copy must be preallocated, the date are just appended into structure.
4648 * @context is ISDS context
4649 * @copy is message copy structure
4650 * @xpath_ctx is XPath context with current node as tMStatus */
4651 static isds_error append_TMStatus(struct isds_ctx *context,
4652 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4653 isds_error err = IE_SUCCESS;
4654 xmlXPathObjectPtr result = NULL;
4655 char *code = NULL, *message = NULL;
4657 if (!context) return IE_INVALID_CONTEXT;
4658 if (!copy || !xpath_ctx) return IE_INVAL;
4660 /* Free old values */
4661 zfree(copy->dmStatus);
4662 zfree(copy->dmID);
4664 /* Get error specific to this copy */
4665 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4666 if (!code) {
4667 isds_log_message(context,
4668 _("Missing isds:dmStatusCode under "
4669 "XSD:tMStatus type element"));
4670 err = IE_ISDS;
4671 goto leave;
4674 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4675 /* This copy failed */
4676 copy->error = IE_ISDS;
4677 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4678 if (message) {
4679 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4680 if (!copy->dmStatus) {
4681 copy->dmStatus = code;
4682 code = NULL;
4684 } else {
4685 copy->dmStatus = code;
4686 code = NULL;
4688 } else {
4689 /* This copy succeeded. In this case only, message ID is valid */
4690 copy->error = IE_SUCCESS;
4692 EXTRACT_STRING("isds:dmID", copy->dmID);
4693 if (!copy->dmID) {
4694 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4695 "but did not returned assigned message ID\n"));
4696 err = IE_ISDS;
4700 leave:
4701 free(code);
4702 free(message);
4703 xmlXPathFreeObject(result);
4704 return err;
4708 /* Insert struct isds_approval data (box approval) into XML tree
4709 * @context is session context
4710 * @approval is libisds structure with approval description. NULL is
4711 * acceptable.
4712 * @parent is XML element to append @approval to */
4713 static isds_error insert_GExtApproval(struct isds_ctx *context,
4714 const struct isds_approval *approval, xmlNodePtr parent) {
4716 isds_error err = IE_SUCCESS;
4717 xmlNodePtr node;
4719 if (!context) return IE_INVALID_CONTEXT;
4720 if (!parent) return IE_INVAL;
4722 if (!approval) return IE_SUCCESS;
4724 /* Build XSD:gExtApproval */
4725 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4726 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4728 leave:
4729 return err;
4733 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4734 * code
4735 * @context is session context
4736 * @service_name is name of SERVICE_DB_ACCESS
4737 * @response is reallocated server SOAP body response as XML document
4738 * @raw_response is reallocated bit stream with response body. Use
4739 * NULL if you don't care
4740 * @raw_response_length is size of @raw_response in bytes
4741 * @code is reallocated ISDS status code
4742 * @status_message is reallocated ISDS status message
4743 * @return error coded from lower layer, context message will be set up
4744 * appropriately. */
4745 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4746 const xmlChar *service_name,
4747 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4748 xmlChar **code, xmlChar **status_message) {
4750 isds_error err = IE_SUCCESS;
4751 char *service_name_locale = NULL;
4752 xmlNodePtr request = NULL, node;
4753 xmlNsPtr isds_ns = NULL;
4755 if (!context) return IE_INVALID_CONTEXT;
4756 if (!service_name) return IE_INVAL;
4757 if (!response || !code || !status_message) return IE_INVAL;
4758 if (!raw_response_length && raw_response) return IE_INVAL;
4760 /* Free output argument */
4761 xmlFreeDoc(*response); *response = NULL;
4762 if (raw_response) zfree(*raw_response);
4763 zfree(*code);
4764 zfree(*status_message);
4767 /* Check if connection is established
4768 * TODO: This check should be done downstairs. */
4769 if (!context->curl) return IE_CONNECTION_CLOSED;
4771 service_name_locale = _isds_utf82locale((char*)service_name);
4772 if (!service_name_locale) {
4773 err = IE_NOMEM;
4774 goto leave;
4777 /* Build request */
4778 request = xmlNewNode(NULL, service_name);
4779 if (!request) {
4780 isds_printf_message(context,
4781 _("Could not build %s request"), service_name_locale);
4782 err = IE_ERROR;
4783 goto leave;
4785 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4786 if(!isds_ns) {
4787 isds_log_message(context, _("Could not create ISDS name space"));
4788 err = IE_ERROR;
4789 goto leave;
4791 xmlSetNs(request, isds_ns);
4794 /* Add XSD:tDummyInput child */
4795 INSERT_STRING(request, "dbDummy", NULL);
4798 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4799 service_name_locale);
4801 /* Send request */
4802 err = isds(context, SERVICE_DB_ACCESS, request, response,
4803 raw_response, raw_response_length);
4804 xmlFreeNode(request); request = NULL;
4806 if (err) {
4807 isds_log(ILF_ISDS, ILL_DEBUG,
4808 _("Processing ISDS response on %s request failed\n"),
4809 service_name_locale);
4810 goto leave;
4813 /* Check for response status */
4814 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4815 code, status_message, NULL);
4816 if (err) {
4817 isds_log(ILF_ISDS, ILL_DEBUG,
4818 _("ISDS response on %s request is missing status\n"),
4819 service_name_locale);
4820 goto leave;
4823 /* Request processed, but nothing found */
4824 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4825 char *code_locale = _isds_utf82locale((char*) *code);
4826 char *status_message_locale =
4827 _isds_utf82locale((char*) *status_message);
4828 isds_log(ILF_ISDS, ILL_DEBUG,
4829 _("Server refused %s request (code=%s, message=%s)\n"),
4830 service_name_locale, code_locale, status_message_locale);
4831 isds_log_message(context, status_message_locale);
4832 free(code_locale);
4833 free(status_message_locale);
4834 err = IE_ISDS;
4835 goto leave;
4838 leave:
4839 free(service_name_locale);
4840 xmlFreeNode(request);
4841 return err;
4843 #endif
4846 /* Get data about logged in user and his box. */
4847 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4848 struct isds_DbOwnerInfo **db_owner_info) {
4849 isds_error err = IE_SUCCESS;
4850 #if HAVE_LIBCURL
4851 xmlDocPtr response = NULL;
4852 xmlChar *code = NULL, *message = NULL;
4853 xmlXPathContextPtr xpath_ctx = NULL;
4854 xmlXPathObjectPtr result = NULL;
4855 char *string = NULL;
4856 #endif
4858 if (!context) return IE_INVALID_CONTEXT;
4859 zfree(context->long_message);
4860 if (!db_owner_info) return IE_INVAL;
4861 isds_DbOwnerInfo_free(db_owner_info);
4863 #if HAVE_LIBCURL
4864 /* Check if connection is established */
4865 if (!context->curl) return IE_CONNECTION_CLOSED;
4868 /* Do request and check for success */
4869 err = build_send_check_dbdummy_request(context,
4870 BAD_CAST "GetOwnerInfoFromLogin",
4871 &response, NULL, NULL, &code, &message);
4872 if (err) goto leave;
4875 /* Extract data */
4876 /* Prepare structure */
4877 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4878 if (!*db_owner_info) {
4879 err = IE_NOMEM;
4880 goto leave;
4882 xpath_ctx = xmlXPathNewContext(response);
4883 if (!xpath_ctx) {
4884 err = IE_ERROR;
4885 goto leave;
4887 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4888 err = IE_ERROR;
4889 goto leave;
4892 /* Set context node */
4893 result = xmlXPathEvalExpression(BAD_CAST
4894 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4895 if (!result) {
4896 err = IE_ERROR;
4897 goto leave;
4899 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4900 isds_log_message(context, _("Missing dbOwnerInfo element"));
4901 err = IE_ISDS;
4902 goto leave;
4904 if (result->nodesetval->nodeNr > 1) {
4905 isds_log_message(context, _("Multiple dbOwnerInfo element"));
4906 err = IE_ISDS;
4907 goto leave;
4909 xpath_ctx->node = result->nodesetval->nodeTab[0];
4910 xmlXPathFreeObject(result); result = NULL;
4912 /* Extract it */
4913 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
4916 leave:
4917 if (err) {
4918 isds_DbOwnerInfo_free(db_owner_info);
4921 free(string);
4922 xmlXPathFreeObject(result);
4923 xmlXPathFreeContext(xpath_ctx);
4925 free(code);
4926 free(message);
4927 xmlFreeDoc(response);
4929 if (!err)
4930 isds_log(ILF_ISDS, ILL_DEBUG,
4931 _("GetOwnerInfoFromLogin request processed by server "
4932 "successfully.\n"));
4933 #else /* not HAVE_LIBCURL */
4934 err = IE_NOTSUP;
4935 #endif
4937 return err;
4941 /* Get data about logged in user. */
4942 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
4943 struct isds_DbUserInfo **db_user_info) {
4944 isds_error err = IE_SUCCESS;
4945 #if HAVE_LIBCURL
4946 xmlDocPtr response = NULL;
4947 xmlChar *code = NULL, *message = NULL;
4948 xmlXPathContextPtr xpath_ctx = NULL;
4949 xmlXPathObjectPtr result = NULL;
4950 #endif
4952 if (!context) return IE_INVALID_CONTEXT;
4953 zfree(context->long_message);
4954 if (!db_user_info) return IE_INVAL;
4955 isds_DbUserInfo_free(db_user_info);
4957 #if HAVE_LIBCURL
4958 /* Check if connection is established */
4959 if (!context->curl) return IE_CONNECTION_CLOSED;
4962 /* Do request and check for success */
4963 err = build_send_check_dbdummy_request(context,
4964 BAD_CAST "GetUserInfoFromLogin",
4965 &response, NULL, NULL, &code, &message);
4966 if (err) goto leave;
4969 /* Extract data */
4970 /* Prepare structure */
4971 *db_user_info = calloc(1, sizeof(**db_user_info));
4972 if (!*db_user_info) {
4973 err = IE_NOMEM;
4974 goto leave;
4976 xpath_ctx = xmlXPathNewContext(response);
4977 if (!xpath_ctx) {
4978 err = IE_ERROR;
4979 goto leave;
4981 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4982 err = IE_ERROR;
4983 goto leave;
4986 /* Set context node */
4987 result = xmlXPathEvalExpression(BAD_CAST
4988 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
4989 if (!result) {
4990 err = IE_ERROR;
4991 goto leave;
4993 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4994 isds_log_message(context, _("Missing dbUserInfo element"));
4995 err = IE_ISDS;
4996 goto leave;
4998 if (result->nodesetval->nodeNr > 1) {
4999 isds_log_message(context, _("Multiple dbUserInfo element"));
5000 err = IE_ISDS;
5001 goto leave;
5003 xpath_ctx->node = result->nodesetval->nodeTab[0];
5004 xmlXPathFreeObject(result); result = NULL;
5006 /* Extract it */
5007 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5009 leave:
5010 if (err) {
5011 isds_DbUserInfo_free(db_user_info);
5014 xmlXPathFreeObject(result);
5015 xmlXPathFreeContext(xpath_ctx);
5017 free(code);
5018 free(message);
5019 xmlFreeDoc(response);
5021 if (!err)
5022 isds_log(ILF_ISDS, ILL_DEBUG,
5023 _("GetUserInfoFromLogin request processed by server "
5024 "successfully.\n"));
5025 #else /* not HAVE_LIBCURL */
5026 err = IE_NOTSUP;
5027 #endif
5029 return err;
5033 /* Get expiration time of current password
5034 * @context is session context
5035 * @expiration is automatically reallocated time when password expires. If
5036 * password expiration is disables, NULL will be returned. In case of error
5037 * it will be nulled too. */
5038 isds_error isds_get_password_expiration(struct isds_ctx *context,
5039 struct timeval **expiration) {
5040 isds_error err = IE_SUCCESS;
5041 #if HAVE_LIBCURL
5042 xmlDocPtr response = NULL;
5043 xmlChar *code = NULL, *message = NULL;
5044 xmlXPathContextPtr xpath_ctx = NULL;
5045 xmlXPathObjectPtr result = NULL;
5046 char *string = NULL;
5047 #endif
5049 if (!context) return IE_INVALID_CONTEXT;
5050 zfree(context->long_message);
5051 if (!expiration) return IE_INVAL;
5052 zfree(*expiration);
5054 #if HAVE_LIBCURL
5055 /* Check if connection is established */
5056 if (!context->curl) return IE_CONNECTION_CLOSED;
5059 /* Do request and check for success */
5060 err = build_send_check_dbdummy_request(context,
5061 BAD_CAST "GetPasswordInfo",
5062 &response, NULL, NULL, &code, &message);
5063 if (err) goto leave;
5066 /* Extract data */
5067 xpath_ctx = xmlXPathNewContext(response);
5068 if (!xpath_ctx) {
5069 err = IE_ERROR;
5070 goto leave;
5072 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5073 err = IE_ERROR;
5074 goto leave;
5077 /* Set context node */
5078 result = xmlXPathEvalExpression(BAD_CAST
5079 "/isds:GetPasswordInfoResponse", xpath_ctx);
5080 if (!result) {
5081 err = IE_ERROR;
5082 goto leave;
5084 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5085 isds_log_message(context,
5086 _("Missing GetPasswordInfoResponse element"));
5087 err = IE_ISDS;
5088 goto leave;
5090 if (result->nodesetval->nodeNr > 1) {
5091 isds_log_message(context,
5092 _("Multiple GetPasswordInfoResponse element"));
5093 err = IE_ISDS;
5094 goto leave;
5096 xpath_ctx->node = result->nodesetval->nodeTab[0];
5097 xmlXPathFreeObject(result); result = NULL;
5099 /* Extract expiration date */
5100 EXTRACT_STRING("isds:pswExpDate", string);
5101 if (string) {
5102 /* And convert it if any returned. Otherwise expiration is disabled. */
5103 err = timestring2timeval((xmlChar *) string, expiration);
5104 if (err) {
5105 char *string_locale = _isds_utf82locale(string);
5106 if (err == IE_DATE) err = IE_ISDS;
5107 isds_printf_message(context,
5108 _("Could not convert pswExpDate as ISO time: %s"),
5109 string_locale);
5110 free(string_locale);
5111 goto leave;
5115 leave:
5116 if (err) {
5117 if (*expiration) {
5118 zfree(*expiration);
5122 free(string);
5123 xmlXPathFreeObject(result);
5124 xmlXPathFreeContext(xpath_ctx);
5126 free(code);
5127 free(message);
5128 xmlFreeDoc(response);
5130 if (!err)
5131 isds_log(ILF_ISDS, ILL_DEBUG,
5132 _("GetPasswordInfo request processed by server "
5133 "successfully.\n"));
5134 #else /* not HAVE_LIBCURL */
5135 err = IE_NOTSUP;
5136 #endif
5138 return err;
5142 #if HAVE_LIBCURL
5143 /* Request delivering new TOTP code from ISDS through side channel before
5144 * changing password.
5145 * @context is session context
5146 * @password is current password.
5147 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5148 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5149 * function for more details.
5150 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5151 * NULL, if you don't care.
5152 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5153 * error code. */
5154 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5155 const char *password, struct isds_otp *otp, char **refnumber) {
5156 isds_error err = IE_SUCCESS;
5157 char *saved_url = NULL; /* No copy */
5158 #if HAVE_CURL_REAUTHORIZATION_BUG
5159 CURL *saved_curl = NULL; /* No copy */
5160 #endif
5161 xmlNsPtr isds_ns = NULL;
5162 xmlNodePtr request = NULL;
5163 xmlDocPtr response = NULL;
5164 xmlChar *code = NULL, *message = NULL;
5165 const xmlChar *codes[] = {
5166 BAD_CAST "2300",
5167 BAD_CAST "2301",
5168 BAD_CAST "2302"
5170 const char *meanings[] = {
5171 N_("Unexpected error"),
5172 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5173 N_("One-time code could not been sent. Try later again.")
5175 const isds_otp_resolution resolutions[] = {
5176 OTP_RESOLUTION_UNKNOWN,
5177 OTP_RESOLUTION_TO_FAST,
5178 OTP_RESOLUTION_TOTP_NOT_SENT
5181 if (NULL == context) return IE_INVALID_CONTEXT;
5182 zfree(context->long_message);
5183 if (NULL == password) {
5184 isds_log_message(context,
5185 _("Second argument (password) of isds_change_password() "
5186 "is NULL"));
5187 return IE_INVAL;
5190 /* Check if connection is established
5191 * TODO: This check should be done downstairs. */
5192 if (!context->curl) return IE_CONNECTION_CLOSED;
5194 if (!context->otp) {
5195 isds_log_message(context, _("This function requires OTP-authenticated "
5196 "context"));
5197 return IE_INVALID_CONTEXT;
5199 if (NULL == otp) {
5200 isds_log_message(context, _("If one-time password authentication "
5201 "method is in use, requesting new OTP code requires "
5202 "one-time credentials argument either"));
5203 return IE_INVAL;
5205 if (otp->method != OTP_TIME) {
5206 isds_log_message(context, _("Requesting new time-based OTP code from "
5207 "server requires one-time password authentication "
5208 "method"));
5209 return IE_INVAL;
5211 if (otp->otp_code != NULL) {
5212 isds_log_message(context, _("Requesting new time-based OTP code from "
5213 "server requires undefined OTP code member in "
5214 "one-time credentials argument"));
5215 return IE_INVAL;
5219 /* Build request */
5220 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5221 if (!request) {
5222 isds_log_message(context, _("Could not build SendSMSCode request"));
5223 return IE_ERROR;
5225 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5226 if(!isds_ns) {
5227 isds_log_message(context, _("Could not create ISDS name space"));
5228 xmlFreeNode(request);
5229 return IE_ERROR;
5231 xmlSetNs(request, isds_ns);
5233 /* Change URL temporarily for sending this request only */
5235 char *new_url = NULL;
5236 if ((err = _isds_build_url_from_context(context,
5237 "%1$.*2$sasws/changePassword", &new_url))) {
5238 goto leave;
5240 saved_url = context->url;
5241 context->url = new_url;
5244 /* Store credentials for sending this request only */
5245 context->otp_credentials = otp;
5246 _isds_discard_credentials(context, 0);
5247 if ((err = _isds_store_credentials(context, context->saved_username,
5248 password, NULL))) {
5249 _isds_discard_credentials(context, 0);
5250 goto leave;
5252 #if HAVE_CURL_REAUTHORIZATION_BUG
5253 saved_curl = context->curl;
5254 context->curl = curl_easy_init();
5255 if (NULL == context->curl) {
5256 err = IE_ERROR;
5257 goto leave;
5259 if (context->timeout) {
5260 err = isds_set_timeout(context, context->timeout);
5261 if (err) goto leave;
5263 #endif
5265 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5267 /* Sent request */
5268 err = isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5270 /* Remove temporal credentials */
5271 _isds_discard_credentials(context, 0);
5272 /* Detach pointer to OTP credentials from context */
5273 context->otp_credentials = NULL;
5274 /* Keep context->otp true to keep signaling this is OTP session */
5276 /* Destroy request */
5277 xmlFreeNode(request); request = NULL;
5279 if (err) {
5280 isds_log(ILF_ISDS, ILL_DEBUG,
5281 _("Processing ISDS response on SendSMSCode request failed\n"));
5282 goto leave;
5285 /* Check for response status */
5286 err = isds_response_status(context, SERVICE_ASWS, response,
5287 &code, &message, (xmlChar **)refnumber);
5288 if (err) {
5289 isds_log(ILF_ISDS, ILL_DEBUG,
5290 _("ISDS response on SendSMSCode request is missing "
5291 "status\n"));
5292 goto leave;
5295 /* Check for error */
5296 if (xmlStrcmp(code, BAD_CAST "0000")) {
5297 char *code_locale = _isds_utf82locale((char*)code);
5298 char *message_locale = _isds_utf82locale((char*)message);
5299 int i;
5300 isds_log(ILF_ISDS, ILL_DEBUG,
5301 _("Server refused to send new code on SendSMSCode "
5302 "request (code=%s, message=%s)\n"),
5303 code_locale, message_locale);
5305 /* Check for known error codes */
5306 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5307 if (!xmlStrcmp(code, codes[i])) break;
5309 if (i < sizeof(codes)/sizeof(*codes)) {
5310 isds_log_message(context, _(meanings[i]));
5311 /* Mimic otp->resolution according to the code, specification does
5312 * prescribe OTP header to be available. */
5313 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5314 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5315 otp->resolution = resolutions[i];
5316 } else
5317 isds_log_message(context, message_locale);
5319 free(code_locale);
5320 free(message_locale);
5322 err = IE_ISDS;
5323 goto leave;
5326 /* Otherwise new code sent successfully */
5327 /* Mimic otp->resolution according to the code, specification does
5328 * prescribe OTP header to be available. */
5329 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5330 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5332 leave:
5333 if (NULL != saved_url) {
5334 /* Revert URL to original one */
5335 zfree(context->url);
5336 context->url = saved_url;
5338 #if HAVE_CURL_REAUTHORIZATION_BUG
5339 if (NULL != saved_curl) {
5340 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5341 context->curl = saved_curl;
5343 #endif
5345 free(code);
5346 free(message);
5347 xmlFreeDoc(response);
5348 xmlFreeNode(request);
5350 if (!err)
5351 isds_log(ILF_ISDS, ILL_DEBUG,
5352 _("New OTP code has been sent successfully on SendSMSCode "
5353 "request.\n"));
5354 return err;
5358 /* Convert response status code to isds_error code and set long message
5359 * @context is context to save long message to
5360 * @map is mapping from codes to errors and messages. Pass NULL for generic
5361 * handling.
5362 * @code is status code to translate
5363 * @message is non-localized status message to put into long message in case
5364 * of uknown error. It can be NULL if server did not provide any.
5365 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5366 * invalid invocation. */
5367 static isds_error statuscode2isds_error(struct isds_ctx *context,
5368 const struct code_map_isds_error *map,
5369 const xmlChar *code, const xmlChar *message) {
5370 if (NULL == code) {
5371 isds_log_message(context,
5372 _("NULL status code passed to statuscode2isds_error()"));
5373 return IE_INVAL;
5376 if (NULL != map) {
5377 /* Check for known error codes */
5378 for (int i=0; map->codes[i] != NULL; i++) {
5379 if (!xmlStrcmp(code, map->codes[i])) {
5380 isds_log_message(context, _(map->meanings[i]));
5381 return map->errors[i];
5386 /* Other error */
5387 if (xmlStrcmp(code, BAD_CAST "0000")) {
5388 char *message_locale = _isds_utf82locale((char*)message);
5389 if (NULL == message_locale)
5390 isds_log_message(context, _("ISDS server returned unknown error"));
5391 else
5392 isds_log_message(context, message_locale);
5393 free(message_locale);
5394 return IE_ISDS;
5397 return IE_SUCCESS;
5399 #endif
5402 /* Change user password in ISDS.
5403 * User must supply old password, new password will takes effect after some
5404 * time, current session can continue. Password must fulfill some constraints.
5405 * @context is session context
5406 * @old_password is current password.
5407 * @new_password is requested new password
5408 * @otp auxiliary data required if one-time password authentication is in use,
5409 * defines OTP code (if known) and returns fine grade resolution of OTP
5410 * procedure. Pass NULL, if one-time password authentication is not needed.
5411 * Please note the @otp argument must match OTP method used at log-in time. See
5412 * isds_login() function for more details.
5413 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5414 * NULL, if you don't care.
5415 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5416 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5417 * awaiting OTP code that has been delivered by side channel to the user. */
5418 isds_error isds_change_password(struct isds_ctx *context,
5419 const char *old_password, const char *new_password,
5420 struct isds_otp *otp, char **refnumber) {
5421 isds_error err = IE_SUCCESS;
5422 #if HAVE_LIBCURL
5423 char *saved_url = NULL; /* No copy */
5424 #if HAVE_CURL_REAUTHORIZATION_BUG
5425 CURL *saved_curl = NULL; /* No copy */
5426 #endif
5427 xmlNsPtr isds_ns = NULL;
5428 xmlNodePtr request = NULL, node;
5429 xmlDocPtr response = NULL;
5430 xmlChar *code = NULL, *message = NULL;
5431 const xmlChar *codes[] = {
5432 BAD_CAST "1066",
5433 BAD_CAST "1067",
5434 BAD_CAST "1079",
5435 BAD_CAST "1080",
5436 BAD_CAST "1081",
5437 BAD_CAST "1082",
5438 BAD_CAST "1083",
5439 BAD_CAST "1090",
5440 BAD_CAST "1091",
5441 BAD_CAST "2300",
5442 BAD_CAST "9204"
5444 const char *meanings[] = {
5445 N_("Password length must be between 8 and 32 characters"),
5446 N_("Password cannot be reused"), /* Server does not distinguish 1067
5447 and 1091 on ChangePasswordOTP */
5448 N_("Password contains forbidden character"),
5449 N_("Password must contain at least one upper-case letter, "
5450 "one lower-case, and one digit"),
5451 N_("Password cannot contain sequence of three identical characters"),
5452 N_("Password cannot contain user identifier"),
5453 N_("Password is too simmple"),
5454 N_("Old password is not valid"),
5455 N_("Password cannot be reused"),
5456 N_("Unexpected error"),
5457 N_("LDAP update error")
5459 #endif
5461 if (!context) return IE_INVALID_CONTEXT;
5462 zfree(context->long_message);
5463 if (NULL != refnumber)
5464 zfree(*refnumber);
5465 if (NULL == old_password) {
5466 isds_log_message(context,
5467 _("Second argument (old password) of isds_change_password() "
5468 "is NULL"));
5469 return IE_INVAL;
5471 if (NULL == otp && NULL == new_password) {
5472 isds_log_message(context,
5473 _("Third argument (new password) of isds_change_password() "
5474 "is NULL"));
5475 return IE_INVAL;
5478 #if HAVE_LIBCURL
5479 /* Check if connection is established
5480 * TODO: This check should be done downstairs. */
5481 if (!context->curl) return IE_CONNECTION_CLOSED;
5483 if (context->otp && NULL == otp) {
5484 isds_log_message(context, _("If one-time password authentication "
5485 "method is in use, changing password requires one-time "
5486 "credentials either"));
5487 return IE_INVAL;
5490 /* Build ChangeISDSPassword request */
5491 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5492 BAD_CAST "ChangePasswordOTP");
5493 if (!request) {
5494 isds_log_message(context, (NULL == otp) ?
5495 _("Could not build ChangeISDSPassword request") :
5496 _("Could not build ChangePasswordOTP request"));
5497 return IE_ERROR;
5499 isds_ns = xmlNewNs(request,
5500 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5501 NULL);
5502 if(!isds_ns) {
5503 isds_log_message(context, _("Could not create ISDS name space"));
5504 xmlFreeNode(request);
5505 return IE_ERROR;
5507 xmlSetNs(request, isds_ns);
5509 INSERT_STRING(request, "dbOldPassword", old_password);
5510 INSERT_STRING(request, "dbNewPassword", new_password);
5512 if (NULL != otp) {
5513 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5514 switch (otp->method) {
5515 case OTP_HMAC:
5516 isds_log(ILF_SEC, ILL_INFO,
5517 _("Selected authentication method: "
5518 "HMAC-based one-time password\n"));
5519 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5520 break;
5521 case OTP_TIME:
5522 isds_log(ILF_SEC, ILL_INFO,
5523 _("Selected authentication method: "
5524 "Time-based one-time password\n"));
5525 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5526 if (otp->otp_code == NULL) {
5527 isds_log(ILF_SEC, ILL_INFO,
5528 _("OTP code has not been provided by "
5529 "application, requesting server for "
5530 "new one.\n"));
5531 err = _isds_request_totp_code(context, old_password, otp,
5532 refnumber);
5533 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5534 goto leave;
5536 } else {
5537 isds_log(ILF_SEC, ILL_INFO,
5538 _("OTP code has been provided by "
5539 "application, not requesting server "
5540 "for new one.\n"));
5542 break;
5543 default:
5544 isds_log_message(context,
5545 _("Unknown one-time password authentication "
5546 "method requested by application"));
5547 err = IE_ENUM;
5548 goto leave;
5551 /* Change URL temporarily for sending this request only */
5553 char *new_url = NULL;
5554 if ((err = _isds_build_url_from_context(context,
5555 "%1$.*2$sasws/changePassword", &new_url))) {
5556 goto leave;
5558 saved_url = context->url;
5559 context->url = new_url;
5562 /* Store credentials for sending this request only */
5563 context->otp_credentials = otp;
5564 _isds_discard_credentials(context, 0);
5565 if ((err = _isds_store_credentials(context, context->saved_username,
5566 old_password, NULL))) {
5567 _isds_discard_credentials(context, 0);
5568 goto leave;
5570 #if HAVE_CURL_REAUTHORIZATION_BUG
5571 saved_curl = context->curl;
5572 context->curl = curl_easy_init();
5573 if (NULL == context->curl) {
5574 err = IE_ERROR;
5575 goto leave;
5577 if (context->timeout) {
5578 err = isds_set_timeout(context, context->timeout);
5579 if (err) goto leave;
5581 #endif
5584 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5585 _("Sending ChangeISDSPassword request to ISDS\n") :
5586 _("Sending ChangePasswordOTP request to ISDS\n"));
5588 /* Sent request */
5589 err = isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5590 request, &response, NULL, NULL);
5592 if (otp) {
5593 /* Remove temporal credentials */
5594 _isds_discard_credentials(context, 0);
5595 /* Detach pointer to OTP credentials from context */
5596 context->otp_credentials = NULL;
5597 /* Keep context->otp true to keep signaling this is OTP session */
5600 /* Destroy request */
5601 xmlFreeNode(request); request = NULL;
5603 if (err) {
5604 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5605 _("Processing ISDS response on ChangeISDSPassword "
5606 "request failed\n") :
5607 _("Processing ISDS response on ChangePasswordOTP "
5608 "request failed\n"));
5609 goto leave;
5612 /* Check for response status */
5613 err = isds_response_status(context,
5614 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5615 &code, &message, (xmlChar **)refnumber);
5616 if (err) {
5617 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5618 _("ISDS response on ChangeISDSPassword request is missing "
5619 "status\n") :
5620 _("ISDS response on ChangePasswordOTP request is missing "
5621 "status\n"));
5622 goto leave;
5625 /* Check for known error codes */
5626 for (int i=0; i < sizeof(codes)/sizeof(*codes); i++) {
5627 if (!xmlStrcmp(code, codes[i])) {
5628 char *code_locale = _isds_utf82locale((char*)code);
5629 char *message_locale = _isds_utf82locale((char*)message);
5630 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5631 _("Server refused to change password on ChangeISDSPassword "
5632 "request (code=%s, message=%s)\n") :
5633 _("Server refused to change password on ChangePasswordOTP "
5634 "request (code=%s, message=%s)\n"),
5635 code_locale, message_locale);
5636 free(code_locale);
5637 free(message_locale);
5638 isds_log_message(context, _(meanings[i]));
5639 err = IE_INVAL;
5640 goto leave;
5644 /* Other error */
5645 if (xmlStrcmp(code, BAD_CAST "0000")) {
5646 char *code_locale = _isds_utf82locale((char*)code);
5647 char *message_locale = _isds_utf82locale((char*)message);
5648 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5649 _("Server refused to change password on ChangeISDSPassword "
5650 "request (code=%s, message=%s)\n") :
5651 _("Server refused to change password on ChangePasswordOTP "
5652 "request (code=%s, message=%s)\n"),
5653 code_locale, message_locale);
5654 isds_log_message(context, message_locale);
5655 free(code_locale);
5656 free(message_locale);
5657 err = IE_ISDS;
5658 goto leave;
5661 /* Otherwise password changed successfully */
5663 leave:
5664 if (NULL != saved_url) {
5665 /* Revert URL to original one */
5666 zfree(context->url);
5667 context->url = saved_url;
5669 #if HAVE_CURL_REAUTHORIZATION_BUG
5670 if (NULL != saved_curl) {
5671 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5672 context->curl = saved_curl;
5674 #endif
5676 free(code);
5677 free(message);
5678 xmlFreeDoc(response);
5679 xmlFreeNode(request);
5681 if (!err)
5682 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5683 _("Password changed successfully on ChangeISDSPassword "
5684 "request.\n") :
5685 _("Password changed successfully on ChangePasswordOTP "
5686 "request.\n"));
5687 #else /* not HAVE_LIBCURL */
5688 err = IE_NOTSUP;
5689 #endif
5691 return err;
5695 #if HAVE_LIBCURL
5696 /* Generic middle part with request sending and response check.
5697 * It sends prepared request and checks for error code.
5698 * @context is ISDS session context.
5699 * @service is ISDS service handler
5700 * @service_name is name in scope of given @service
5701 * @request is XML tree with request. Will be freed to save memory.
5702 * @response is XML document outputting ISDS response.
5703 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5704 * @map is mapping from status code to library error. Pass NULL if no special
5705 * handling is requested.
5706 * NULL, if you don't care. */
5707 static isds_error send_destroy_request_check_response(
5708 struct isds_ctx *context,
5709 const isds_service service, const xmlChar *service_name,
5710 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5711 const struct code_map_isds_error *map) {
5712 isds_error err = IE_SUCCESS;
5713 char *service_name_locale = NULL;
5714 xmlChar *code = NULL, *message = NULL;
5717 if (!context) return IE_INVALID_CONTEXT;
5718 if (!service_name || *service_name == '\0' || !request || !*request ||
5719 !response)
5720 return IE_INVAL;
5722 /* Check if connection is established
5723 * TODO: This check should be done downstairs. */
5724 if (!context->curl) return IE_CONNECTION_CLOSED;
5726 service_name_locale = _isds_utf82locale((char*) service_name);
5727 if (!service_name_locale) {
5728 err = IE_NOMEM;
5729 goto leave;
5732 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5733 service_name_locale);
5735 /* Send request */
5736 err = isds(context, service, *request, response, NULL, NULL);
5737 xmlFreeNode(*request); *request = NULL;
5739 if (err) {
5740 isds_log(ILF_ISDS, ILL_DEBUG,
5741 _("Processing ISDS response on %s request failed\n"),
5742 service_name_locale);
5743 goto leave;
5746 /* Check for response status */
5747 err = isds_response_status(context, service, *response,
5748 &code, &message, refnumber);
5749 if (err) {
5750 isds_log(ILF_ISDS, ILL_DEBUG,
5751 _("ISDS response on %s request is missing status\n"),
5752 service_name_locale);
5753 goto leave;
5756 err = statuscode2isds_error(context, map, code, message);
5758 /* Request processed, but server failed */
5759 if (xmlStrcmp(code, BAD_CAST "0000")) {
5760 char *code_locale = _isds_utf82locale((char*) code);
5761 char *message_locale = _isds_utf82locale((char*) message);
5762 isds_log(ILF_ISDS, ILL_DEBUG,
5763 _("Server refused %s request (code=%s, message=%s)\n"),
5764 service_name_locale, code_locale, message_locale);
5765 free(code_locale);
5766 free(message_locale);
5767 goto leave;
5771 leave:
5772 free(code);
5773 free(message);
5774 if (err && *response) {
5775 xmlFreeDoc(*response);
5776 *response = NULL;
5778 if (*request) {
5779 xmlFreeNode(*request);
5780 *request = NULL;
5782 free(service_name_locale);
5784 return err;
5788 /* Generic bottom half with request sending.
5789 * It sends prepared request, checks for error code, destroys response and
5790 * request and log success or failure.
5791 * @context is ISDS session context.
5792 * @service is ISDS service handler
5793 * @service_name is name in scope of given @service
5794 * @request is XML tree with request. Will be freed to save memory.
5795 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5796 * NULL, if you don't care. */
5797 static isds_error send_request_check_drop_response(
5798 struct isds_ctx *context,
5799 const isds_service service, const xmlChar *service_name,
5800 xmlNodePtr *request, xmlChar **refnumber) {
5801 isds_error err = IE_SUCCESS;
5802 xmlDocPtr response = NULL;
5805 if (!context) return IE_INVALID_CONTEXT;
5806 if (!service_name || *service_name == '\0' || !request || !*request)
5807 return IE_INVAL;
5809 /* Send request and check response*/
5810 err = send_destroy_request_check_response(context,
5811 service, service_name, request, &response, refnumber, NULL);
5813 xmlFreeDoc(response);
5815 if (*request) {
5816 xmlFreeNode(*request);
5817 *request = NULL;
5820 if (!err) {
5821 char *service_name_locale = _isds_utf82locale((char *) service_name);
5822 isds_log(ILF_ISDS, ILL_DEBUG,
5823 _("%s request processed by server successfully.\n"),
5824 service_name_locale);
5825 free(service_name_locale);
5828 return err;
5832 /* Insert isds_credentials_delivery structure into XML request if not NULL
5833 * @context is session context
5834 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5835 * credentials delivery. The email field is passed.
5836 * @parent is XML element where to insert */
5837 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5838 const struct isds_credentials_delivery *credentials_delivery,
5839 xmlNodePtr parent) {
5840 isds_error err = IE_SUCCESS;
5841 xmlNodePtr node;
5843 if (!context) return IE_INVALID_CONTEXT;
5844 if (!parent) return IE_INVAL;
5846 if (credentials_delivery) {
5847 /* Following elements are valid only for services:
5848 * NewAccessData, AddDataBoxUser, CreateDataBox */
5849 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5850 INSERT_STRING(parent, "email", credentials_delivery->email);
5853 leave:
5854 return err;
5858 /* Extract credentials delivery from ISDS response.
5859 * @context is session context
5860 * @credentials_delivery is pointer to valid structure to fill in returned
5861 * user's password (and new log-in name). If NULL, do not extract the data.
5862 * @response is pointer to XML document with ISDS response
5863 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5864 * @return IE_SUCCESS even if new user name has not been found because it's not
5865 * clear whether it's returned always. */
5866 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5867 struct isds_credentials_delivery *credentials_delivery,
5868 xmlDocPtr response, const char *request_name) {
5869 isds_error err = IE_SUCCESS;
5870 xmlXPathContextPtr xpath_ctx = NULL;
5871 xmlXPathObjectPtr result = NULL;
5872 char *xpath_query = NULL;
5874 if (!context) return IE_INVALID_CONTEXT;
5875 if (credentials_delivery) {
5876 zfree(credentials_delivery->token);
5877 zfree(credentials_delivery->new_user_name);
5879 if (!response || !request_name || !*request_name) return IE_INVAL;
5882 /* Extract optional token */
5883 if (credentials_delivery) {
5884 xpath_ctx = xmlXPathNewContext(response);
5885 if (!xpath_ctx) {
5886 err = IE_ERROR;
5887 goto leave;
5889 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5890 err = IE_ERROR;
5891 goto leave;
5894 /* Verify root element */
5895 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
5896 request_name)) {
5897 err = IE_NOMEM;
5898 goto leave;
5900 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
5901 if (!result) {
5902 err = IE_ERROR;
5903 goto leave;
5905 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5906 char *request_name_locale = _isds_utf82locale(request_name);
5907 isds_log(ILF_ISDS, ILL_WARNING,
5908 _("Wrong element in ISDS response for %s request "
5909 "while extracting credentials delivery details\n"),
5910 request_name_locale);
5911 free(request_name_locale);
5912 err = IE_ERROR;
5913 goto leave;
5915 xpath_ctx->node = result->nodesetval->nodeTab[0];
5918 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5919 * optional. */
5920 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
5922 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
5923 if (!credentials_delivery->token) {
5924 char *request_name_locale = _isds_utf82locale(request_name);
5925 isds_log(ILF_ISDS, ILL_ERR,
5926 _("ISDS did not return token on %s request "
5927 "even if requested\n"), request_name_locale);
5928 free(request_name_locale);
5929 err = IE_ERROR;
5933 leave:
5934 free(xpath_query);
5935 xmlXPathFreeObject(result);
5936 xmlXPathFreeContext(xpath_ctx);
5938 return err;
5942 /* Build XSD:tCreateDBInput request type for box creating.
5943 * @context is session context
5944 * @request outputs built XML tree
5945 * @service_name is request name of SERVICE_DB_MANIPULATION service
5946 * @box is box description to create including single primary user (in case of
5947 * FO box type)
5948 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5949 * box, or contact address of PFO box owner)
5950 * @former_names is optional former name of box owner. Pass NULL if otherwise.
5951 * @upper_box_id is optional ID of supper box if currently created box is
5952 * subordinated.
5953 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
5954 * don't care.
5955 * @credentials_delivery is valid pointer if ISDS should return token that box
5956 * owner can use to obtain his new credentials in on-line way. Then valid email
5957 * member value should be supplied.
5958 * @approval is optional external approval of box manipulation */
5959 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
5960 xmlNodePtr *request, const xmlChar *service_name,
5961 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
5962 const xmlChar *former_names, const xmlChar *upper_box_id,
5963 const xmlChar *ceo_label,
5964 const struct isds_credentials_delivery *credentials_delivery,
5965 const struct isds_approval *approval) {
5966 isds_error err = IE_SUCCESS;
5967 xmlNsPtr isds_ns = NULL;
5968 xmlNodePtr node, dbPrimaryUsers;
5969 xmlChar *string = NULL;
5970 const struct isds_list *item;
5973 if (!context) return IE_INVALID_CONTEXT;
5974 if (!request || !service_name || service_name[0] == '\0' || !box)
5975 return IE_INVAL;
5978 /* Build CreateDataBox-similar request */
5979 *request = xmlNewNode(NULL, service_name);
5980 if (!*request) {
5981 char *service_name_locale = _isds_utf82locale((char*) service_name);
5982 isds_printf_message(context, _("Could build %s request"),
5983 service_name_locale);
5984 free(service_name_locale);
5985 return IE_ERROR;
5987 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
5988 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
5989 if (!isds_ns) {
5990 isds_log_message(context, _("Could not create ISDS1 name space"));
5991 xmlFreeNode(*request);
5992 return IE_ERROR;
5994 } else {
5995 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
5996 if (!isds_ns) {
5997 isds_log_message(context, _("Could not create ISDS name space"));
5998 xmlFreeNode(*request);
5999 return IE_ERROR;
6002 xmlSetNs(*request, isds_ns);
6004 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6005 err = insert_DbOwnerInfo(context, box, node);
6006 if (err) goto leave;
6008 /* Insert users */
6009 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6010 * verbose documentation allows none dbUserInfo */
6011 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6012 for (item = users; item; item = item->next) {
6013 if (item->data) {
6014 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6015 err = insert_DbUserInfo(context,
6016 (struct isds_DbUserInfo *) item->data, node);
6017 if (err) goto leave;
6021 INSERT_STRING(*request, "dbFormerNames", former_names);
6022 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6023 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6025 err = insert_credentials_delivery(context, credentials_delivery, *request);
6026 if (err) goto leave;
6028 err = insert_GExtApproval(context, approval, *request);
6029 if (err) goto leave;
6031 leave:
6032 if (err) {
6033 xmlFreeNode(*request);
6034 *request = NULL;
6036 free(string);
6037 return err;
6039 #endif /* HAVE_LIBCURL */
6042 /* Create new box.
6043 * @context is session context
6044 * @box is box description to create including single primary user (in case of
6045 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6046 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6047 * box, or contact address of PFO box owner)
6048 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6049 * @upper_box_id is optional ID of supper box if currently created box is
6050 * subordinated.
6051 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6052 * @credentials_delivery is NULL if new password should be delivered off-line
6053 * to box owner. It is valid pointer if owner should obtain new password on-line
6054 * on dedicated web server. Then input @credentials_delivery.email value is
6055 * his e-mail address he must provide to dedicated web server together
6056 * with output reallocated @credentials_delivery.token member. Output
6057 * member @credentials_delivery.new_user_name is unused up on this call.
6058 * @approval is optional external approval of box manipulation
6059 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6060 * NULL, if you don't care.*/
6061 isds_error isds_add_box(struct isds_ctx *context,
6062 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6063 const char *former_names, const char *upper_box_id,
6064 const char *ceo_label,
6065 struct isds_credentials_delivery *credentials_delivery,
6066 const struct isds_approval *approval, char **refnumber) {
6067 isds_error err = IE_SUCCESS;
6068 #if HAVE_LIBCURL
6069 xmlNodePtr request = NULL;
6070 xmlDocPtr response = NULL;
6071 xmlXPathContextPtr xpath_ctx = NULL;
6072 xmlXPathObjectPtr result = NULL;
6073 #endif
6076 if (!context) return IE_INVALID_CONTEXT;
6077 zfree(context->long_message);
6078 if (credentials_delivery) {
6079 zfree(credentials_delivery->token);
6080 zfree(credentials_delivery->new_user_name);
6082 if (!box) return IE_INVAL;
6084 #if HAVE_LIBCURL
6085 /* Scratch box ID */
6086 zfree(box->dbID);
6088 /* Build CreateDataBox request */
6089 err = build_CreateDBInput_request(context,
6090 &request, BAD_CAST "CreateDataBox",
6091 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6092 (xmlChar *) ceo_label, credentials_delivery, approval);
6093 if (err) goto leave;
6095 /* Send it to server and process response */
6096 err = send_destroy_request_check_response(context,
6097 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6098 &response, (xmlChar **) refnumber, NULL);
6100 /* Extract box ID */
6101 xpath_ctx = xmlXPathNewContext(response);
6102 if (!xpath_ctx) {
6103 err = IE_ERROR;
6104 goto leave;
6106 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6107 err = IE_ERROR;
6108 goto leave;
6110 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6112 /* Extract optional token */
6113 err = extract_credentials_delivery(context, credentials_delivery, response,
6114 "CreateDataBox");
6116 leave:
6117 xmlXPathFreeObject(result);
6118 xmlXPathFreeContext(xpath_ctx);
6119 xmlFreeDoc(response);
6120 xmlFreeNode(request);
6122 if (!err) {
6123 isds_log(ILF_ISDS, ILL_DEBUG,
6124 _("CreateDataBox request processed by server successfully.\n"));
6126 #else /* not HAVE_LIBCURL */
6127 err = IE_NOTSUP;
6128 #endif
6130 return err;
6134 /* Notify ISDS about new PFO entity.
6135 * This function has no real effect.
6136 * @context is session context
6137 * @box is PFO description including single primary user.
6138 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6139 * @former_names is optional undocumented string. Pass NULL if you don't care.
6140 * @upper_box_id is optional ID of supper box if currently created box is
6141 * subordinated.
6142 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6143 * @approval is optional external approval of box manipulation
6144 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6145 * NULL, if you don't care.*/
6146 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6147 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6148 const char *former_names, const char *upper_box_id,
6149 const char *ceo_label, const struct isds_approval *approval,
6150 char **refnumber) {
6151 isds_error err = IE_SUCCESS;
6152 #if HAVE_LIBCURL
6153 xmlNodePtr request = NULL;
6154 #endif
6156 if (!context) return IE_INVALID_CONTEXT;
6157 zfree(context->long_message);
6158 if (!box) return IE_INVAL;
6160 #if HAVE_LIBCURL
6161 /* Build CreateDataBoxPFOInfo request */
6162 err = build_CreateDBInput_request(context,
6163 &request, BAD_CAST "CreateDataBoxPFOInfo",
6164 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6165 (xmlChar *) ceo_label, NULL, approval);
6166 if (err) goto leave;
6168 /* Send it to server and process response */
6169 err = send_request_check_drop_response(context,
6170 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6171 (xmlChar **) refnumber);
6172 /* XXX: XML Schema names output dbID element but textual documentation
6173 * states no box identifier is returned. */
6174 leave:
6175 xmlFreeNode(request);
6176 #else /* not HAVE_LIBCURL */
6177 err = IE_NOTSUP;
6178 #endif
6179 return err;
6183 /* Common implementation for removing given box.
6184 * @context is session context
6185 * @service_name is UTF-8 encoded name fo ISDS service
6186 * @box is box description to delete
6187 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6188 * carry sane value. If NULL, do not inject this information into request.
6189 * @approval is optional external approval of box manipulation
6190 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6191 * NULL, if you don't care.*/
6192 isds_error _isds_delete_box_common(struct isds_ctx *context,
6193 const xmlChar *service_name,
6194 const struct isds_DbOwnerInfo *box, const struct tm *since,
6195 const struct isds_approval *approval, char **refnumber) {
6196 isds_error err = IE_SUCCESS;
6197 #if HAVE_LIBCURL
6198 xmlNsPtr isds_ns = NULL;
6199 xmlNodePtr request = NULL;
6200 xmlNodePtr node;
6201 xmlChar *string = NULL;
6202 #endif
6205 if (!context) return IE_INVALID_CONTEXT;
6206 zfree(context->long_message);
6207 if (!service_name || !*service_name || !box) return IE_INVAL;
6210 #if HAVE_LIBCURL
6211 /* Build DeleteDataBox(Promptly) request */
6212 request = xmlNewNode(NULL, service_name);
6213 if (!request) {
6214 char *service_name_locale = _isds_utf82locale((char*)service_name);
6215 isds_printf_message(context,
6216 _("Could build %s request"), service_name_locale);
6217 free(service_name_locale);
6218 return IE_ERROR;
6220 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6221 if(!isds_ns) {
6222 isds_log_message(context, _("Could not create ISDS name space"));
6223 xmlFreeNode(request);
6224 return IE_ERROR;
6226 xmlSetNs(request, isds_ns);
6228 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6229 err = insert_DbOwnerInfo(context, box, node);
6230 if (err) goto leave;
6232 if (since) {
6233 err = tm2datestring(since, &string);
6234 if (err) {
6235 isds_log_message(context,
6236 _("Could not convert `since' argument to ISO date string"));
6237 goto leave;
6239 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6240 zfree(string);
6243 err = insert_GExtApproval(context, approval, request);
6244 if (err) goto leave;
6247 /* Send it to server and process response */
6248 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6249 service_name, &request, (xmlChar **) refnumber);
6251 leave:
6252 xmlFreeNode(request);
6253 free(string);
6254 #else /* not HAVE_LIBCURL */
6255 err = IE_NOTSUP;
6256 #endif
6257 return err;
6261 /* Remove given box permanently.
6262 * @context is session context
6263 * @box is box description to delete
6264 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6265 * carry sane value.
6266 * @approval is optional external approval of box manipulation
6267 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6268 * NULL, if you don't care.*/
6269 isds_error isds_delete_box(struct isds_ctx *context,
6270 const struct isds_DbOwnerInfo *box, const struct tm *since,
6271 const struct isds_approval *approval, char **refnumber) {
6272 if (!context) return IE_INVALID_CONTEXT;
6273 zfree(context->long_message);
6274 if (!box || !since) return IE_INVAL;
6276 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6277 box, since, approval, refnumber);
6281 /* Undocumented function.
6282 * @context is session context
6283 * @box is box description to delete
6284 * @approval is optional external approval of box manipulation
6285 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6286 * NULL, if you don't care.*/
6287 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6288 const struct isds_DbOwnerInfo *box,
6289 const struct isds_approval *approval, char **refnumber) {
6290 if (!context) return IE_INVALID_CONTEXT;
6291 zfree(context->long_message);
6292 if (!box) return IE_INVAL;
6294 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6295 box, NULL, approval, refnumber);
6299 /* Update data about given box.
6300 * @context is session context
6301 * @old_box current box description
6302 * @new_box are updated data about @old_box
6303 * @approval is optional external approval of box manipulation
6304 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6305 * NULL, if you don't care.*/
6306 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6307 const struct isds_DbOwnerInfo *old_box,
6308 const struct isds_DbOwnerInfo *new_box,
6309 const struct isds_approval *approval, char **refnumber) {
6310 isds_error err = IE_SUCCESS;
6311 #if HAVE_LIBCURL
6312 xmlNsPtr isds_ns = NULL;
6313 xmlNodePtr request = NULL;
6314 xmlNodePtr node;
6315 #endif
6318 if (!context) return IE_INVALID_CONTEXT;
6319 zfree(context->long_message);
6320 if (!old_box || !new_box) return IE_INVAL;
6323 #if HAVE_LIBCURL
6324 /* Build UpdateDataBoxDescr request */
6325 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6326 if (!request) {
6327 isds_log_message(context,
6328 _("Could build UpdateDataBoxDescr request"));
6329 return IE_ERROR;
6331 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6332 if(!isds_ns) {
6333 isds_log_message(context, _("Could not create ISDS name space"));
6334 xmlFreeNode(request);
6335 return IE_ERROR;
6337 xmlSetNs(request, isds_ns);
6339 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6340 err = insert_DbOwnerInfo(context, old_box, node);
6341 if (err) goto leave;
6343 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6344 err = insert_DbOwnerInfo(context, new_box, node);
6345 if (err) goto leave;
6347 err = insert_GExtApproval(context, approval, request);
6348 if (err) goto leave;
6351 /* Send it to server and process response */
6352 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6353 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6355 leave:
6356 xmlFreeNode(request);
6357 #else /* not HAVE_LIBCURL */
6358 err = IE_NOTSUP;
6359 #endif
6361 return err;
6365 #if HAVE_LIBCURL
6366 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6367 * code
6368 * @context is session context
6369 * @service is SOAP service
6370 * @service_name is name of request in @service
6371 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6372 * @box_id is box ID of interest
6373 * @approval is optional external approval of box manipulation
6374 * @response is server SOAP body response as XML document
6375 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6376 * NULL, if you don't care.
6377 * @return error coded from lower layer, context message will be set up
6378 * appropriately. */
6379 static isds_error build_send_dbid_request_check_response(
6380 struct isds_ctx *context, const isds_service service,
6381 const xmlChar *service_name, const xmlChar *box_id_element,
6382 const xmlChar *box_id, const struct isds_approval *approval,
6383 xmlDocPtr *response, xmlChar **refnumber) {
6385 isds_error err = IE_SUCCESS;
6386 char *service_name_locale = NULL, *box_id_locale = NULL;
6387 xmlNodePtr request = NULL, node;
6388 xmlNsPtr isds_ns = NULL;
6390 if (!context) return IE_INVALID_CONTEXT;
6391 if (!service_name || !box_id) return IE_INVAL;
6392 if (!response) return IE_INVAL;
6394 /* Free output argument */
6395 xmlFreeDoc(*response); *response = NULL;
6397 /* Prepare strings */
6398 service_name_locale = _isds_utf82locale((char*)service_name);
6399 if (!service_name_locale) {
6400 err = IE_NOMEM;
6401 goto leave;
6403 box_id_locale = _isds_utf82locale((char*)box_id);
6404 if (!box_id_locale) {
6405 err = IE_NOMEM;
6406 goto leave;
6409 /* Build request */
6410 request = xmlNewNode(NULL, service_name);
6411 if (!request) {
6412 isds_printf_message(context,
6413 _("Could not build %s request for %s box"), service_name_locale,
6414 box_id_locale);
6415 err = IE_ERROR;
6416 goto leave;
6418 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6419 if(!isds_ns) {
6420 isds_log_message(context, _("Could not create ISDS name space"));
6421 err = IE_ERROR;
6422 goto leave;
6424 xmlSetNs(request, isds_ns);
6426 /* Add XSD:tIdDbInput children */
6427 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6428 INSERT_STRING(request, box_id_element, box_id);
6429 err = insert_GExtApproval(context, approval, request);
6430 if (err) goto leave;
6432 /* Send request and check response*/
6433 err = send_destroy_request_check_response(context,
6434 service, service_name, &request, response, refnumber, NULL);
6436 leave:
6437 free(service_name_locale);
6438 free(box_id_locale);
6439 xmlFreeNode(request);
6440 return err;
6442 #endif /* HAVE_LIBCURL */
6445 /* Get data about all users assigned to given box.
6446 * @context is session context
6447 * @box_id is box ID
6448 * @users is automatically reallocated list of struct isds_DbUserInfo */
6449 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6450 struct isds_list **users) {
6451 isds_error err = IE_SUCCESS;
6452 #if HAVE_LIBCURL
6453 xmlDocPtr response = NULL;
6454 xmlXPathContextPtr xpath_ctx = NULL;
6455 xmlXPathObjectPtr result = NULL;
6456 int i;
6457 struct isds_list *item, *prev_item = NULL;
6458 #endif
6460 if (!context) return IE_INVALID_CONTEXT;
6461 zfree(context->long_message);
6462 if (!users || !box_id) return IE_INVAL;
6463 isds_list_free(users);
6466 #if HAVE_LIBCURL
6467 /* Do request and check for success */
6468 err = build_send_dbid_request_check_response(context,
6469 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6470 BAD_CAST box_id, NULL, &response, NULL);
6471 if (err) goto leave;
6474 /* Extract data */
6475 /* Prepare structure */
6476 xpath_ctx = xmlXPathNewContext(response);
6477 if (!xpath_ctx) {
6478 err = IE_ERROR;
6479 goto leave;
6481 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6482 err = IE_ERROR;
6483 goto leave;
6486 /* Set context node */
6487 result = xmlXPathEvalExpression(BAD_CAST
6488 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6489 xpath_ctx);
6490 if (!result) {
6491 err = IE_ERROR;
6492 goto leave;
6494 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6495 /* Iterate over all users */
6496 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6498 /* Prepare structure */
6499 item = calloc(1, sizeof(*item));
6500 if (!item) {
6501 err = IE_NOMEM;
6502 goto leave;
6504 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6505 if (i == 0) *users = item;
6506 else prev_item->next = item;
6507 prev_item = item;
6509 /* Extract it */
6510 xpath_ctx->node = result->nodesetval->nodeTab[i];
6511 err = extract_DbUserInfo(context,
6512 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6513 if (err) goto leave;
6517 leave:
6518 if (err) {
6519 isds_list_free(users);
6522 xmlXPathFreeObject(result);
6523 xmlXPathFreeContext(xpath_ctx);
6524 xmlFreeDoc(response);
6526 if (!err)
6527 isds_log(ILF_ISDS, ILL_DEBUG,
6528 _("GetDataBoxUsers request processed by server "
6529 "successfully.\n"));
6530 #else /* not HAVE_LIBCURL */
6531 err = IE_NOTSUP;
6532 #endif
6534 return err;
6538 /* Update data about user assigned to given box.
6539 * @context is session context
6540 * @box is box identification
6541 * @old_user identifies user to update
6542 * @new_user are updated data about @old_user
6543 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6544 * NULL, if you don't care.*/
6545 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6546 const struct isds_DbOwnerInfo *box,
6547 const struct isds_DbUserInfo *old_user,
6548 const struct isds_DbUserInfo *new_user,
6549 char **refnumber) {
6550 isds_error err = IE_SUCCESS;
6551 #if HAVE_LIBCURL
6552 xmlNsPtr isds_ns = NULL;
6553 xmlNodePtr request = NULL;
6554 xmlNodePtr node;
6555 #endif
6558 if (!context) return IE_INVALID_CONTEXT;
6559 zfree(context->long_message);
6560 if (!box || !old_user || !new_user) return IE_INVAL;
6563 #if HAVE_LIBCURL
6564 /* Build UpdateDataBoxUser request */
6565 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6566 if (!request) {
6567 isds_log_message(context,
6568 _("Could build UpdateDataBoxUser request"));
6569 return IE_ERROR;
6571 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6572 if(!isds_ns) {
6573 isds_log_message(context, _("Could not create ISDS name space"));
6574 xmlFreeNode(request);
6575 return IE_ERROR;
6577 xmlSetNs(request, isds_ns);
6579 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6580 err = insert_DbOwnerInfo(context, box, node);
6581 if (err) goto leave;
6583 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6584 err = insert_DbUserInfo(context, old_user, node);
6585 if (err) goto leave;
6587 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6588 err = insert_DbUserInfo(context, new_user, node);
6589 if (err) goto leave;
6591 /* Send it to server and process response */
6592 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6593 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6595 leave:
6596 xmlFreeNode(request);
6597 #else /* not HAVE_LIBCURL */
6598 err = IE_NOTSUP;
6599 #endif
6601 return err;
6605 /* Undocumented function.
6606 * @context is session context
6607 * @box_id is UTF-8 encoded box identifier
6608 * @token is UTF-8 encoded temporary password
6609 * @user_id outputs UTF-8 encoded reallocated user identifier
6610 * @password outpus UTF-8 encoded reallocated user password
6611 * Output arguments will be nulled in case of error */
6612 isds_error isds_activate(struct isds_ctx *context,
6613 const char *box_id, const char *token,
6614 char **user_id, char **password) {
6615 isds_error err = IE_SUCCESS;
6616 #if HAVE_LIBCURL
6617 xmlNsPtr isds_ns = NULL;
6618 xmlNodePtr request = NULL, node;
6619 xmlDocPtr response = NULL;
6620 xmlXPathContextPtr xpath_ctx = NULL;
6621 xmlXPathObjectPtr result = NULL;
6622 #endif
6625 if (!context) return IE_INVALID_CONTEXT;
6626 zfree(context->long_message);
6628 if (user_id) zfree(*user_id);
6629 if (password) zfree(*password);
6631 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6634 #if HAVE_LIBCURL
6635 /* Build Activate request */
6636 request = xmlNewNode(NULL, BAD_CAST "Activate");
6637 if (!request) {
6638 isds_log_message(context, _("Could build Activate request"));
6639 return IE_ERROR;
6641 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6642 if(!isds_ns) {
6643 isds_log_message(context, _("Could not create ISDS name space"));
6644 xmlFreeNode(request);
6645 return IE_ERROR;
6647 xmlSetNs(request, isds_ns);
6649 INSERT_STRING(request, "dbAccessDataId", token);
6650 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6651 INSERT_STRING(request, "dbID", box_id);
6654 /* Send request and check response*/
6655 err = send_destroy_request_check_response(context,
6656 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6657 &response, NULL, NULL);
6658 if (err) goto leave;
6661 /* Extract data */
6662 xpath_ctx = xmlXPathNewContext(response);
6663 if (!xpath_ctx) {
6664 err = IE_ERROR;
6665 goto leave;
6667 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6668 err = IE_ERROR;
6669 goto leave;
6671 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6672 xpath_ctx);
6673 if (!result) {
6674 err = IE_ERROR;
6675 goto leave;
6677 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6678 isds_log_message(context, _("Missing ActivateResponse element"));
6679 err = IE_ISDS;
6680 goto leave;
6682 if (result->nodesetval->nodeNr > 1) {
6683 isds_log_message(context, _("Multiple ActivateResponse element"));
6684 err = IE_ISDS;
6685 goto leave;
6687 xpath_ctx->node = result->nodesetval->nodeTab[0];
6688 xmlXPathFreeObject(result); result = NULL;
6690 EXTRACT_STRING("isds:userId", *user_id);
6691 if (!*user_id)
6692 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6693 "but did not return `userId' element.\n"));
6695 EXTRACT_STRING("isds:password", *password);
6696 if (!*password)
6697 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6698 "but did not return `password' element.\n"));
6700 leave:
6701 xmlXPathFreeObject(result);
6702 xmlXPathFreeContext(xpath_ctx);
6703 xmlFreeDoc(response);
6704 xmlFreeNode(request);
6706 if (!err)
6707 isds_log(ILF_ISDS, ILL_DEBUG,
6708 _("Activate request processed by server successfully.\n"));
6709 #else /* not HAVE_LIBCURL */
6710 err = IE_NOTSUP;
6711 #endif
6713 return err;
6717 /* Reset credentials of user assigned to given box.
6718 * @context is session context
6719 * @box is box identification
6720 * @user identifies user to reset password
6721 * @fee_paid is true if fee has been paid, false otherwise
6722 * @approval is optional external approval of box manipulation
6723 * @credentials_delivery is NULL if new password should be delivered off-line
6724 * to the user. It is valid pointer if user should obtain new password on-line
6725 * on dedicated web server. Then input @credentials_delivery.email value is
6726 * user's e-mail address user must provide to dedicated web server together
6727 * with @credentials_delivery.token. The output reallocated token user needs
6728 * to use to authorize on the web server to view his new password. Output
6729 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6730 * ISDS changed up on this call. (No reason why server could change the name
6731 * is known now.)
6732 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6733 * NULL, if you don't care.*/
6734 isds_error isds_reset_password(struct isds_ctx *context,
6735 const struct isds_DbOwnerInfo *box,
6736 const struct isds_DbUserInfo *user,
6737 const _Bool fee_paid, const struct isds_approval *approval,
6738 struct isds_credentials_delivery *credentials_delivery,
6739 char **refnumber) {
6740 isds_error err = IE_SUCCESS;
6741 #if HAVE_LIBCURL
6742 xmlNsPtr isds_ns = NULL;
6743 xmlNodePtr request = NULL, node;
6744 xmlDocPtr response = NULL;
6745 #endif
6748 if (!context) return IE_INVALID_CONTEXT;
6749 zfree(context->long_message);
6751 if (credentials_delivery) {
6752 zfree(credentials_delivery->token);
6753 zfree(credentials_delivery->new_user_name);
6755 if (!box || !user) return IE_INVAL;
6758 #if HAVE_LIBCURL
6759 /* Build NewAccessData request */
6760 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6761 if (!request) {
6762 isds_log_message(context,
6763 _("Could build NewAccessData request"));
6764 return IE_ERROR;
6766 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6767 if(!isds_ns) {
6768 isds_log_message(context, _("Could not create ISDS name space"));
6769 xmlFreeNode(request);
6770 return IE_ERROR;
6772 xmlSetNs(request, isds_ns);
6774 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6775 err = insert_DbOwnerInfo(context, box, node);
6776 if (err) goto leave;
6778 INSERT_ELEMENT(node, request, "dbUserInfo");
6779 err = insert_DbUserInfo(context, user, node);
6780 if (err) goto leave;
6782 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6784 err = insert_credentials_delivery(context, credentials_delivery, request);
6785 if (err) goto leave;
6787 err = insert_GExtApproval(context, approval, request);
6788 if (err) goto leave;
6790 /* Send request and check response*/
6791 err = send_destroy_request_check_response(context,
6792 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6793 &response, (xmlChar **) refnumber, NULL);
6794 if (err) goto leave;
6797 /* Extract optional token */
6798 err = extract_credentials_delivery(context, credentials_delivery,
6799 response, "NewAccessData");
6801 leave:
6802 xmlFreeDoc(response);
6803 xmlFreeNode(request);
6805 if (!err)
6806 isds_log(ILF_ISDS, ILL_DEBUG,
6807 _("NewAccessData request processed by server "
6808 "successfully.\n"));
6809 #else /* not HAVE_LIBCURL */
6810 err = IE_NOTSUP;
6811 #endif
6813 return err;
6817 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6818 * code, destroy response and log success.
6819 * @context is ISDS session context.
6820 * @service_name is name of SERVICE_DB_MANIPULATION service
6821 * @box is box identification
6822 * @user identifies user to remove
6823 * @credentials_delivery is NULL if new user's password should be delivered
6824 * off-line to the user. It is valid pointer if user should obtain new
6825 * password on-line on dedicated web server. Then input
6826 * @credentials_delivery.email value is user's e-mail address user must
6827 * provide to dedicated web server together with @credentials_delivery.token.
6828 * The output reallocated token user needs to use to authorize on the web
6829 * server to view his new password. Output reallocated
6830 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6831 * assingned or changed up on this call.
6832 * @approval is optional external approval of box manipulation
6833 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6834 * NULL, if you don't care. */
6835 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6836 struct isds_ctx *context, const xmlChar *service_name,
6837 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6838 struct isds_credentials_delivery *credentials_delivery,
6839 const struct isds_approval *approval, xmlChar **refnumber) {
6840 isds_error err = IE_SUCCESS;
6841 #if HAVE_LIBCURL
6842 xmlNsPtr isds_ns = NULL;
6843 xmlNodePtr request = NULL, node;
6844 xmlDocPtr response = NULL;
6845 #endif
6848 if (!context) return IE_INVALID_CONTEXT;
6849 zfree(context->long_message);
6850 if (credentials_delivery) {
6851 zfree(credentials_delivery->token);
6852 zfree(credentials_delivery->new_user_name);
6854 if (!service_name || service_name[0] == '\0' || !box || !user)
6855 return IE_INVAL;
6858 #if HAVE_LIBCURL
6859 /* Build NewAccessData or similar request */
6860 request = xmlNewNode(NULL, service_name);
6861 if (!request) {
6862 char *service_name_locale = _isds_utf82locale((char *) service_name);
6863 isds_printf_message(context, _("Could not build %s request"),
6864 service_name_locale);
6865 free(service_name_locale);
6866 return IE_ERROR;
6868 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6869 if(!isds_ns) {
6870 isds_log_message(context, _("Could not create ISDS name space"));
6871 xmlFreeNode(request);
6872 return IE_ERROR;
6874 xmlSetNs(request, isds_ns);
6876 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6877 err = insert_DbOwnerInfo(context, box, node);
6878 if (err) goto leave;
6880 INSERT_ELEMENT(node, request, "dbUserInfo");
6881 err = insert_DbUserInfo(context, user, node);
6882 if (err) goto leave;
6884 err = insert_credentials_delivery(context, credentials_delivery, request);
6885 if (err) goto leave;
6887 err = insert_GExtApproval(context, approval, request);
6888 if (err) goto leave;
6891 /* Send request and check response*/
6892 err = send_destroy_request_check_response(context,
6893 SERVICE_DB_MANIPULATION, service_name, &request, &response,
6894 refnumber, NULL);
6896 xmlFreeNode(request);
6897 request = NULL;
6899 /* Pick up credentials_delivery if requested */
6900 err = extract_credentials_delivery(context, credentials_delivery, response,
6901 (char *)service_name);
6903 leave:
6904 xmlFreeDoc(response);
6905 if (request) xmlFreeNode(request);
6907 if (!err) {
6908 char *service_name_locale = _isds_utf82locale((char *) service_name);
6909 isds_log(ILF_ISDS, ILL_DEBUG,
6910 _("%s request processed by server successfully.\n"),
6911 service_name_locale);
6912 free(service_name_locale);
6914 #else /* not HAVE_LIBCURL */
6915 err = IE_NOTSUP;
6916 #endif
6918 return err;
6922 /* Assign new user to given box.
6923 * @context is session context
6924 * @box is box identification
6925 * @user defines new user to add
6926 * @credentials_delivery is NULL if new user's password should be delivered
6927 * off-line to the user. It is valid pointer if user should obtain new
6928 * password on-line on dedicated web server. Then input
6929 * @credentials_delivery.email value is user's e-mail address user must
6930 * provide to dedicated web server together with @credentials_delivery.token.
6931 * The output reallocated token user needs to use to authorize on the web
6932 * server to view his new password. Output reallocated
6933 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6934 * assingned up on this call.
6935 * @approval is optional external approval of box manipulation
6936 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6937 * NULL, if you don't care.*/
6938 isds_error isds_add_user(struct isds_ctx *context,
6939 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6940 struct isds_credentials_delivery *credentials_delivery,
6941 const struct isds_approval *approval, char **refnumber) {
6942 return build_send_manipulationboxuser_request_check_drop_response(context,
6943 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
6944 approval, (xmlChar **) refnumber);
6948 /* Remove user assigned to given box.
6949 * @context is session context
6950 * @box is box identification
6951 * @user identifies user to remove
6952 * @approval is optional external approval of box manipulation
6953 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6954 * NULL, if you don't care.*/
6955 isds_error isds_delete_user(struct isds_ctx *context,
6956 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6957 const struct isds_approval *approval, char **refnumber) {
6958 return build_send_manipulationboxuser_request_check_drop_response(context,
6959 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
6960 (xmlChar **) refnumber);
6964 /* Get list of boxes in ZIP archive.
6965 * @context is session context
6966 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
6967 * System recognizes following values currently: ALL (all boxes), UPG
6968 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
6969 * receiving commercial messages). This argument is a string because
6970 * specification states new values can appear in the future. Not all list
6971 * types are available to all users.
6972 * @buffer is automatically reallocated memory to store the list of boxes. The
6973 * list is zipped CSV file.
6974 * @buffer_length is size of @buffer data in bytes.
6975 * In case of error @buffer will be freed and @buffer_length will be
6976 * undefined.*/
6977 isds_error isds_get_box_list_archive(struct isds_ctx *context,
6978 const char *list_identifier, void **buffer, size_t *buffer_length) {
6979 isds_error err = IE_SUCCESS;
6980 #if HAVE_LIBCURL
6981 xmlNsPtr isds_ns = NULL;
6982 xmlNodePtr request = NULL, node;
6983 xmlDocPtr response = NULL;
6984 xmlXPathContextPtr xpath_ctx = NULL;
6985 xmlXPathObjectPtr result = NULL;
6986 char *string = NULL;
6987 #endif
6990 if (!context) return IE_INVALID_CONTEXT;
6991 zfree(context->long_message);
6992 if (buffer) zfree(*buffer);
6993 if (!buffer || !buffer_length) return IE_INVAL;
6996 #if HAVE_LIBCURL
6997 /* Check if connection is established
6998 * TODO: This check should be done downstairs. */
6999 if (!context->curl) return IE_CONNECTION_CLOSED;
7002 /* Build AuthenticateMessage request */
7003 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7004 if (!request) {
7005 isds_log_message(context,
7006 _("Could not build GetDataBoxList request"));
7007 return IE_ERROR;
7009 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7010 if(!isds_ns) {
7011 isds_log_message(context, _("Could not create ISDS name space"));
7012 xmlFreeNode(request);
7013 return IE_ERROR;
7015 xmlSetNs(request, isds_ns);
7016 INSERT_STRING(request, "dblType", list_identifier);
7018 /* Send request to server and process response */
7019 err = send_destroy_request_check_response(context,
7020 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7021 &response, NULL, NULL);
7022 if (err) goto leave;
7025 /* Extract Base-64 encoded ZIP file */
7026 xpath_ctx = xmlXPathNewContext(response);
7027 if (!xpath_ctx) {
7028 err = IE_ERROR;
7029 goto leave;
7031 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7032 err = IE_ERROR;
7033 goto leave;
7035 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7037 /* Decode non-empty archive */
7038 if (string && string[0] != '\0') {
7039 *buffer_length = _isds_b64decode(string, buffer);
7040 if (*buffer_length == (size_t) -1) {
7041 isds_printf_message(context,
7042 _("Error while Base64-decoding box list archive"));
7043 err = IE_ERROR;
7044 goto leave;
7049 leave:
7050 free(string);
7051 xmlXPathFreeObject(result);
7052 xmlXPathFreeContext(xpath_ctx);
7053 xmlFreeDoc(response);
7054 xmlFreeNode(request);
7056 if (!err) {
7057 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7058 "processed by server successfully.\n"));
7060 #else /* not HAVE_LIBCURL */
7061 err = IE_NOTSUP;
7062 #endif
7064 return err;
7068 /* Find boxes suiting given criteria.
7069 * @criteria is filter. You should fill in at least some members.
7070 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7071 * possibly empty. Input NULL or valid old structure.
7072 * @return:
7073 * IE_SUCCESS if search succeeded, @boxes contains useful data
7074 * IE_NOEXIST if no such box exists, @boxes will be NULL
7075 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7076 * contains still valid data
7077 * other code if something bad happens. @boxes will be NULL. */
7078 isds_error isds_FindDataBox(struct isds_ctx *context,
7079 const struct isds_DbOwnerInfo *criteria,
7080 struct isds_list **boxes) {
7081 isds_error err = IE_SUCCESS;
7082 #if HAVE_LIBCURL
7083 _Bool truncated = 0;
7084 xmlNsPtr isds_ns = NULL;
7085 xmlNodePtr request = NULL;
7086 xmlDocPtr response = NULL;
7087 xmlChar *code = NULL, *message = NULL;
7088 xmlNodePtr db_owner_info;
7089 xmlXPathContextPtr xpath_ctx = NULL;
7090 xmlXPathObjectPtr result = NULL;
7091 xmlChar *string = NULL;
7092 #endif
7095 if (!context) return IE_INVALID_CONTEXT;
7096 zfree(context->long_message);
7097 if (!boxes) return IE_INVAL;
7098 isds_list_free(boxes);
7100 if (!criteria) {
7101 return IE_INVAL;
7104 #if HAVE_LIBCURL
7105 /* Check if connection is established
7106 * TODO: This check should be done downstairs. */
7107 if (!context->curl) return IE_CONNECTION_CLOSED;
7110 /* Build FindDataBox request */
7111 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
7112 if (!request) {
7113 isds_log_message(context,
7114 _("Could build FindDataBox request"));
7115 return IE_ERROR;
7117 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7118 if(!isds_ns) {
7119 isds_log_message(context, _("Could not create ISDS name space"));
7120 xmlFreeNode(request);
7121 return IE_ERROR;
7123 xmlSetNs(request, isds_ns);
7124 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7125 if (!db_owner_info) {
7126 isds_log_message(context, _("Could not add dbOwnerInfo child to "
7127 "FindDataBox element"));
7128 xmlFreeNode(request);
7129 return IE_ERROR;
7132 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
7133 if (err) goto leave;
7136 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
7138 /* Sent request */
7139 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7141 /* Destroy request */
7142 xmlFreeNode(request); request = NULL;
7144 if (err) {
7145 isds_log(ILF_ISDS, ILL_DEBUG,
7146 _("Processing ISDS response on FindDataBox "
7147 "request failed\n"));
7148 goto leave;
7151 /* Check for response status */
7152 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7153 &code, &message, NULL);
7154 if (err) {
7155 isds_log(ILF_ISDS, ILL_DEBUG,
7156 _("ISDS response on FindDataBox request is missing status\n"));
7157 goto leave;
7160 /* Request processed, but nothing found */
7161 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7162 !xmlStrcmp(code, BAD_CAST "5001")) {
7163 char *code_locale = _isds_utf82locale((char*)code);
7164 char *message_locale = _isds_utf82locale((char*)message);
7165 isds_log(ILF_ISDS, ILL_DEBUG,
7166 _("Server did not found any box on FindDataBox request "
7167 "(code=%s, message=%s)\n"), code_locale, message_locale);
7168 isds_log_message(context, message_locale);
7169 free(code_locale);
7170 free(message_locale);
7171 err = IE_NOEXIST;
7172 goto leave;
7175 /* Warning, not a error */
7176 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7177 char *code_locale = _isds_utf82locale((char*)code);
7178 char *message_locale = _isds_utf82locale((char*)message);
7179 isds_log(ILF_ISDS, ILL_DEBUG,
7180 _("Server truncated response on FindDataBox request "
7181 "(code=%s, message=%s)\n"), code_locale, message_locale);
7182 isds_log_message(context, message_locale);
7183 free(code_locale);
7184 free(message_locale);
7185 truncated = 1;
7188 /* Other error */
7189 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7190 char *code_locale = _isds_utf82locale((char*)code);
7191 char *message_locale = _isds_utf82locale((char*)message);
7192 isds_log(ILF_ISDS, ILL_DEBUG,
7193 _("Server refused FindDataBox request "
7194 "(code=%s, message=%s)\n"), code_locale, message_locale);
7195 isds_log_message(context, message_locale);
7196 free(code_locale);
7197 free(message_locale);
7198 err = IE_ISDS;
7199 goto leave;
7202 xpath_ctx = xmlXPathNewContext(response);
7203 if (!xpath_ctx) {
7204 err = IE_ERROR;
7205 goto leave;
7207 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7208 err = IE_ERROR;
7209 goto leave;
7212 /* Extract boxes if they present */
7213 result = xmlXPathEvalExpression(BAD_CAST
7214 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7215 xpath_ctx);
7216 if (!result) {
7217 err = IE_ERROR;
7218 goto leave;
7220 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7221 struct isds_list *item, *prev_item = NULL;
7222 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7223 item = calloc(1, sizeof(*item));
7224 if (!item) {
7225 err = IE_NOMEM;
7226 goto leave;
7229 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7230 if (i == 0) *boxes = item;
7231 else prev_item->next = item;
7232 prev_item = item;
7234 xpath_ctx->node = result->nodesetval->nodeTab[i];
7235 err = extract_DbOwnerInfo(context,
7236 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7237 if (err) goto leave;
7241 leave:
7242 if (err) {
7243 isds_list_free(boxes);
7244 } else {
7245 if (truncated) err = IE_2BIG;
7248 free(string);
7249 xmlFreeNode(request);
7250 xmlXPathFreeObject(result);
7251 xmlXPathFreeContext(xpath_ctx);
7253 free(code);
7254 free(message);
7255 xmlFreeDoc(response);
7257 if (!err)
7258 isds_log(ILF_ISDS, ILL_DEBUG,
7259 _("FindDataBox request processed by server successfully.\n"));
7260 #else /* not HAVE_LIBCURL */
7261 err = IE_NOTSUP;
7262 #endif
7264 return err;
7268 /* Get status of a box.
7269 * @context is ISDS session context.
7270 * @box_id is UTF-8 encoded box identifier as zero terminated string
7271 * @box_status is return value of box status.
7272 * @return:
7273 * IE_SUCCESS if box has been found and its status retrieved
7274 * IE_NOEXIST if box is not known to ISDS server
7275 * or other appropriate error.
7276 * You can use isds_DbState to enumerate box status. However out of enum
7277 * range value can be returned too. This is feature because ISDS
7278 * specification leaves the set of values open.
7279 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7280 * the box has been deleted, but ISDS still lists its former existence. */
7281 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7282 long int *box_status) {
7283 isds_error err = IE_SUCCESS;
7284 #if HAVE_LIBCURL
7285 xmlNsPtr isds_ns = NULL;
7286 xmlNodePtr request = NULL, db_id;
7287 xmlDocPtr response = NULL;
7288 xmlXPathContextPtr xpath_ctx = NULL;
7289 xmlXPathObjectPtr result = NULL;
7290 xmlChar *string = NULL;
7292 const xmlChar *codes[] = {
7293 BAD_CAST "5001",
7294 BAD_CAST "1007",
7295 BAD_CAST "2011",
7296 NULL
7298 const char *meanings[] = {
7299 "The box does not exist",
7300 "Box ID is malformed",
7301 "Box ID malformed",
7303 const isds_error errors[] = {
7304 IE_NOEXIST,
7305 IE_INVAL,
7306 IE_INVAL,
7308 struct code_map_isds_error map = {
7309 .codes = codes,
7310 .meanings = meanings,
7311 .errors = errors
7313 #endif
7315 if (!context) return IE_INVALID_CONTEXT;
7316 zfree(context->long_message);
7317 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7319 #if HAVE_LIBCURL
7320 /* Check if connection is established
7321 * TODO: This check should be done downstairs. */
7322 if (!context->curl) return IE_CONNECTION_CLOSED;
7325 /* Build CheckDataBox request */
7326 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7327 if (!request) {
7328 isds_log_message(context,
7329 _("Could build CheckDataBox request"));
7330 return IE_ERROR;
7332 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7333 if(!isds_ns) {
7334 isds_log_message(context, _("Could not create ISDS name space"));
7335 xmlFreeNode(request);
7336 return IE_ERROR;
7338 xmlSetNs(request, isds_ns);
7339 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7340 if (!db_id) {
7341 isds_log_message(context, _("Could not add dbID child to "
7342 "CheckDataBox element"));
7343 xmlFreeNode(request);
7344 return IE_ERROR;
7348 /* Send request and check response*/
7349 err = send_destroy_request_check_response(context,
7350 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7351 &request, &response, NULL, &map);
7352 if (err) goto leave;
7355 /* Extract data */
7356 xpath_ctx = xmlXPathNewContext(response);
7357 if (!xpath_ctx) {
7358 err = IE_ERROR;
7359 goto leave;
7361 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7362 err = IE_ERROR;
7363 goto leave;
7365 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7366 xpath_ctx);
7367 if (!result) {
7368 err = IE_ERROR;
7369 goto leave;
7371 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7372 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7373 err = IE_ISDS;
7374 goto leave;
7376 if (result->nodesetval->nodeNr > 1) {
7377 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7378 err = IE_ISDS;
7379 goto leave;
7381 xpath_ctx->node = result->nodesetval->nodeTab[0];
7382 xmlXPathFreeObject(result); result = NULL;
7384 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7387 leave:
7388 free(string);
7389 xmlXPathFreeObject(result);
7390 xmlXPathFreeContext(xpath_ctx);
7392 xmlFreeDoc(response);
7394 if (!err)
7395 isds_log(ILF_ISDS, ILL_DEBUG,
7396 _("CheckDataBox request processed by server successfully.\n"));
7397 #else /* not HAVE_LIBCURL */
7398 err = IE_NOTSUP;
7399 #endif
7401 return err;
7405 /* Get list of permissions to send commercial messages.
7406 * @context is ISDS session context.
7407 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7408 * @permissions is a reallocated list of permissions (struct
7409 * isds_commercial_permission*) to send commercial messages from @box_id. The
7410 * order of permissions is significant as the server applies the permissions
7411 * and associated pre-paid credits in the order. Empty list means no
7412 * permission.
7413 * @return:
7414 * IE_SUCCESS if the list has been obtained correctly,
7415 * or other appropriate error. */
7416 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7417 const char *box_id, struct isds_list **permissions) {
7418 isds_error err = IE_SUCCESS;
7419 #if HAVE_LIBCURL
7420 xmlDocPtr response = NULL;
7421 xmlXPathContextPtr xpath_ctx = NULL;
7422 xmlXPathObjectPtr result = NULL;
7423 #endif
7425 if (!context) return IE_INVALID_CONTEXT;
7426 zfree(context->long_message);
7427 if (NULL == permissions) return IE_INVAL;
7428 isds_list_free(permissions);
7429 if (NULL == box_id) return IE_INVAL;
7431 #if HAVE_LIBCURL
7432 /* Check if connection is established */
7433 if (!context->curl) return IE_CONNECTION_CLOSED;
7435 /* Do request and check for success */
7436 err = build_send_dbid_request_check_response(context,
7437 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7438 BAD_CAST box_id, NULL, &response, NULL);
7439 if (!err) {
7440 isds_log(ILF_ISDS, ILL_DEBUG,
7441 _("PDZInfo request processed by server successfully.\n"));
7444 /* Extract data */
7445 /* Prepare structure */
7446 xpath_ctx = xmlXPathNewContext(response);
7447 if (!xpath_ctx) {
7448 err = IE_ERROR;
7449 goto leave;
7451 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7452 err = IE_ERROR;
7453 goto leave;
7456 /* Set context node */
7457 result = xmlXPathEvalExpression(BAD_CAST
7458 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7459 xpath_ctx);
7460 if (!result) {
7461 err = IE_ERROR;
7462 goto leave;
7464 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7465 struct isds_list *prev_item = NULL;
7467 /* Iterate over all permission records */
7468 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7469 struct isds_list *item;
7471 /* Prepare structure */
7472 item = calloc(1, sizeof(*item));
7473 if (!item) {
7474 err = IE_NOMEM;
7475 goto leave;
7477 item->destructor = (void(*)(void**))isds_commercial_permission_free;
7478 if (i == 0) *permissions = item;
7479 else prev_item->next = item;
7480 prev_item = item;
7482 /* Extract it */
7483 xpath_ctx->node = result->nodesetval->nodeTab[i];
7484 err = extract_DbPDZRecord(context,
7485 (struct isds_commercial_permission **) (&item->data),
7486 xpath_ctx);
7487 if (err) goto leave;
7491 leave:
7492 if (err) {
7493 isds_list_free(permissions);
7496 xmlXPathFreeObject(result);
7497 xmlXPathFreeContext(xpath_ctx);
7498 xmlFreeDoc(response);
7500 #else /* not HAVE_LIBCURL */
7501 err = IE_NOTSUP;
7502 #endif
7504 return err;
7508 /* Get details about credit for sending pre-paid commercial messages.
7509 * @context is ISDS session context.
7510 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
7511 * @from_date is first day of credit history to return in @history. Only
7512 * tm_year, tm_mon and tm_mday carry sane value.
7513 * @to_date is last day of credit history to return in @history. Only
7514 * tm_year, tm_mon and tm_mday carry sane value.
7515 * @credit outputs current credit value into pre-allocated memory. Pass NULL
7516 * if you don't care. This and all other credit values are integers in
7517 * hundredths of Czech Crowns.
7518 * @email outputs notification e-mail address where notifications about credit
7519 * are sent. This is automatically reallocated string. Pass NULL if you don't
7520 * care. It can return NULL if no address is defined.
7521 * @history outputs auto-reallocated list of pointers to struct
7522 * isds_credit_event. Events in closed interval @from_time to @to_time are
7523 * returned. Pass NULL @to_time and @from_time if you don't care. The events
7524 * are sorted by time.
7525 * @return:
7526 * IE_SUCCESS if the credit details have been obtained correctly,
7527 * or other appropriate error. Please note that server allows to retrieve
7528 * only limited history of events. */
7529 isds_error isds_get_commercial_credit(struct isds_ctx *context,
7530 const char *box_id,
7531 const struct tm *from_date, const struct tm *to_date,
7532 long int *credit, char **email, struct isds_list **history) {
7533 isds_error err = IE_SUCCESS;
7534 #if HAVE_LIBCURL
7535 char *box_id_locale = NULL;
7536 xmlNodePtr request = NULL, node;
7537 xmlNsPtr isds_ns = NULL;
7538 xmlChar *string = NULL;
7540 xmlDocPtr response = NULL;
7541 xmlXPathContextPtr xpath_ctx = NULL;
7542 xmlXPathObjectPtr result = NULL;
7544 const xmlChar *codes[] = {
7545 BAD_CAST "1004",
7546 BAD_CAST "2011",
7547 BAD_CAST "1093",
7548 BAD_CAST "1137",
7549 BAD_CAST "1058",
7550 NULL
7552 const char *meanings[] = {
7553 "Insufficient priviledges for the box",
7554 "The box does not exist",
7555 "Date is too long (history is not available after 15 months)",
7556 "Interval is too long (limit is 3 months)",
7557 "Invalid date"
7559 const isds_error errors[] = {
7560 IE_ISDS,
7561 IE_NOEXIST,
7562 IE_DATE,
7563 IE_DATE,
7564 IE_DATE,
7566 struct code_map_isds_error map = {
7567 .codes = codes,
7568 .meanings = meanings,
7569 .errors = errors
7571 #endif
7573 if (!context) return IE_INVALID_CONTEXT;
7574 zfree(context->long_message);
7576 /* Free output argument */
7577 if (NULL != credit) *credit = 0;
7578 if (NULL != email) zfree(*email);
7579 isds_list_free(history);
7581 if (NULL == box_id) return IE_INVAL;
7583 #if HAVE_LIBCURL
7584 /* Check if connection is established */
7585 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7587 box_id_locale = _isds_utf82locale((char*)box_id);
7588 if (NULL == box_id_locale) {
7589 err = IE_NOMEM;
7590 goto leave;
7593 /* Build request */
7594 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
7595 if (NULL == request) {
7596 isds_printf_message(context,
7597 _("Could not build DataBoxCreditInfo request for %s box"),
7598 box_id_locale);
7599 err = IE_ERROR;
7600 goto leave;
7602 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7603 if(!isds_ns) {
7604 isds_log_message(context, _("Could not create ISDS name space"));
7605 err = IE_ERROR;
7606 goto leave;
7608 xmlSetNs(request, isds_ns);
7610 /* Add mandatory XSD:tIdDbInput child */
7611 INSERT_STRING(request, BAD_CAST "dbID", box_id);
7612 /* Add mandatory dates elements with optional values */
7613 if (from_date) {
7614 err = tm2datestring(from_date, &string);
7615 if (err) {
7616 isds_log_message(context,
7617 _("Could not convert `from_date' argument to ISO date "
7618 "string"));
7619 goto leave;
7621 INSERT_STRING(request, "ciFromDate", string);
7622 zfree(string);
7623 } else {
7624 INSERT_STRING(request, "ciFromDate", NULL);
7626 if (to_date) {
7627 err = tm2datestring(to_date, &string);
7628 if (err) {
7629 isds_log_message(context,
7630 _("Could not convert `to_date' argument to ISO date "
7631 "string"));
7632 goto leave;
7634 INSERT_STRING(request, "ciTodate", string);
7635 zfree(string);
7636 } else {
7637 INSERT_STRING(request, "ciTodate", NULL);
7640 /* Send request and check response*/
7641 err = send_destroy_request_check_response(context,
7642 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
7643 &request, &response, NULL, &map);
7644 if (err) goto leave;
7647 /* Extract data */
7648 /* Set context to the root */
7649 xpath_ctx = xmlXPathNewContext(response);
7650 if (!xpath_ctx) {
7651 err = IE_ERROR;
7652 goto leave;
7654 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7655 err = IE_ERROR;
7656 goto leave;
7658 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
7659 xpath_ctx);
7660 if (!result) {
7661 err = IE_ERROR;
7662 goto leave;
7664 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7665 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
7666 err = IE_ISDS;
7667 goto leave;
7669 if (result->nodesetval->nodeNr > 1) {
7670 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
7671 err = IE_ISDS;
7672 goto leave;
7674 xpath_ctx->node = result->nodesetval->nodeTab[0];
7675 xmlXPathFreeObject(result); result = NULL;
7677 /* Extract common data */
7678 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
7679 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
7681 /* Extract records */
7682 if (NULL == history) goto leave;
7683 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
7684 xpath_ctx);
7685 if (!result) {
7686 err = IE_ERROR;
7687 goto leave;
7689 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7690 struct isds_list *prev_item = NULL;
7692 /* Iterate over all records */
7693 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7694 struct isds_list *item;
7696 /* Prepare structure */
7697 item = calloc(1, sizeof(*item));
7698 if (!item) {
7699 err = IE_NOMEM;
7700 goto leave;
7702 item->destructor = (void(*)(void**))isds_credit_event_free;
7703 if (i == 0) *history = item;
7704 else prev_item->next = item;
7705 prev_item = item;
7707 /* Extract it */
7708 xpath_ctx->node = result->nodesetval->nodeTab[i];
7709 err = extract_CiRecord(context,
7710 (struct isds_credit_event **) (&item->data),
7711 xpath_ctx);
7712 if (err) goto leave;
7716 leave:
7717 if (!err) {
7718 isds_log(ILF_ISDS, ILL_DEBUG,
7719 _("DataBoxCreditInfo request processed by server successfully.\n"));
7721 if (err) {
7722 isds_list_free(history);
7723 if (NULL != email) zfree(*email)
7726 free(box_id_locale);
7727 xmlXPathFreeObject(result);
7728 xmlXPathFreeContext(xpath_ctx);
7729 xmlFreeDoc(response);
7731 #else /* not HAVE_LIBCURL */
7732 err = IE_NOTSUP;
7733 #endif
7735 return err;
7739 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
7740 * code, destroy response and log success.
7741 * @context is ISDS session context.
7742 * @service_name is name of SERVICE_DB_MANIPULATION service
7743 * @box_id is UTF-8 encoded box identifier as zero terminated string
7744 * @approval is optional external approval of box manipulation
7745 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7746 * NULL, if you don't care. */
7747 static isds_error build_send_manipulationdbid_request_check_drop_response(
7748 struct isds_ctx *context, const xmlChar *service_name,
7749 const xmlChar *box_id, const struct isds_approval *approval,
7750 xmlChar **refnumber) {
7751 isds_error err = IE_SUCCESS;
7752 #if HAVE_LIBCURL
7753 xmlDocPtr response = NULL;
7754 #endif
7756 if (!context) return IE_INVALID_CONTEXT;
7757 zfree(context->long_message);
7758 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
7760 #if HAVE_LIBCURL
7761 /* Check if connection is established */
7762 if (!context->curl) return IE_CONNECTION_CLOSED;
7764 /* Do request and check for success */
7765 err = build_send_dbid_request_check_response(context,
7766 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
7767 &response, refnumber);
7768 xmlFreeDoc(response);
7770 if (!err) {
7771 char *service_name_locale = _isds_utf82locale((char *) service_name);
7772 isds_log(ILF_ISDS, ILL_DEBUG,
7773 _("%s request processed by server successfully.\n"),
7774 service_name_locale);
7775 free(service_name_locale);
7777 #else /* not HAVE_LIBCURL */
7778 err = IE_NOTSUP;
7779 #endif
7781 return err;
7785 /* Switch box into state where box can receive commercial messages (off by
7786 * default)
7787 * @context is ISDS session context.
7788 * @box_id is UTF-8 encoded box identifier as zero terminated string
7789 * @allow is true for enable, false for disable commercial messages income
7790 * @approval is optional external approval of box manipulation
7791 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7792 * NULL, if you don't care. */
7793 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
7794 const char *box_id, const _Bool allow,
7795 const struct isds_approval *approval, char **refnumber) {
7796 return build_send_manipulationdbid_request_check_drop_response(context,
7797 (allow) ? BAD_CAST "SetOpenAddressing" :
7798 BAD_CAST "ClearOpenAddressing",
7799 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7803 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
7804 * message acceptance). This is just a box permission. Sender must apply
7805 * such role by sending each message.
7806 * @context is ISDS session context.
7807 * @box_id is UTF-8 encoded box identifier as zero terminated string
7808 * @allow is true for enable, false for disable OVM role permission
7809 * @approval is optional external approval of box manipulation
7810 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7811 * NULL, if you don't care. */
7812 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
7813 const char *box_id, const _Bool allow,
7814 const struct isds_approval *approval, char **refnumber) {
7815 return build_send_manipulationdbid_request_check_drop_response(context,
7816 (allow) ? BAD_CAST "SetEffectiveOVM" :
7817 BAD_CAST "ClearEffectiveOVM",
7818 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7822 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
7823 * code, destroy response and log success.
7824 * @context is ISDS session context.
7825 * @service_name is name of SERVICE_DB_MANIPULATION service
7826 * @owner is structure describing box
7827 * @approval is optional external approval of box manipulation
7828 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7829 * NULL, if you don't care. */
7830 static isds_error build_send_manipulationdbowner_request_check_drop_response(
7831 struct isds_ctx *context, const xmlChar *service_name,
7832 const struct isds_DbOwnerInfo *owner,
7833 const struct isds_approval *approval, xmlChar **refnumber) {
7834 isds_error err = IE_SUCCESS;
7835 #if HAVE_LIBCURL
7836 char *service_name_locale = NULL;
7837 xmlNodePtr request = NULL, db_owner_info;
7838 xmlNsPtr isds_ns = NULL;
7839 #endif
7842 if (!context) return IE_INVALID_CONTEXT;
7843 zfree(context->long_message);
7844 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
7846 #if HAVE_LIBCURL
7847 service_name_locale = _isds_utf82locale((char*)service_name);
7848 if (!service_name_locale) {
7849 err = IE_NOMEM;
7850 goto leave;
7853 /* Build request */
7854 request = xmlNewNode(NULL, service_name);
7855 if (!request) {
7856 isds_printf_message(context,
7857 _("Could not build %s request"), service_name_locale);
7858 err = IE_ERROR;
7859 goto leave;
7861 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7862 if(!isds_ns) {
7863 isds_log_message(context, _("Could not create ISDS name space"));
7864 err = IE_ERROR;
7865 goto leave;
7867 xmlSetNs(request, isds_ns);
7870 /* Add XSD:tOwnerInfoInput child*/
7871 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
7872 err = insert_DbOwnerInfo(context, owner, db_owner_info);
7873 if (err) goto leave;
7875 /* Add XSD:gExtApproval*/
7876 err = insert_GExtApproval(context, approval, request);
7877 if (err) goto leave;
7879 /* Send it to server and process response */
7880 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7881 service_name, &request, refnumber);
7883 leave:
7884 xmlFreeNode(request);
7885 free(service_name_locale);
7886 #else /* not HAVE_LIBCURL */
7887 err = IE_NOTSUP;
7888 #endif
7890 return err;
7894 /* Switch box accessibility state on request of box owner.
7895 * Despite the name, owner must do the request off-line. This function is
7896 * designed for such off-line meeting points (e.g. Czech POINT).
7897 * @context is ISDS session context.
7898 * @box identifies box to switch accessibility state.
7899 * @allow is true for making accessible, false to disallow access.
7900 * @approval is optional external approval of box manipulation
7901 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7902 * NULL, if you don't care. */
7903 isds_error isds_switch_box_accessibility_on_owner_request(
7904 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7905 const _Bool allow, const struct isds_approval *approval,
7906 char **refnumber) {
7907 return build_send_manipulationdbowner_request_check_drop_response(context,
7908 (allow) ? BAD_CAST "EnableOwnDataBox" :
7909 BAD_CAST "DisableOwnDataBox",
7910 box, approval, (xmlChar **) refnumber);
7914 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
7915 * date.
7916 * @context is ISDS session context.
7917 * @box identifies box to switch accessibility state.
7918 * @since is date since accessibility has been denied. This can be past too.
7919 * Only tm_year, tm_mon and tm_mday carry sane value.
7920 * @approval is optional external approval of box manipulation
7921 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7922 * NULL, if you don't care. */
7923 isds_error isds_disable_box_accessibility_externaly(
7924 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7925 const struct tm *since, const struct isds_approval *approval,
7926 char **refnumber) {
7927 isds_error err = IE_SUCCESS;
7928 #if HAVE_LIBCURL
7929 char *service_name_locale = NULL;
7930 xmlNodePtr request = NULL, node;
7931 xmlNsPtr isds_ns = NULL;
7932 xmlChar *string = NULL;
7933 #endif
7936 if (!context) return IE_INVALID_CONTEXT;
7937 zfree(context->long_message);
7938 if (!box || !since) return IE_INVAL;
7940 #if HAVE_LIBCURL
7941 /* Build request */
7942 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
7943 if (!request) {
7944 isds_printf_message(context,
7945 _("Could not build %s request"), "DisableDataBoxExternally");
7946 err = IE_ERROR;
7947 goto leave;
7949 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7950 if(!isds_ns) {
7951 isds_log_message(context, _("Could not create ISDS name space"));
7952 err = IE_ERROR;
7953 goto leave;
7955 xmlSetNs(request, isds_ns);
7958 /* Add @box identification */
7959 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7960 err = insert_DbOwnerInfo(context, box, node);
7961 if (err) goto leave;
7963 /* Add @since date */
7964 err = tm2datestring(since, &string);
7965 if(err) {
7966 isds_log_message(context,
7967 _("Could not convert `since' argument to ISO date string"));
7968 goto leave;
7970 INSERT_STRING(request, "dbOwnerDisableDate", string);
7971 zfree(string);
7973 /* Add @approval */
7974 err = insert_GExtApproval(context, approval, request);
7975 if (err) goto leave;
7977 /* Send it to server and process response */
7978 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7979 BAD_CAST "DisableDataBoxExternally", &request,
7980 (xmlChar **) refnumber);
7982 leave:
7983 free(string);
7984 xmlFreeNode(request);
7985 free(service_name_locale);
7986 #else /* not HAVE_LIBCURL */
7987 err = IE_NOTSUP;
7988 #endif
7990 return err;
7994 #if HAVE_LIBCURL
7995 /* Insert struct isds_message data (envelope (recipient data optional) and
7996 * documents into XML tree
7997 * @context is session context
7998 * @outgoing_message is libisds structure with message data
7999 * @create_message is XML CreateMessage or CreateMultipleMessage element
8000 * @process_recipient true for recipient data serialization, false for no
8001 * serialization */
8002 static isds_error insert_envelope_files(struct isds_ctx *context,
8003 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8004 const _Bool process_recipient) {
8006 isds_error err = IE_SUCCESS;
8007 xmlNodePtr envelope, dm_files, node;
8008 xmlChar *string = NULL;
8010 if (!context) return IE_INVALID_CONTEXT;
8011 if (!outgoing_message || !create_message) return IE_INVAL;
8014 /* Build envelope */
8015 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8016 if (!envelope) {
8017 isds_printf_message(context, _("Could not add dmEnvelope child to "
8018 "%s element"), create_message->name);
8019 return IE_ERROR;
8022 if (!outgoing_message->envelope) {
8023 isds_log_message(context, _("Outgoing message is missing envelope"));
8024 err = IE_INVAL;
8025 goto leave;
8028 /* Insert optional message type */
8029 err = insert_message_type(context, outgoing_message->envelope->dmType,
8030 envelope);
8031 if (err) goto leave;
8033 INSERT_STRING(envelope, "dmSenderOrgUnit",
8034 outgoing_message->envelope->dmSenderOrgUnit);
8035 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8036 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8038 if (process_recipient) {
8039 if (!outgoing_message->envelope->dbIDRecipient) {
8040 isds_log_message(context,
8041 _("Outgoing message is missing recipient box identifier"));
8042 err = IE_INVAL;
8043 goto leave;
8045 INSERT_STRING(envelope, "dbIDRecipient",
8046 outgoing_message->envelope->dbIDRecipient);
8048 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8049 outgoing_message->envelope->dmRecipientOrgUnit);
8050 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8051 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8052 INSERT_STRING(envelope, "dmToHands",
8053 outgoing_message->envelope->dmToHands);
8056 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8057 "dmAnnotation");
8058 INSERT_STRING(envelope, "dmAnnotation",
8059 outgoing_message->envelope->dmAnnotation);
8061 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8062 0, 50, "dmRecipientRefNumber");
8063 INSERT_STRING(envelope, "dmRecipientRefNumber",
8064 outgoing_message->envelope->dmRecipientRefNumber);
8066 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8067 0, 50, "dmSenderRefNumber");
8068 INSERT_STRING(envelope, "dmSenderRefNumber",
8069 outgoing_message->envelope->dmSenderRefNumber);
8071 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8072 0, 50, "dmRecipientIdent");
8073 INSERT_STRING(envelope, "dmRecipientIdent",
8074 outgoing_message->envelope->dmRecipientIdent);
8076 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8077 0, 50, "dmSenderIdent");
8078 INSERT_STRING(envelope, "dmSenderIdent",
8079 outgoing_message->envelope->dmSenderIdent);
8081 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8082 outgoing_message->envelope->dmLegalTitleLaw, string);
8083 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8084 outgoing_message->envelope->dmLegalTitleYear, string);
8085 INSERT_STRING(envelope, "dmLegalTitleSect",
8086 outgoing_message->envelope->dmLegalTitleSect);
8087 INSERT_STRING(envelope, "dmLegalTitlePar",
8088 outgoing_message->envelope->dmLegalTitlePar);
8089 INSERT_STRING(envelope, "dmLegalTitlePoint",
8090 outgoing_message->envelope->dmLegalTitlePoint);
8092 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8093 outgoing_message->envelope->dmPersonalDelivery);
8094 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8095 outgoing_message->envelope->dmAllowSubstDelivery);
8097 /* ???: Should we require value for dbEffectiveOVM sender?
8098 * ISDS has default as true */
8099 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8100 INSERT_BOOLEAN(envelope, "dmOVM",
8101 outgoing_message->envelope->dmPublishOwnID);
8104 /* Append dmFiles */
8105 if (!outgoing_message->documents) {
8106 isds_log_message(context,
8107 _("Outgoing message is missing list of documents"));
8108 err = IE_INVAL;
8109 goto leave;
8111 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8112 if (!dm_files) {
8113 isds_printf_message(context, _("Could not add dmFiles child to "
8114 "%s element"), create_message->name);
8115 err = IE_ERROR;
8116 goto leave;
8119 /* Check for document hierarchy */
8120 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8121 if (err) goto leave;
8123 /* Process each document */
8124 for (struct isds_list *item =
8125 (struct isds_list *) outgoing_message->documents;
8126 item; item = item->next) {
8127 if (!item->data) {
8128 isds_log_message(context,
8129 _("List of documents contains empty item"));
8130 err = IE_INVAL;
8131 goto leave;
8133 /* FIXME: Check for dmFileMetaType and for document references.
8134 * Only first document can be of MAIN type */
8135 err = insert_document(context, (struct isds_document*) item->data,
8136 dm_files);
8138 if (err) goto leave;
8141 leave:
8142 free(string);
8143 return err;
8145 #endif /* HAVE_LIBCURL */
8148 /* Send a message via ISDS to a recipient
8149 * @context is session context
8150 * @outgoing_message is message to send; Some members are mandatory (like
8151 * dbIDRecipient), some are optional and some are irrelevant (especially data
8152 * about sender). Included pointer to isds_list documents must contain at
8153 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8154 * members will be filled with valid data from ISDS. Exact list of write
8155 * members is subject to change. Currently dmID is changed.
8156 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8157 isds_error isds_send_message(struct isds_ctx *context,
8158 struct isds_message *outgoing_message) {
8160 isds_error err = IE_SUCCESS;
8161 #if HAVE_LIBCURL
8162 xmlNsPtr isds_ns = NULL;
8163 xmlNodePtr request = NULL;
8164 xmlDocPtr response = NULL;
8165 xmlChar *code = NULL, *message = NULL;
8166 xmlXPathContextPtr xpath_ctx = NULL;
8167 xmlXPathObjectPtr result = NULL;
8168 /*_Bool message_is_complete = 0;*/
8169 #endif
8171 if (!context) return IE_INVALID_CONTEXT;
8172 zfree(context->long_message);
8173 if (!outgoing_message) return IE_INVAL;
8175 #if HAVE_LIBCURL
8176 /* Check if connection is established
8177 * TODO: This check should be done downstairs. */
8178 if (!context->curl) return IE_CONNECTION_CLOSED;
8181 /* Build CreateMessage request */
8182 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8183 if (!request) {
8184 isds_log_message(context,
8185 _("Could not build CreateMessage request"));
8186 return IE_ERROR;
8188 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8189 if(!isds_ns) {
8190 isds_log_message(context, _("Could not create ISDS name space"));
8191 xmlFreeNode(request);
8192 return IE_ERROR;
8194 xmlSetNs(request, isds_ns);
8196 /* Append envelope and files */
8197 err = insert_envelope_files(context, outgoing_message, request, 1);
8198 if (err) goto leave;
8201 /* Signal we can serialize message since now */
8202 /*message_is_complete = 1;*/
8205 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8207 /* Sent request */
8208 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8210 /* Don't' destroy request, we want to provide it to application later */
8212 if (err) {
8213 isds_log(ILF_ISDS, ILL_DEBUG,
8214 _("Processing ISDS response on CreateMessage "
8215 "request failed\n"));
8216 goto leave;
8219 /* Check for response status */
8220 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8221 &code, &message, NULL);
8222 if (err) {
8223 isds_log(ILF_ISDS, ILL_DEBUG,
8224 _("ISDS response on CreateMessage request "
8225 "is missing status\n"));
8226 goto leave;
8229 /* Request processed, but refused by server or server failed */
8230 if (xmlStrcmp(code, BAD_CAST "0000")) {
8231 char *box_id_locale =
8232 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8233 char *code_locale = _isds_utf82locale((char*)code);
8234 char *message_locale = _isds_utf82locale((char*)message);
8235 isds_log(ILF_ISDS, ILL_DEBUG,
8236 _("Server did not accept message for %s on CreateMessage "
8237 "request (code=%s, message=%s)\n"),
8238 box_id_locale, code_locale, message_locale);
8239 isds_log_message(context, message_locale);
8240 free(box_id_locale);
8241 free(code_locale);
8242 free(message_locale);
8243 err = IE_ISDS;
8244 goto leave;
8248 /* Extract data */
8249 xpath_ctx = xmlXPathNewContext(response);
8250 if (!xpath_ctx) {
8251 err = IE_ERROR;
8252 goto leave;
8254 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8255 err = IE_ERROR;
8256 goto leave;
8258 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8259 xpath_ctx);
8260 if (!result) {
8261 err = IE_ERROR;
8262 goto leave;
8264 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8265 isds_log_message(context, _("Missing CreateMessageResponse element"));
8266 err = IE_ISDS;
8267 goto leave;
8269 if (result->nodesetval->nodeNr > 1) {
8270 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8271 err = IE_ISDS;
8272 goto leave;
8274 xpath_ctx->node = result->nodesetval->nodeTab[0];
8275 xmlXPathFreeObject(result); result = NULL;
8277 if (outgoing_message->envelope->dmID) {
8278 free(outgoing_message->envelope->dmID);
8279 outgoing_message->envelope->dmID = NULL;
8281 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8282 if (!outgoing_message->envelope->dmID) {
8283 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8284 "but did not return assigned message ID\n"));
8287 leave:
8288 /* TODO: Serialize message into structure member raw */
8289 /* XXX: Each web service transport message in different format.
8290 * Therefore it's not possible to save them directly.
8291 * To save them, one must figure out common format.
8292 * We can leave it on application, or we can implement the ESS format. */
8293 /*if (message_is_complete) {
8294 if (outgoing_message->envelope->dmID) {
8296 /* Add assigned message ID as first child*/
8297 /*xmlNodePtr dmid_text = xmlNewText(
8298 (xmlChar *) outgoing_message->envelope->dmID);
8299 if (!dmid_text) goto serialization_failed;
8301 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8302 BAD_CAST "dmID");
8303 if (!dmid_element) {
8304 xmlFreeNode(dmid_text);
8305 goto serialization_failed;
8308 xmlNodePtr dmid_element_with_text =
8309 xmlAddChild(dmid_element, dmid_text);
8310 if (!dmid_element_with_text) {
8311 xmlFreeNode(dmid_element);
8312 xmlFreeNode(dmid_text);
8313 goto serialization_failed;
8316 node = xmlAddPrevSibling(envelope->childern,
8317 dmid_element_with_text);
8318 if (!node) {
8319 xmlFreeNodeList(dmid_element_with_text);
8320 goto serialization_failed;
8324 /* Serialize message with ID into raw */
8325 /*buffer = serialize_element(envelope)*/
8326 /* }
8328 serialization_failed:
8332 /* Clean up */
8333 xmlXPathFreeObject(result);
8334 xmlXPathFreeContext(xpath_ctx);
8336 free(code);
8337 free(message);
8338 xmlFreeDoc(response);
8339 xmlFreeNode(request);
8341 if (!err)
8342 isds_log(ILF_ISDS, ILL_DEBUG,
8343 _("CreateMessage request processed by server "
8344 "successfully.\n"));
8345 #else /* not HAVE_LIBCURL */
8346 err = IE_NOTSUP;
8347 #endif
8349 return err;
8353 /* Send a message via ISDS to a multiple recipients
8354 * @context is session context
8355 * @outgoing_message is message to send; Some members are mandatory,
8356 * some are optional and some are irrelevant (especially data
8357 * about sender). Data about recipient will be substituted by ISDS from
8358 * @copies. Included pointer to isds_list documents must
8359 * contain at least one document of FILEMETATYPE_MAIN.
8360 * @copies is list of isds_message_copy structures addressing all desired
8361 * recipients. This is read-write structure, some members will be filled with
8362 * valid data from ISDS (message IDs, error codes, error descriptions).
8363 * @return
8364 * ISDS_SUCCESS if all messages have been sent
8365 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8366 * succeeded messages can be identified by copies->data->error),
8367 * or other error code if something other goes wrong. */
8368 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8369 const struct isds_message *outgoing_message,
8370 struct isds_list *copies) {
8372 isds_error err = IE_SUCCESS;
8373 #if HAVE_LIBCURL
8374 isds_error append_err;
8375 xmlNsPtr isds_ns = NULL;
8376 xmlNodePtr request = NULL, recipients, recipient, node;
8377 struct isds_list *item;
8378 struct isds_message_copy *copy;
8379 xmlDocPtr response = NULL;
8380 xmlChar *code = NULL, *message = NULL;
8381 xmlXPathContextPtr xpath_ctx = NULL;
8382 xmlXPathObjectPtr result = NULL;
8383 xmlChar *string = NULL;
8384 int i;
8385 #endif
8387 if (!context) return IE_INVALID_CONTEXT;
8388 zfree(context->long_message);
8389 if (!outgoing_message || !copies) return IE_INVAL;
8391 #if HAVE_LIBCURL
8392 /* Check if connection is established
8393 * TODO: This check should be done downstairs. */
8394 if (!context->curl) return IE_CONNECTION_CLOSED;
8397 /* Build CreateMultipleMessage request */
8398 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
8399 if (!request) {
8400 isds_log_message(context,
8401 _("Could not build CreateMultipleMessage request"));
8402 return IE_ERROR;
8404 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8405 if(!isds_ns) {
8406 isds_log_message(context, _("Could not create ISDS name space"));
8407 xmlFreeNode(request);
8408 return IE_ERROR;
8410 xmlSetNs(request, isds_ns);
8413 /* Build recipients */
8414 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8415 if (!recipients) {
8416 isds_log_message(context, _("Could not add dmRecipients child to "
8417 "CreateMultipleMessage element"));
8418 xmlFreeNode(request);
8419 return IE_ERROR;
8422 /* Insert each recipient */
8423 for (item = copies; item; item = item->next) {
8424 copy = (struct isds_message_copy *) item->data;
8425 if (!copy) {
8426 isds_log_message(context,
8427 _("`copies' list item contains empty data"));
8428 err = IE_INVAL;
8429 goto leave;
8432 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
8433 if (!recipient) {
8434 isds_log_message(context, _("Could not add dmRecipient child to "
8435 "dmRecipients element"));
8436 err = IE_ERROR;
8437 goto leave;
8440 if (!copy->dbIDRecipient) {
8441 isds_log_message(context,
8442 _("Message copy is missing recipient box identifier"));
8443 err = IE_INVAL;
8444 goto leave;
8446 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
8447 INSERT_STRING(recipient, "dmRecipientOrgUnit",
8448 copy->dmRecipientOrgUnit);
8449 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
8450 copy->dmRecipientOrgUnitNum, string);
8451 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
8454 /* Append envelope and files */
8455 err = insert_envelope_files(context, outgoing_message, request, 0);
8456 if (err) goto leave;
8459 isds_log(ILF_ISDS, ILL_DEBUG,
8460 _("Sending CreateMultipleMessage request to ISDS\n"));
8462 /* Sent request */
8463 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8464 if (err) {
8465 isds_log(ILF_ISDS, ILL_DEBUG,
8466 _("Processing ISDS response on CreateMultipleMessage "
8467 "request failed\n"));
8468 goto leave;
8471 /* Check for response status */
8472 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8473 &code, &message, NULL);
8474 if (err) {
8475 isds_log(ILF_ISDS, ILL_DEBUG,
8476 _("ISDS response on CreateMultipleMessage request "
8477 "is missing status\n"));
8478 goto leave;
8481 /* Request processed, but some copies failed */
8482 if (!xmlStrcmp(code, BAD_CAST "0004")) {
8483 char *box_id_locale =
8484 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8485 char *code_locale = _isds_utf82locale((char*)code);
8486 char *message_locale = _isds_utf82locale((char*)message);
8487 isds_log(ILF_ISDS, ILL_DEBUG,
8488 _("Server did accept message for multiple recipients "
8489 "on CreateMultipleMessage request but delivery to "
8490 "some of them failed (code=%s, message=%s)\n"),
8491 box_id_locale, code_locale, message_locale);
8492 isds_log_message(context, message_locale);
8493 free(box_id_locale);
8494 free(code_locale);
8495 free(message_locale);
8496 err = IE_PARTIAL_SUCCESS;
8499 /* Request refused by server as whole */
8500 else if (xmlStrcmp(code, BAD_CAST "0000")) {
8501 char *box_id_locale =
8502 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8503 char *code_locale = _isds_utf82locale((char*)code);
8504 char *message_locale = _isds_utf82locale((char*)message);
8505 isds_log(ILF_ISDS, ILL_DEBUG,
8506 _("Server did not accept message for multiple recipients "
8507 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
8508 box_id_locale, code_locale, message_locale);
8509 isds_log_message(context, message_locale);
8510 free(box_id_locale);
8511 free(code_locale);
8512 free(message_locale);
8513 err = IE_ISDS;
8514 goto leave;
8518 /* Extract data */
8519 xpath_ctx = xmlXPathNewContext(response);
8520 if (!xpath_ctx) {
8521 err = IE_ERROR;
8522 goto leave;
8524 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8525 err = IE_ERROR;
8526 goto leave;
8528 result = xmlXPathEvalExpression(
8529 BAD_CAST "/isds:CreateMultipleMessageResponse"
8530 "/isds:dmMultipleStatus/isds:dmSingleStatus",
8531 xpath_ctx);
8532 if (!result) {
8533 err = IE_ERROR;
8534 goto leave;
8536 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8537 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
8538 err = IE_ISDS;
8539 goto leave;
8542 /* Extract message ID and delivery status for each copy */
8543 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
8544 item = item->next, i++) {
8545 copy = (struct isds_message_copy *) item->data;
8546 xpath_ctx->node = result->nodesetval->nodeTab[i];
8548 append_err = append_TMStatus(context, copy, xpath_ctx);
8549 if (append_err) {
8550 err = append_err;
8551 goto leave;
8554 if (item || i < result->nodesetval->nodeNr) {
8555 isds_printf_message(context, _("ISDS returned unexpected number of "
8556 "message copy delivery states: %d"),
8557 result->nodesetval->nodeNr);
8558 err = IE_ISDS;
8559 goto leave;
8563 leave:
8564 /* Clean up */
8565 free(string);
8566 xmlXPathFreeObject(result);
8567 xmlXPathFreeContext(xpath_ctx);
8569 free(code);
8570 free(message);
8571 xmlFreeDoc(response);
8572 xmlFreeNode(request);
8574 if (!err)
8575 isds_log(ILF_ISDS, ILL_DEBUG,
8576 _("CreateMultipleMessageResponse request processed by server "
8577 "successfully.\n"));
8578 #else /* not HAVE_LIBCURL */
8579 err = IE_NOTSUP;
8580 #endif
8582 return err;
8586 /* Get list of messages. This is common core for getting sent or received
8587 * messages.
8588 * Any criterion argument can be NULL, if you don't care about it.
8589 * @context is session context. Must not be NULL.
8590 * @outgoing_direction is true if you want list of outgoing messages,
8591 * it's false if you want incoming messages.
8592 * @from_time is minimal time and date of message sending inclusive.
8593 * @to_time is maximal time and date of message sending inclusive
8594 * @organization_unit_number is number of sender/recipient respectively.
8595 * @status_filter is bit field of isds_message_status values. Use special
8596 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8597 * all values, you can use bit-wise arithmetic if you want.)
8598 * @offset is index of first message we are interested in. First message is 1.
8599 * Set to 0 (or 1) if you don't care.
8600 * @number is maximal length of list you want to get as input value, outputs
8601 * number of messages matching these criteria. Can be NULL if you don't care
8602 * (applies to output value either).
8603 * @messages is automatically reallocated list of isds_message's. Be ware that
8604 * it returns only brief overview (envelope and some other fields) about each
8605 * message, not the complete message. FIXME: Specify exact fields.
8606 * The list is sorted by delivery time in ascending order.
8607 * Use NULL if you don't care about don't need the data (useful if you want to
8608 * know only the @number). If you provide &NULL, list will be allocated on
8609 * heap, if you provide pointer to non-NULL, list will be freed automatically
8610 * at first. Also in case of error the list will be NULLed.
8611 * @return IE_SUCCESS or appropriate error code. */
8612 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
8613 _Bool outgoing_direction,
8614 const struct timeval *from_time, const struct timeval *to_time,
8615 const long int *organization_unit_number,
8616 const unsigned int status_filter,
8617 const unsigned long int offset, unsigned long int *number,
8618 struct isds_list **messages) {
8620 isds_error err = IE_SUCCESS;
8621 #if HAVE_LIBCURL
8622 xmlNsPtr isds_ns = NULL;
8623 xmlNodePtr request = NULL, node;
8624 xmlDocPtr response = NULL;
8625 xmlChar *code = NULL, *message = NULL;
8626 xmlXPathContextPtr xpath_ctx = NULL;
8627 xmlXPathObjectPtr result = NULL;
8628 xmlChar *string = NULL;
8629 long unsigned int count = 0;
8630 #endif
8632 if (!context) return IE_INVALID_CONTEXT;
8633 zfree(context->long_message);
8635 /* Free former message list if any */
8636 if (messages) isds_list_free(messages);
8638 #if HAVE_LIBCURL
8639 /* Check if connection is established
8640 * TODO: This check should be done downstairs. */
8641 if (!context->curl) return IE_CONNECTION_CLOSED;
8643 /* Build GetListOf*Messages request */
8644 request = xmlNewNode(NULL,
8645 (outgoing_direction) ?
8646 BAD_CAST "GetListOfSentMessages" :
8647 BAD_CAST "GetListOfReceivedMessages"
8649 if (!request) {
8650 isds_log_message(context,
8651 (outgoing_direction) ?
8652 _("Could not build GetListOfSentMessages request") :
8653 _("Could not build GetListOfReceivedMessages request")
8655 return IE_ERROR;
8657 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8658 if(!isds_ns) {
8659 isds_log_message(context, _("Could not create ISDS name space"));
8660 xmlFreeNode(request);
8661 return IE_ERROR;
8663 xmlSetNs(request, isds_ns);
8666 if (from_time) {
8667 err = timeval2timestring(from_time, &string);
8668 if (err) goto leave;
8670 INSERT_STRING(request, "dmFromTime", string);
8671 free(string); string = NULL;
8673 if (to_time) {
8674 err = timeval2timestring(to_time, &string);
8675 if (err) goto leave;
8677 INSERT_STRING(request, "dmToTime", string);
8678 free(string); string = NULL;
8680 if (outgoing_direction) {
8681 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
8682 organization_unit_number, string);
8683 } else {
8684 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
8685 organization_unit_number, string);
8688 if (status_filter > MESSAGESTATE_ANY) {
8689 isds_printf_message(context,
8690 _("Invalid message state filter value: %ld"), status_filter);
8691 err = IE_INVAL;
8692 goto leave;
8694 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
8696 if (offset > 0 ) {
8697 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
8698 } else {
8699 INSERT_STRING(request, "dmOffset", "1");
8702 /* number 0 means no limit */
8703 if (number && *number == 0) {
8704 INSERT_STRING(request, "dmLimit", NULL);
8705 } else {
8706 INSERT_ULONGINT(request, "dmLimit", number, string);
8710 isds_log(ILF_ISDS, ILL_DEBUG,
8711 (outgoing_direction) ?
8712 _("Sending GetListOfSentMessages request to ISDS\n") :
8713 _("Sending GetListOfReceivedMessages request to ISDS\n")
8716 /* Sent request */
8717 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
8718 xmlFreeNode(request); request = NULL;
8720 if (err) {
8721 isds_log(ILF_ISDS, ILL_DEBUG,
8722 (outgoing_direction) ?
8723 _("Processing ISDS response on GetListOfSentMessages "
8724 "request failed\n") :
8725 _("Processing ISDS response on GetListOfReceivedMessages "
8726 "request failed\n")
8728 goto leave;
8731 /* Check for response status */
8732 err = isds_response_status(context, SERVICE_DM_INFO, response,
8733 &code, &message, NULL);
8734 if (err) {
8735 isds_log(ILF_ISDS, ILL_DEBUG,
8736 (outgoing_direction) ?
8737 _("ISDS response on GetListOfSentMessages request "
8738 "is missing status\n") :
8739 _("ISDS response on GetListOfReceivedMessages request "
8740 "is missing status\n")
8742 goto leave;
8745 /* Request processed, but nothing found */
8746 if (xmlStrcmp(code, BAD_CAST "0000")) {
8747 char *code_locale = _isds_utf82locale((char*)code);
8748 char *message_locale = _isds_utf82locale((char*)message);
8749 isds_log(ILF_ISDS, ILL_DEBUG,
8750 (outgoing_direction) ?
8751 _("Server refused GetListOfSentMessages request "
8752 "(code=%s, message=%s)\n") :
8753 _("Server refused GetListOfReceivedMessages request "
8754 "(code=%s, message=%s)\n"),
8755 code_locale, message_locale);
8756 isds_log_message(context, message_locale);
8757 free(code_locale);
8758 free(message_locale);
8759 err = IE_ISDS;
8760 goto leave;
8764 /* Extract data */
8765 xpath_ctx = xmlXPathNewContext(response);
8766 if (!xpath_ctx) {
8767 err = IE_ERROR;
8768 goto leave;
8770 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8771 err = IE_ERROR;
8772 goto leave;
8774 result = xmlXPathEvalExpression(
8775 (outgoing_direction) ?
8776 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
8777 "isds:dmRecords/isds:dmRecord" :
8778 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
8779 "isds:dmRecords/isds:dmRecord",
8780 xpath_ctx);
8781 if (!result) {
8782 err = IE_ERROR;
8783 goto leave;
8786 /* Fill output arguments in */
8787 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8788 struct isds_envelope *envelope;
8789 struct isds_list *item = NULL, *last_item = NULL;
8791 for (count = 0; count < result->nodesetval->nodeNr; count++) {
8792 /* Create new message */
8793 item = calloc(1, sizeof(*item));
8794 if (!item) {
8795 err = IE_NOMEM;
8796 goto leave;
8798 item->destructor = (void(*)(void**)) &isds_message_free;
8799 item->data = calloc(1, sizeof(struct isds_message));
8800 if (!item->data) {
8801 isds_list_free(&item);
8802 err = IE_NOMEM;
8803 goto leave;
8806 /* Extract envelope data */
8807 xpath_ctx->node = result->nodesetval->nodeTab[count];
8808 envelope = NULL;
8809 err = extract_DmRecord(context, &envelope, xpath_ctx);
8810 if (err) {
8811 isds_list_free(&item);
8812 goto leave;
8815 /* Attach extracted envelope */
8816 ((struct isds_message *) item->data)->envelope = envelope;
8818 /* Append new message into the list */
8819 if (!*messages) {
8820 *messages = last_item = item;
8821 } else {
8822 last_item->next = item;
8823 last_item = item;
8827 if (number) *number = count;
8829 leave:
8830 if (err) {
8831 isds_list_free(messages);
8834 free(string);
8835 xmlXPathFreeObject(result);
8836 xmlXPathFreeContext(xpath_ctx);
8838 free(code);
8839 free(message);
8840 xmlFreeDoc(response);
8841 xmlFreeNode(request);
8843 if (!err)
8844 isds_log(ILF_ISDS, ILL_DEBUG,
8845 (outgoing_direction) ?
8846 _("GetListOfSentMessages request processed by server "
8847 "successfully.\n") :
8848 _("GetListOfReceivedMessages request processed by server "
8849 "successfully.\n")
8851 #else /* not HAVE_LIBCURL */
8852 err = IE_NOTSUP;
8853 #endif
8854 return err;
8858 /* Get list of outgoing (already sent) messages.
8859 * Any criterion argument can be NULL, if you don't care about it.
8860 * @context is session context. Must not be NULL.
8861 * @from_time is minimal time and date of message sending inclusive.
8862 * @to_time is maximal time and date of message sending inclusive
8863 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
8864 * @status_filter is bit field of isds_message_status values. Use special
8865 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8866 * all values, you can use bit-wise arithmetic if you want.)
8867 * @offset is index of first message we are interested in. First message is 1.
8868 * Set to 0 (or 1) if you don't care.
8869 * @number is maximal length of list you want to get as input value, outputs
8870 * number of messages matching these criteria. Can be NULL if you don't care
8871 * (applies to output value either).
8872 * @messages is automatically reallocated list of isds_message's. Be ware that
8873 * it returns only brief overview (envelope and some other fields) about each
8874 * message, not the complete message. FIXME: Specify exact fields.
8875 * The list is sorted by delivery time in ascending order.
8876 * Use NULL if you don't care about the meta data (useful if you want to know
8877 * only the @number). If you provide &NULL, list will be allocated on heap,
8878 * if you provide pointer to non-NULL, list will be freed automatically at
8879 * first. Also in case of error the list will be NULLed.
8880 * @return IE_SUCCESS or appropriate error code. */
8881 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
8882 const struct timeval *from_time, const struct timeval *to_time,
8883 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
8884 const unsigned long int offset, unsigned long int *number,
8885 struct isds_list **messages) {
8887 return isds_get_list_of_messages(
8888 context, 1,
8889 from_time, to_time, dmSenderOrgUnitNum, status_filter,
8890 offset, number,
8891 messages);
8895 /* Get list of incoming (addressed to you) messages.
8896 * Any criterion argument can be NULL, if you don't care about it.
8897 * @context is session context. Must not be NULL.
8898 * @from_time is minimal time and date of message sending inclusive.
8899 * @to_time is maximal time and date of message sending inclusive
8900 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
8901 * @status_filter is bit field of isds_message_status values. Use special
8902 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8903 * all values, you can use bit-wise arithmetic if you want.)
8904 * @offset is index of first message we are interested in. First message is 1.
8905 * Set to 0 (or 1) if you don't care.
8906 * @number is maximal length of list you want to get as input value, outputs
8907 * number of messages matching these criteria. Can be NULL if you don't care
8908 * (applies to output value either).
8909 * @messages is automatically reallocated list of isds_message's. Be ware that
8910 * it returns only brief overview (envelope and some other fields) about each
8911 * message, not the complete message. FIXME: Specify exact fields.
8912 * Use NULL if you don't care about the meta data (useful if you want to know
8913 * only the @number). If you provide &NULL, list will be allocated on heap,
8914 * if you provide pointer to non-NULL, list will be freed automatically at
8915 * first. Also in case of error the list will be NULLed.
8916 * @return IE_SUCCESS or appropriate error code. */
8917 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
8918 const struct timeval *from_time, const struct timeval *to_time,
8919 const long int *dmRecipientOrgUnitNum,
8920 const unsigned int status_filter,
8921 const unsigned long int offset, unsigned long int *number,
8922 struct isds_list **messages) {
8924 return isds_get_list_of_messages(
8925 context, 0,
8926 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
8927 offset, number,
8928 messages);
8932 /* Get list of sent message state changes.
8933 * Any criterion argument can be NULL, if you don't care about it.
8934 * @context is session context. Must not be NULL.
8935 * @from_time is minimal time and date of status changes inclusive
8936 * @to_time is maximal time and date of status changes inclusive
8937 * @changed_states is automatically reallocated list of
8938 * isds_message_status_change's. If you provide &NULL, list will be allocated
8939 * on heap, if you provide pointer to non-NULL, list will be freed
8940 * automatically at first. Also in case of error the list will be NULLed.
8941 * XXX: The list item ordering is not specified.
8942 * XXX: Server provides only `recent' changes.
8943 * @return IE_SUCCESS or appropriate error code. */
8944 isds_error isds_get_list_of_sent_message_state_changes(
8945 struct isds_ctx *context,
8946 const struct timeval *from_time, const struct timeval *to_time,
8947 struct isds_list **changed_states) {
8949 isds_error err = IE_SUCCESS;
8950 #if HAVE_LIBCURL
8951 xmlNsPtr isds_ns = NULL;
8952 xmlNodePtr request = NULL, node;
8953 xmlDocPtr response = NULL;
8954 xmlXPathContextPtr xpath_ctx = NULL;
8955 xmlXPathObjectPtr result = NULL;
8956 xmlChar *string = NULL;
8957 long unsigned int count = 0;
8958 #endif
8960 if (!context) return IE_INVALID_CONTEXT;
8961 zfree(context->long_message);
8963 /* Free former message list if any */
8964 isds_list_free(changed_states);
8966 #if HAVE_LIBCURL
8967 /* Check if connection is established
8968 * TODO: This check should be done downstairs. */
8969 if (!context->curl) return IE_CONNECTION_CLOSED;
8971 /* Build GetMessageStateChanges request */
8972 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
8973 if (!request) {
8974 isds_log_message(context,
8975 _("Could not build GetMessageStateChanges request"));
8976 return IE_ERROR;
8978 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8979 if(!isds_ns) {
8980 isds_log_message(context, _("Could not create ISDS name space"));
8981 xmlFreeNode(request);
8982 return IE_ERROR;
8984 xmlSetNs(request, isds_ns);
8987 if (from_time) {
8988 err = timeval2timestring(from_time, &string);
8989 if (err) goto leave;
8991 INSERT_STRING(request, "dmFromTime", string);
8992 zfree(string);
8994 if (to_time) {
8995 err = timeval2timestring(to_time, &string);
8996 if (err) goto leave;
8998 INSERT_STRING(request, "dmToTime", string);
8999 zfree(string);
9002 /* Sent request */
9003 err = send_destroy_request_check_response(context,
9004 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9005 &response, NULL, NULL);
9006 if (err) goto leave;
9009 /* Extract data */
9010 xpath_ctx = xmlXPathNewContext(response);
9011 if (!xpath_ctx) {
9012 err = IE_ERROR;
9013 goto leave;
9015 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9016 err = IE_ERROR;
9017 goto leave;
9019 result = xmlXPathEvalExpression(
9020 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9021 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9022 if (!result) {
9023 err = IE_ERROR;
9024 goto leave;
9027 /* Fill output arguments in */
9028 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9029 struct isds_list *item = NULL, *last_item = NULL;
9031 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9032 /* Create new status change */
9033 item = calloc(1, sizeof(*item));
9034 if (!item) {
9035 err = IE_NOMEM;
9036 goto leave;
9038 item->destructor =
9039 (void(*)(void**)) &isds_message_status_change_free;
9041 /* Extract message status change */
9042 xpath_ctx->node = result->nodesetval->nodeTab[count];
9043 err = extract_StateChangesRecord(context,
9044 (struct isds_message_status_change **) &item->data,
9045 xpath_ctx);
9046 if (err) {
9047 isds_list_free(&item);
9048 goto leave;
9051 /* Append new message status change into the list */
9052 if (!*changed_states) {
9053 *changed_states = last_item = item;
9054 } else {
9055 last_item->next = item;
9056 last_item = item;
9061 leave:
9062 if (err) {
9063 isds_list_free(changed_states);
9066 free(string);
9067 xmlXPathFreeObject(result);
9068 xmlXPathFreeContext(xpath_ctx);
9069 xmlFreeDoc(response);
9070 xmlFreeNode(request);
9072 if (!err)
9073 isds_log(ILF_ISDS, ILL_DEBUG,
9074 _("GetMessageStateChanges request processed by server "
9075 "successfully.\n"));
9076 #else /* not HAVE_LIBCURL */
9077 err = IE_NOTSUP;
9078 #endif
9079 return err;
9083 #if HAVE_LIBCURL
9084 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9085 * code
9086 * @context is session context
9087 * @service is ISDS WS service handler
9088 * @service_name is name of SERVICE_DM_OPERATIONS
9089 * @message_id is message ID to send as service argument to ISDS
9090 * @response is reallocated server SOAP body response as XML document
9091 * @raw_response is reallocated bit stream with response body. Use
9092 * NULL if you don't care
9093 * @raw_response_length is size of @raw_response in bytes
9094 * @code is reallocated ISDS status code
9095 * @status_message is reallocated ISDS status message
9096 * @return error coded from lower layer, context message will be set up
9097 * appropriately. */
9098 static isds_error build_send_check_message_request(struct isds_ctx *context,
9099 const isds_service service, const xmlChar *service_name,
9100 const char *message_id,
9101 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9102 xmlChar **code, xmlChar **status_message) {
9104 isds_error err = IE_SUCCESS;
9105 char *service_name_locale = NULL, *message_id_locale = NULL;
9106 xmlNodePtr request = NULL, node;
9107 xmlNsPtr isds_ns = NULL;
9109 if (!context) return IE_INVALID_CONTEXT;
9110 if (!service_name || !message_id) return IE_INVAL;
9111 if (!response || !code || !status_message) return IE_INVAL;
9112 if (!raw_response_length && raw_response) return IE_INVAL;
9114 /* Free output argument */
9115 xmlFreeDoc(*response); *response = NULL;
9116 if (raw_response) zfree(*raw_response);
9117 zfree(*code);
9118 zfree(*status_message);
9121 /* Check if connection is established
9122 * TODO: This check should be done downstairs. */
9123 if (!context->curl) return IE_CONNECTION_CLOSED;
9125 service_name_locale = _isds_utf82locale((char*)service_name);
9126 message_id_locale = _isds_utf82locale(message_id);
9127 if (!service_name_locale || !message_id_locale) {
9128 err = IE_NOMEM;
9129 goto leave;
9132 /* Build request */
9133 request = xmlNewNode(NULL, service_name);
9134 if (!request) {
9135 isds_printf_message(context,
9136 _("Could not build %s request for %s message ID"),
9137 service_name_locale, message_id_locale);
9138 err = IE_ERROR;
9139 goto leave;
9141 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9142 if(!isds_ns) {
9143 isds_log_message(context, _("Could not create ISDS name space"));
9144 err = IE_ERROR;
9145 goto leave;
9147 xmlSetNs(request, isds_ns);
9150 /* Add requested ID */
9151 err = validate_message_id_length(context, (xmlChar *) message_id);
9152 if (err) goto leave;
9153 INSERT_STRING(request, "dmID", message_id);
9156 isds_log(ILF_ISDS, ILL_DEBUG,
9157 _("Sending %s request for %s message ID to ISDS\n"),
9158 service_name_locale, message_id_locale);
9160 /* Send request */
9161 err = isds(context, service, request, response,
9162 raw_response, raw_response_length);
9163 xmlFreeNode(request); request = NULL;
9165 if (err) {
9166 isds_log(ILF_ISDS, ILL_DEBUG,
9167 _("Processing ISDS response on %s request failed\n"),
9168 service_name_locale);
9169 goto leave;
9172 /* Check for response status */
9173 err = isds_response_status(context, service, *response,
9174 code, status_message, NULL);
9175 if (err) {
9176 isds_log(ILF_ISDS, ILL_DEBUG,
9177 _("ISDS response on %s request is missing status\n"),
9178 service_name_locale);
9179 goto leave;
9182 /* Request processed, but nothing found */
9183 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9184 char *code_locale = _isds_utf82locale((char*) *code);
9185 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9186 isds_log(ILF_ISDS, ILL_DEBUG,
9187 _("Server refused %s request for %s message ID "
9188 "(code=%s, message=%s)\n"),
9189 service_name_locale, message_id_locale,
9190 code_locale, status_message_locale);
9191 isds_log_message(context, status_message_locale);
9192 free(code_locale);
9193 free(status_message_locale);
9194 err = IE_ISDS;
9195 goto leave;
9198 leave:
9199 free(message_id_locale);
9200 free(service_name_locale);
9201 xmlFreeNode(request);
9202 return err;
9206 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9207 * signed data and free ISDS response.
9208 * @context is session context
9209 * @message_id is UTF-8 encoded message ID for logging purpose
9210 * @response is parsed XML document. It will be freed and NULLed in the middle
9211 * of function run to save memory. This is not guaranteed in case of error.
9212 * @request_name is name of ISDS request used to construct response root
9213 * element name and for logging purpose.
9214 * @raw is reallocated output buffer with DER encoded CMS data
9215 * @raw_length is size of @raw buffer in bytes
9216 * @returns standard error codes, in case of error, @raw will be freed and
9217 * NULLed, @response sometimes. */
9218 static isds_error find_extract_signed_data_free_response(
9219 struct isds_ctx *context, const xmlChar *message_id,
9220 xmlDocPtr *response, const xmlChar *request_name,
9221 void **raw, size_t *raw_length) {
9223 isds_error err = IE_SUCCESS;
9224 char *xpath_expression = NULL;
9225 xmlXPathContextPtr xpath_ctx = NULL;
9226 xmlXPathObjectPtr result = NULL;
9227 char *encoded_structure = NULL;
9229 if (!context) return IE_INVALID_CONTEXT;
9230 if (!raw) return IE_INVAL;
9231 zfree(*raw);
9232 if (!message_id || !response || !*response || !request_name || !raw_length)
9233 return IE_INVAL;
9235 /* Build XPath expression */
9236 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9237 "Response/isds:dmSignature");
9238 if (!xpath_expression) return IE_NOMEM;
9240 /* Extract data */
9241 xpath_ctx = xmlXPathNewContext(*response);
9242 if (!xpath_ctx) {
9243 err = IE_ERROR;
9244 goto leave;
9246 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9247 err = IE_ERROR;
9248 goto leave;
9250 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9251 if (!result) {
9252 err = IE_ERROR;
9253 goto leave;
9255 /* Empty response */
9256 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9257 char *message_id_locale = _isds_utf82locale((char*) message_id);
9258 isds_printf_message(context,
9259 _("Server did not return any signed data for message ID `%s' "
9260 "on %s request"),
9261 message_id_locale, request_name);
9262 free(message_id_locale);
9263 err = IE_ISDS;
9264 goto leave;
9266 /* More responses */
9267 if (result->nodesetval->nodeNr > 1) {
9268 char *message_id_locale = _isds_utf82locale((char*) message_id);
9269 isds_printf_message(context,
9270 _("Server did return more signed data for message ID `%s' "
9271 "on %s request"),
9272 message_id_locale, request_name);
9273 free(message_id_locale);
9274 err = IE_ISDS;
9275 goto leave;
9277 /* One response */
9278 xpath_ctx->node = result->nodesetval->nodeTab[0];
9280 /* Extract PKCS#7 structure */
9281 EXTRACT_STRING(".", encoded_structure);
9282 if (!encoded_structure) {
9283 isds_log_message(context, _("dmSignature element is empty"));
9286 /* Here we have delivery info as standalone CMS in encoded_structure.
9287 * We don't need any other data, free them: */
9288 xmlXPathFreeObject(result); result = NULL;
9289 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9290 xmlFreeDoc(*response); *response = NULL;
9293 /* Decode PKCS#7 to DER format */
9294 *raw_length = _isds_b64decode(encoded_structure, raw);
9295 if (*raw_length == (size_t) -1) {
9296 isds_log_message(context,
9297 _("Error while Base64-decoding PKCS#7 structure"));
9298 err = IE_ERROR;
9299 goto leave;
9302 leave:
9303 if (err) {
9304 zfree(*raw);
9305 raw_length = 0;
9308 free(encoded_structure);
9309 xmlXPathFreeObject(result);
9310 xmlXPathFreeContext(xpath_ctx);
9311 free(xpath_expression);
9313 return err;
9315 #endif /* HAVE_LIBCURL */
9318 /* Download incoming message envelope identified by ID.
9319 * @context is session context
9320 * @message_id is message identifier (you can get them from
9321 * isds_get_list_of_received_messages())
9322 * @message is automatically reallocated message retrieved from ISDS.
9323 * It will miss documents per se. Use isds_get_received_message(), if you are
9324 * interested in documents (content) too.
9325 * Returned hash and timestamp require documents to be verifiable. */
9326 isds_error isds_get_received_envelope(struct isds_ctx *context,
9327 const char *message_id, struct isds_message **message) {
9329 isds_error err = IE_SUCCESS;
9330 #if HAVE_LIBCURL
9331 xmlDocPtr response = NULL;
9332 xmlChar *code = NULL, *status_message = NULL;
9333 xmlXPathContextPtr xpath_ctx = NULL;
9334 xmlXPathObjectPtr result = NULL;
9335 #endif
9337 if (!context) return IE_INVALID_CONTEXT;
9338 zfree(context->long_message);
9340 /* Free former message if any */
9341 if (!message) return IE_INVAL;
9342 isds_message_free(message);
9344 #if HAVE_LIBCURL
9345 /* Do request and check for success */
9346 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9347 BAD_CAST "MessageEnvelopeDownload", message_id,
9348 &response, NULL, NULL, &code, &status_message);
9349 if (err) goto leave;
9351 /* Extract data */
9352 xpath_ctx = xmlXPathNewContext(response);
9353 if (!xpath_ctx) {
9354 err = IE_ERROR;
9355 goto leave;
9357 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9358 err = IE_ERROR;
9359 goto leave;
9361 result = xmlXPathEvalExpression(
9362 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9363 "isds:dmReturnedMessageEnvelope",
9364 xpath_ctx);
9365 if (!result) {
9366 err = IE_ERROR;
9367 goto leave;
9369 /* Empty response */
9370 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9371 char *message_id_locale = _isds_utf82locale((char*) message_id);
9372 isds_printf_message(context,
9373 _("Server did not return any envelope for ID `%s' "
9374 "on MessageEnvelopeDownload request"), message_id_locale);
9375 free(message_id_locale);
9376 err = IE_ISDS;
9377 goto leave;
9379 /* More envelops */
9380 if (result->nodesetval->nodeNr > 1) {
9381 char *message_id_locale = _isds_utf82locale((char*) message_id);
9382 isds_printf_message(context,
9383 _("Server did return more envelopes for ID `%s' "
9384 "on MessageEnvelopeDownload request"), message_id_locale);
9385 free(message_id_locale);
9386 err = IE_ISDS;
9387 goto leave;
9389 /* One message */
9390 xpath_ctx->node = result->nodesetval->nodeTab[0];
9392 /* Extract the envelope (= message without documents, hence 0) */
9393 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9394 if (err) goto leave;
9396 /* Save XML blob */
9397 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9398 &(*message)->raw_length);
9400 leave:
9401 if (err) {
9402 isds_message_free(message);
9405 xmlXPathFreeObject(result);
9406 xmlXPathFreeContext(xpath_ctx);
9408 free(code);
9409 free(status_message);
9410 if (!*message || !(*message)->xml) {
9411 xmlFreeDoc(response);
9414 if (!err)
9415 isds_log(ILF_ISDS, ILL_DEBUG,
9416 _("MessageEnvelopeDownload request processed by server "
9417 "successfully.\n")
9419 #else /* not HAVE_LIBCURL */
9420 err = IE_NOTSUP;
9421 #endif
9422 return err;
9426 /* Load delivery info of any format from buffer.
9427 * @context is session context
9428 * @raw_type advertises format of @buffer content. Only delivery info types
9429 * are accepted.
9430 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9431 * retrieve such data from message->raw after calling
9432 * isds_get_signed_delivery_info().
9433 * @length is length of buffer in bytes.
9434 * @message is automatically reallocated message parsed from @buffer.
9435 * @strategy selects how buffer will be attached into raw isds_message member.
9436 * */
9437 isds_error isds_load_delivery_info(struct isds_ctx *context,
9438 const isds_raw_type raw_type,
9439 const void *buffer, const size_t length,
9440 struct isds_message **message, const isds_buffer_strategy strategy) {
9442 isds_error err = IE_SUCCESS;
9443 message_ns_type message_ns;
9444 xmlDocPtr message_doc = NULL;
9445 xmlXPathContextPtr xpath_ctx = NULL;
9446 xmlXPathObjectPtr result = NULL;
9447 void *xml_stream = NULL;
9448 size_t xml_stream_length = 0;
9450 if (!context) return IE_INVALID_CONTEXT;
9451 zfree(context->long_message);
9452 if (!message) return IE_INVAL;
9453 isds_message_free(message);
9454 if (!buffer) return IE_INVAL;
9457 /* Select buffer format and extract XML from CMS*/
9458 switch (raw_type) {
9459 case RAWTYPE_DELIVERYINFO:
9460 message_ns = MESSAGE_NS_UNSIGNED;
9461 xml_stream = (void *) buffer;
9462 xml_stream_length = length;
9463 break;
9465 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
9466 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9467 xml_stream = (void *) buffer;
9468 xml_stream_length = length;
9469 break;
9471 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
9472 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9473 err = _isds_extract_cms_data(context, buffer, length,
9474 &xml_stream, &xml_stream_length);
9475 if (err) goto leave;
9476 break;
9478 default:
9479 isds_log_message(context, _("Bad raw delivery representation type"));
9480 return IE_INVAL;
9481 break;
9484 isds_log(ILF_ISDS, ILL_DEBUG,
9485 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
9486 xml_stream_length, xml_stream);
9488 /* Convert delivery info XML stream into XPath context */
9489 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9490 if (!message_doc) {
9491 err = IE_XML;
9492 goto leave;
9494 xpath_ctx = xmlXPathNewContext(message_doc);
9495 if (!xpath_ctx) {
9496 err = IE_ERROR;
9497 goto leave;
9499 /* XXX: Name spaces mangled for signed delivery info:
9500 * http://isds.czechpoint.cz/v20/delivery:
9502 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
9503 * <q:dmDelivery>
9504 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9505 * <p:dmID>170272</p:dmID>
9506 * ...
9507 * </p:dmDm>
9508 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9509 * ...
9510 * </q:dmEvents>...</q:dmEvents>
9511 * </q:dmDelivery>
9512 * </q:GetDeliveryInfoResponse>
9513 * */
9514 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
9515 err = IE_ERROR;
9516 goto leave;
9518 result = xmlXPathEvalExpression(
9519 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
9520 xpath_ctx);
9521 if (!result) {
9522 err = IE_ERROR;
9523 goto leave;
9525 /* Empty delivery info */
9526 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9527 isds_printf_message(context,
9528 _("XML document is not sisds:dmDelivery document"));
9529 err = IE_ISDS;
9530 goto leave;
9532 /* More delivery info's */
9533 if (result->nodesetval->nodeNr > 1) {
9534 isds_printf_message(context,
9535 _("XML document has more sisds:dmDelivery elements"));
9536 err = IE_ISDS;
9537 goto leave;
9539 /* One delivery info */
9540 xpath_ctx->node = result->nodesetval->nodeTab[0];
9542 /* Extract the envelope (= message without documents, hence 0).
9543 * XXX: extract_TReturnedMessage() can obtain attachments size,
9544 * but delivery info carries none. It's coded as option elements,
9545 * so it should work. */
9546 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9547 if (err) goto leave;
9549 /* Extract events */
9550 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
9551 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
9552 if (err) { err = IE_ERROR; goto leave; }
9553 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
9554 if (err) goto leave;
9556 /* Append raw CMS structure into message */
9557 (*message)->raw_type = raw_type;
9558 switch (strategy) {
9559 case BUFFER_DONT_STORE:
9560 break;
9561 case BUFFER_COPY:
9562 (*message)->raw = malloc(length);
9563 if (!(*message)->raw) {
9564 err = IE_NOMEM;
9565 goto leave;
9567 memcpy((*message)->raw, buffer, length);
9568 (*message)->raw_length = length;
9569 break;
9570 case BUFFER_MOVE:
9571 (*message)->raw = (void *) buffer;
9572 (*message)->raw_length = length;
9573 break;
9574 default:
9575 err = IE_ENUM;
9576 goto leave;
9579 leave:
9580 if (err) {
9581 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
9582 isds_message_free(message);
9585 xmlXPathFreeObject(result);
9586 xmlXPathFreeContext(xpath_ctx);
9587 if (!*message || !(*message)->xml) {
9588 xmlFreeDoc(message_doc);
9590 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9592 if (!err)
9593 isds_log(ILF_ISDS, ILL_DEBUG,
9594 _("Delivery info loaded successfully.\n"));
9595 return err;
9599 /* Download signed delivery info-sheet of given message identified by ID.
9600 * @context is session context
9601 * @message_id is message identifier (you can get them from
9602 * isds_get_list_of_{sent,received}_messages())
9603 * @message is automatically reallocated message retrieved from ISDS.
9604 * It will miss documents per se. Use isds_get_signed_received_message(),
9605 * if you are interested in documents (content). OTOH, only this function
9606 * can get list events message has gone through. */
9607 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
9608 const char *message_id, struct isds_message **message) {
9610 isds_error err = IE_SUCCESS;
9611 #if HAVE_LIBCURL
9612 xmlDocPtr response = NULL;
9613 xmlChar *code = NULL, *status_message = NULL;
9614 void *raw = NULL;
9615 size_t raw_length = 0;
9616 #endif
9618 if (!context) return IE_INVALID_CONTEXT;
9619 zfree(context->long_message);
9621 /* Free former message if any */
9622 if (!message) return IE_INVAL;
9623 isds_message_free(message);
9625 #if HAVE_LIBCURL
9626 /* Do request and check for success */
9627 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9628 BAD_CAST "GetSignedDeliveryInfo", message_id,
9629 &response, NULL, NULL, &code, &status_message);
9630 if (err) goto leave;
9632 /* Find signed delivery info, extract it into raw and maybe free
9633 * response */
9634 err = find_extract_signed_data_free_response(context,
9635 (xmlChar *)message_id, &response,
9636 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
9637 if (err) goto leave;
9639 /* Parse delivery info */
9640 err = isds_load_delivery_info(context,
9641 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
9642 message, BUFFER_MOVE);
9643 if (err) goto leave;
9645 raw = NULL;
9647 leave:
9648 if (err) {
9649 isds_message_free(message);
9652 free(raw);
9653 free(code);
9654 free(status_message);
9655 xmlFreeDoc(response);
9657 if (!err)
9658 isds_log(ILF_ISDS, ILL_DEBUG,
9659 _("GetSignedDeliveryInfo request processed by server "
9660 "successfully.\n")
9662 #else /* not HAVE_LIBCURL */
9663 err = IE_NOTSUP;
9664 #endif
9665 return err;
9669 /* Download delivery info-sheet of given message identified by ID.
9670 * @context is session context
9671 * @message_id is message identifier (you can get them from
9672 * isds_get_list_of_{sent,received}_messages())
9673 * @message is automatically reallocated message retrieved from ISDS.
9674 * It will miss documents per se. Use isds_get_received_message(), if you are
9675 * interested in documents (content). OTOH, only this function can get list
9676 * of events message has gone through. */
9677 isds_error isds_get_delivery_info(struct isds_ctx *context,
9678 const char *message_id, struct isds_message **message) {
9680 isds_error err = IE_SUCCESS;
9681 #if HAVE_LIBCURL
9682 xmlDocPtr response = NULL;
9683 xmlChar *code = NULL, *status_message = NULL;
9684 xmlNodePtr delivery_node = NULL;
9685 void *raw = NULL;
9686 size_t raw_length = 0;
9687 #endif
9689 if (!context) return IE_INVALID_CONTEXT;
9690 zfree(context->long_message);
9692 /* Free former message if any */
9693 if (!message) return IE_INVAL;
9694 isds_message_free(message);
9696 #if HAVE_LIBCURL
9697 /* Do request and check for success */
9698 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9699 BAD_CAST "GetDeliveryInfo", message_id,
9700 &response, NULL, NULL, &code, &status_message);
9701 if (err) goto leave;
9704 /* Serialize delivery info */
9705 delivery_node = xmlDocGetRootElement(response);
9706 if (!delivery_node) {
9707 char *message_id_locale = _isds_utf82locale((char*) message_id);
9708 isds_printf_message(context,
9709 _("Server did not return any delivery info for ID `%s' "
9710 "on GetDeliveryInfo request"), message_id_locale);
9711 free(message_id_locale);
9712 err = IE_ISDS;
9713 goto leave;
9715 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
9716 if (err) goto leave;
9718 /* Parse delivery info */
9719 /* TODO: Here we parse the response second time. We could single delivery
9720 * parser from isds_load_delivery_info() to make things faster. */
9721 err = isds_load_delivery_info(context,
9722 RAWTYPE_DELIVERYINFO, raw, raw_length,
9723 message, BUFFER_MOVE);
9724 if (err) goto leave;
9726 raw = NULL;
9729 leave:
9730 if (err) {
9731 isds_message_free(message);
9734 free(raw);
9735 free(code);
9736 free(status_message);
9737 xmlFreeDoc(response);
9739 if (!err)
9740 isds_log(ILF_ISDS, ILL_DEBUG,
9741 _("GetDeliveryInfo request processed by server "
9742 "successfully.\n")
9744 #else /* not HAVE_LIBCURL */
9745 err = IE_NOTSUP;
9746 #endif
9747 return err;
9751 /* Download incoming message identified by ID.
9752 * @context is session context
9753 * @message_id is message identifier (you can get them from
9754 * isds_get_list_of_received_messages())
9755 * @message is automatically reallocated message retrieved from ISDS */
9756 isds_error isds_get_received_message(struct isds_ctx *context,
9757 const char *message_id, struct isds_message **message) {
9759 isds_error err = IE_SUCCESS;
9760 #if HAVE_LIBCURL
9761 xmlDocPtr response = NULL;
9762 void *xml_stream = NULL;
9763 size_t xml_stream_length;
9764 xmlChar *code = NULL, *status_message = NULL;
9765 xmlXPathContextPtr xpath_ctx = NULL;
9766 xmlXPathObjectPtr result = NULL;
9767 char *phys_path = NULL;
9768 size_t phys_start, phys_end;
9769 #endif
9771 if (!context) return IE_INVALID_CONTEXT;
9772 zfree(context->long_message);
9774 /* Free former message if any */
9775 if (NULL == message) return IE_INVAL;
9776 if (message) isds_message_free(message);
9778 #if HAVE_LIBCURL
9779 /* Do request and check for success */
9780 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
9781 BAD_CAST "MessageDownload", message_id,
9782 &response, &xml_stream, &xml_stream_length,
9783 &code, &status_message);
9784 if (err) goto leave;
9786 /* Extract data */
9787 xpath_ctx = xmlXPathNewContext(response);
9788 if (!xpath_ctx) {
9789 err = IE_ERROR;
9790 goto leave;
9792 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9793 err = IE_ERROR;
9794 goto leave;
9796 result = xmlXPathEvalExpression(
9797 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
9798 xpath_ctx);
9799 if (!result) {
9800 err = IE_ERROR;
9801 goto leave;
9803 /* Empty response */
9804 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9805 char *message_id_locale = _isds_utf82locale((char*) message_id);
9806 isds_printf_message(context,
9807 _("Server did not return any message for ID `%s' "
9808 "on MessageDownload request"), message_id_locale);
9809 free(message_id_locale);
9810 err = IE_ISDS;
9811 goto leave;
9813 /* More messages */
9814 if (result->nodesetval->nodeNr > 1) {
9815 char *message_id_locale = _isds_utf82locale((char*) message_id);
9816 isds_printf_message(context,
9817 _("Server did return more messages for ID `%s' "
9818 "on MessageDownload request"), message_id_locale);
9819 free(message_id_locale);
9820 err = IE_ISDS;
9821 goto leave;
9823 /* One message */
9824 xpath_ctx->node = result->nodesetval->nodeTab[0];
9826 /* Extract the message */
9827 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
9828 if (err) goto leave;
9830 /* Locate raw XML blob */
9831 phys_path = strdup(
9832 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
9833 PHYSXML_ELEMENT_SEPARATOR
9834 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
9835 PHYSXML_ELEMENT_SEPARATOR
9836 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
9838 if (!phys_path) {
9839 err = IE_NOMEM;
9840 goto leave;
9842 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
9843 phys_path, &phys_start, &phys_end);
9844 zfree(phys_path);
9845 if (err) {
9846 isds_log_message(context,
9847 _("Substring with isds:MessageDownloadResponse element "
9848 "could not be located in raw SOAP message"));
9849 goto leave;
9851 /* Save XML blob */
9852 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9853 &(*message)->raw_length);*/
9854 /* TODO: Store name space declarations from ancestors */
9855 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
9856 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
9857 (*message)->raw_length = phys_end - phys_start + 1;
9858 (*message)->raw = malloc((*message)->raw_length);
9859 if (!(*message)->raw) {
9860 err = IE_NOMEM;
9861 goto leave;
9863 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
9866 leave:
9867 if (err) {
9868 isds_message_free(message);
9871 free(phys_path);
9873 xmlXPathFreeObject(result);
9874 xmlXPathFreeContext(xpath_ctx);
9876 free(code);
9877 free(status_message);
9878 free(xml_stream);
9879 if (!*message || !(*message)->xml) {
9880 xmlFreeDoc(response);
9883 if (!err)
9884 isds_log(ILF_ISDS, ILL_DEBUG,
9885 _("MessageDownload request processed by server "
9886 "successfully.\n")
9888 #else /* not HAVE_LIBCURL */
9889 err = IE_NOTSUP;
9890 #endif
9891 return err;
9895 /* Load message of any type from buffer.
9896 * @context is session context
9897 * @raw_type defines content type of @buffer. Only message types are allowed.
9898 * @buffer is message raw representation. Format (CMS, plain signed,
9899 * message direction) is defined in @raw_type. You can retrieve such data
9900 * from message->raw after calling isds_get_[signed]{received,sent}_message().
9901 * @length is length of buffer in bytes.
9902 * @message is automatically reallocated message parsed from @buffer.
9903 * @strategy selects how buffer will be attached into raw isds_message member.
9904 * */
9905 isds_error isds_load_message(struct isds_ctx *context,
9906 const isds_raw_type raw_type, const void *buffer, const size_t length,
9907 struct isds_message **message, const isds_buffer_strategy strategy) {
9909 isds_error err = IE_SUCCESS;
9910 void *xml_stream = NULL;
9911 size_t xml_stream_length = 0;
9912 message_ns_type message_ns;
9913 xmlDocPtr message_doc = NULL;
9914 xmlXPathContextPtr xpath_ctx = NULL;
9915 xmlXPathObjectPtr result = NULL;
9917 if (!context) return IE_INVALID_CONTEXT;
9918 zfree(context->long_message);
9919 if (!message) return IE_INVAL;
9920 isds_message_free(message);
9921 if (!buffer) return IE_INVAL;
9924 /* Select buffer format and extract XML from CMS*/
9925 switch (raw_type) {
9926 case RAWTYPE_INCOMING_MESSAGE:
9927 message_ns = MESSAGE_NS_UNSIGNED;
9928 xml_stream = (void *) buffer;
9929 xml_stream_length = length;
9930 break;
9932 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
9933 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9934 xml_stream = (void *) buffer;
9935 xml_stream_length = length;
9936 break;
9938 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
9939 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9940 err = _isds_extract_cms_data(context, buffer, length,
9941 &xml_stream, &xml_stream_length);
9942 if (err) goto leave;
9943 break;
9945 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
9946 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9947 xml_stream = (void *) buffer;
9948 xml_stream_length = length;
9949 break;
9951 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
9952 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9953 err = _isds_extract_cms_data(context, buffer, length,
9954 &xml_stream, &xml_stream_length);
9955 if (err) goto leave;
9956 break;
9958 default:
9959 isds_log_message(context, _("Bad raw message representation type"));
9960 return IE_INVAL;
9961 break;
9964 isds_log(ILF_ISDS, ILL_DEBUG,
9965 _("Loading message:\n%.*s\nEnd of message\n"),
9966 xml_stream_length, xml_stream);
9968 /* Convert messages XML stream into XPath context */
9969 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9970 if (!message_doc) {
9971 err = IE_XML;
9972 goto leave;
9974 xpath_ctx = xmlXPathNewContext(message_doc);
9975 if (!xpath_ctx) {
9976 err = IE_ERROR;
9977 goto leave;
9979 /* XXX: Standard name space for unsigned incoming direction:
9980 * http://isds.czechpoint.cz/v20/
9982 * XXX: Name spaces mangled for signed outgoing direction:
9983 * http://isds.czechpoint.cz/v20/SentMessage:
9985 * <q:MessageDownloadResponse
9986 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
9987 * <q:dmReturnedMessage>
9988 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9989 * <p:dmID>151916</p:dmID>
9990 * ...
9991 * </p:dmDm>
9992 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9993 * ...
9994 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9995 * </q:dmReturnedMessage>
9996 * </q:MessageDownloadResponse>
9998 * XXX: Name spaces mangled for signed incoming direction:
9999 * http://isds.czechpoint.cz/v20/message:
10001 * <q:MessageDownloadResponse
10002 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10003 * <q:dmReturnedMessage>
10004 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10005 * <p:dmID>151916</p:dmID>
10006 * ...
10007 * </p:dmDm>
10008 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10009 * ...
10010 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10011 * </q:dmReturnedMessage>
10012 * </q:MessageDownloadResponse>
10014 * Stupidity of ISDS developers is unlimited */
10015 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10016 err = IE_ERROR;
10017 goto leave;
10019 result = xmlXPathEvalExpression(
10020 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10021 xpath_ctx);
10022 if (!result) {
10023 err = IE_ERROR;
10024 goto leave;
10026 /* Empty message */
10027 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10028 isds_printf_message(context,
10029 _("XML document does not contain "
10030 "sisds:dmReturnedMessage element"));
10031 err = IE_ISDS;
10032 goto leave;
10034 /* More messages */
10035 if (result->nodesetval->nodeNr > 1) {
10036 isds_printf_message(context,
10037 _("XML document has more sisds:dmReturnedMessage elements"));
10038 err = IE_ISDS;
10039 goto leave;
10041 /* One message */
10042 xpath_ctx->node = result->nodesetval->nodeTab[0];
10044 /* Extract the message */
10045 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10046 if (err) goto leave;
10048 /* Append raw buffer into message */
10049 (*message)->raw_type = raw_type;
10050 switch (strategy) {
10051 case BUFFER_DONT_STORE:
10052 break;
10053 case BUFFER_COPY:
10054 (*message)->raw = malloc(length);
10055 if (!(*message)->raw) {
10056 err = IE_NOMEM;
10057 goto leave;
10059 memcpy((*message)->raw, buffer, length);
10060 (*message)->raw_length = length;
10061 break;
10062 case BUFFER_MOVE:
10063 (*message)->raw = (void *) buffer;
10064 (*message)->raw_length = length;
10065 break;
10066 default:
10067 err = IE_ENUM;
10068 goto leave;
10072 leave:
10073 if (err) {
10074 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10075 isds_message_free(message);
10078 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10079 xmlXPathFreeObject(result);
10080 xmlXPathFreeContext(xpath_ctx);
10081 if (!*message || !(*message)->xml) {
10082 xmlFreeDoc(message_doc);
10085 if (!err)
10086 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10087 return err;
10091 /* Determine type of raw message or delivery info according some heuristics.
10092 * It does not validate the raw blob.
10093 * @context is session context
10094 * @raw_type returns content type of @buffer. Valid only if exit code of this
10095 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10096 * reallocated memory.
10097 * @buffer is message raw representation.
10098 * @length is length of buffer in bytes. */
10099 isds_error isds_guess_raw_type(struct isds_ctx *context,
10100 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10101 isds_error err;
10102 void *xml_stream = NULL;
10103 size_t xml_stream_length = 0;
10104 xmlDocPtr document = NULL;
10105 xmlNodePtr root = NULL;
10107 if (!context) return IE_INVALID_CONTEXT;
10108 zfree(context->long_message);
10109 if (length == 0 || !buffer) return IE_INVAL;
10110 if (!raw_type) return IE_INVAL;
10112 /* Try CMS */
10113 err = _isds_extract_cms_data(context, buffer, length,
10114 &xml_stream, &xml_stream_length);
10115 if (err) {
10116 xml_stream = (void *) buffer;
10117 xml_stream_length = (size_t) length;
10118 err = IE_SUCCESS;
10121 /* Try XML */
10122 document = xmlParseMemory(xml_stream, xml_stream_length);
10123 if (!document) {
10124 isds_printf_message(context,
10125 _("Could not parse data as XML document"));
10126 err = IE_NOTSUP;
10127 goto leave;
10130 /* Get root element */
10131 root = xmlDocGetRootElement(document);
10132 if (!root) {
10133 isds_printf_message(context,
10134 _("XML document is missing root element"));
10135 err = IE_XML;
10136 goto leave;
10139 if (!root->ns || !root->ns->href) {
10140 isds_printf_message(context,
10141 _("Root element does not belong to any name space"));
10142 err = IE_NOTSUP;
10143 goto leave;
10146 /* Test name space */
10147 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10148 if (xml_stream == buffer)
10149 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10150 else
10151 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10152 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10153 if (xml_stream == buffer)
10154 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10155 else
10156 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10157 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10158 if (xml_stream == buffer)
10159 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10160 else
10161 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10162 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10163 if (xml_stream != buffer) {
10164 isds_printf_message(context,
10165 _("Document in ISDS name space is encapsulated into CMS" ));
10166 err = IE_NOTSUP;
10167 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10168 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10169 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10170 *raw_type = RAWTYPE_DELIVERYINFO;
10171 else {
10172 isds_printf_message(context,
10173 _("Unknown root element in ISDS name space"));
10174 err = IE_NOTSUP;
10176 } else {
10177 isds_printf_message(context,
10178 _("Unknown name space"));
10179 err = IE_NOTSUP;
10182 leave:
10183 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10184 xmlFreeDoc(document);
10185 return err;
10189 /* Download signed incoming/outgoing message identified by ID.
10190 * @context is session context
10191 * @output is true for outgoing message, false for incoming message
10192 * @message_id is message identifier (you can get them from
10193 * isds_get_list_of_{sent,received}_messages())
10194 * @message is automatically reallocated message retrieved from ISDS. The raw
10195 * member will be filled with PKCS#7 structure in DER format. */
10196 static isds_error isds_get_signed_message(struct isds_ctx *context,
10197 const _Bool outgoing, const char *message_id,
10198 struct isds_message **message) {
10200 isds_error err = IE_SUCCESS;
10201 #if HAVE_LIBCURL
10202 xmlDocPtr response = NULL;
10203 xmlChar *code = NULL, *status_message = NULL;
10204 xmlXPathContextPtr xpath_ctx = NULL;
10205 xmlXPathObjectPtr result = NULL;
10206 char *encoded_structure = NULL;
10207 void *raw = NULL;
10208 size_t raw_length = 0;
10209 #endif
10211 if (!context) return IE_INVALID_CONTEXT;
10212 zfree(context->long_message);
10213 if (!message) return IE_INVAL;
10214 isds_message_free(message);
10216 #if HAVE_LIBCURL
10217 /* Do request and check for success */
10218 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10219 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10220 BAD_CAST "SignedMessageDownload",
10221 message_id, &response, NULL, NULL, &code, &status_message);
10222 if (err) goto leave;
10224 /* Find signed message, extract it into raw and maybe free
10225 * response */
10226 err = find_extract_signed_data_free_response(context,
10227 (xmlChar *)message_id, &response,
10228 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10229 BAD_CAST "SignedMessageDownload",
10230 &raw, &raw_length);
10231 if (err) goto leave;
10233 /* Parse message */
10234 err = isds_load_message(context,
10235 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10236 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10237 raw, raw_length, message, BUFFER_MOVE);
10238 if (err) goto leave;
10240 raw = NULL;
10242 leave:
10243 if (err) {
10244 isds_message_free(message);
10247 free(encoded_structure);
10248 xmlXPathFreeObject(result);
10249 xmlXPathFreeContext(xpath_ctx);
10250 free(raw);
10252 free(code);
10253 free(status_message);
10254 xmlFreeDoc(response);
10256 if (!err)
10257 isds_log(ILF_ISDS, ILL_DEBUG,
10258 (outgoing) ?
10259 _("SignedSentMessageDownload request processed by server "
10260 "successfully.\n") :
10261 _("SignedMessageDownload request processed by server "
10262 "successfully.\n")
10264 #else /* not HAVE_LIBCURL */
10265 err = IE_NOTSUP;
10266 #endif
10267 return err;
10271 /* Download signed incoming message identified by ID.
10272 * @context is session context
10273 * @message_id is message identifier (you can get them from
10274 * isds_get_list_of_received_messages())
10275 * @message is automatically reallocated message retrieved from ISDS. The raw
10276 * member will be filled with PKCS#7 structure in DER format. */
10277 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10278 const char *message_id, struct isds_message **message) {
10279 return isds_get_signed_message(context, 0, message_id, message);
10283 /* Download signed outgoing message identified by ID.
10284 * @context is session context
10285 * @message_id is message identifier (you can get them from
10286 * isds_get_list_of_sent_messages())
10287 * @message is automatically reallocated message retrieved from ISDS. The raw
10288 * member will be filled with PKCS#7 structure in DER format. */
10289 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10290 const char *message_id, struct isds_message **message) {
10291 return isds_get_signed_message(context, 1, message_id, message);
10295 /* Get type and name of user who sent a message identified by ID.
10296 * @context is session context
10297 * @message_id is message identifier
10298 * @sender_type is pointer to automatically allocated type of sender detected
10299 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10300 * library or to the server, NULL will be returned. Pass NULL if you don't
10301 * care about it.
10302 * @raw_sender_type is automatically reallocated UTF-8 string describing
10303 * sender type or NULL if not known to server. Pass NULL if you don't care.
10304 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10305 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10306 isds_error isds_get_message_sender(struct isds_ctx *context,
10307 const char *message_id, isds_sender_type **sender_type,
10308 char **raw_sender_type, char **sender_name) {
10309 isds_error err = IE_SUCCESS;
10310 #if HAVE_LIBCURL
10311 xmlDocPtr response = NULL;
10312 xmlChar *code = NULL, *status_message = NULL;
10313 xmlXPathContextPtr xpath_ctx = NULL;
10314 xmlXPathObjectPtr result = NULL;
10315 char *type_string = NULL;
10316 #endif
10318 if (!context) return IE_INVALID_CONTEXT;
10319 zfree(context->long_message);
10320 if (sender_type) zfree(*sender_type);
10321 if (raw_sender_type) zfree(*raw_sender_type);
10322 if (sender_name) zfree(*sender_name);
10323 if (!message_id) return IE_INVAL;
10325 #if HAVE_LIBCURL
10326 /* Do request and check for success */
10327 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10328 BAD_CAST "GetMessageAuthor",
10329 message_id, &response, NULL, NULL, &code, &status_message);
10330 if (err) goto leave;
10332 /* Extract data */
10333 xpath_ctx = xmlXPathNewContext(response);
10334 if (!xpath_ctx) {
10335 err = IE_ERROR;
10336 goto leave;
10338 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10339 err = IE_ERROR;
10340 goto leave;
10342 result = xmlXPathEvalExpression(
10343 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10344 if (!result) {
10345 err = IE_ERROR;
10346 goto leave;
10348 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10349 isds_log_message(context,
10350 _("Missing GetMessageAuthorResponse element"));
10351 err = IE_ISDS;
10352 goto leave;
10354 if (result->nodesetval->nodeNr > 1) {
10355 isds_log_message(context,
10356 _("Multiple GetMessageAuthorResponse element"));
10357 err = IE_ISDS;
10358 goto leave;
10360 xpath_ctx->node = result->nodesetval->nodeTab[0];
10361 xmlXPathFreeObject(result); result = NULL;
10363 /* Fill output arguments in */
10364 EXTRACT_STRING("isds:userType", type_string);
10365 if (NULL != type_string) {
10366 if (NULL != sender_type) {
10367 *sender_type = calloc(1, sizeof(**sender_type));
10368 if (NULL == *sender_type) {
10369 err = IE_NOMEM;
10370 goto leave;
10373 err = string2isds_sender_type((xmlChar *)type_string,
10374 *sender_type);
10375 if (err) {
10376 zfree(*sender_type);
10377 if (err == IE_ENUM) {
10378 err = IE_SUCCESS;
10379 char *type_string_locale = _isds_utf82locale(type_string);
10380 isds_log(ILF_ISDS, ILL_WARNING,
10381 _("Unknown isds:userType value: %s"),
10382 type_string_locale);
10383 free(type_string_locale);
10388 if (NULL != sender_name)
10389 EXTRACT_STRING("isds:authorName", *sender_name);
10391 leave:
10392 if (err) {
10393 if (NULL != sender_type) zfree(*sender_type);
10394 zfree(type_string);
10395 if (NULL != sender_name) zfree(*sender_name);
10397 if (NULL != raw_sender_type) *raw_sender_type = type_string;
10399 xmlXPathFreeObject(result);
10400 xmlXPathFreeContext(xpath_ctx);
10402 free(code);
10403 free(status_message);
10404 xmlFreeDoc(response);
10406 if (!err)
10407 isds_log(ILF_ISDS, ILL_DEBUG,
10408 _("GetMessageAuthor request processed by server "
10409 "successfully.\n"));
10410 #else /* not HAVE_LIBCURL */
10411 err = IE_NOTSUP;
10412 #endif
10413 return err;
10417 /* Retrieve hash of message identified by ID stored in ISDS.
10418 * @context is session context
10419 * @message_id is message identifier
10420 * @hash is automatically reallocated message hash downloaded from ISDS.
10421 * Message must exist in system and must not be deleted. */
10422 isds_error isds_download_message_hash(struct isds_ctx *context,
10423 const char *message_id, struct isds_hash **hash) {
10425 isds_error err = IE_SUCCESS;
10426 #if HAVE_LIBCURL
10427 xmlDocPtr response = NULL;
10428 xmlChar *code = NULL, *status_message = NULL;
10429 xmlXPathContextPtr xpath_ctx = NULL;
10430 xmlXPathObjectPtr result = NULL;
10431 #endif
10433 if (!context) return IE_INVALID_CONTEXT;
10434 zfree(context->long_message);
10436 isds_hash_free(hash);
10438 #if HAVE_LIBCURL
10439 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10440 BAD_CAST "VerifyMessage", message_id,
10441 &response, NULL, NULL, &code, &status_message);
10442 if (err) goto leave;
10445 /* Extract data */
10446 xpath_ctx = xmlXPathNewContext(response);
10447 if (!xpath_ctx) {
10448 err = IE_ERROR;
10449 goto leave;
10451 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10452 err = IE_ERROR;
10453 goto leave;
10455 result = xmlXPathEvalExpression(
10456 BAD_CAST "/isds:VerifyMessageResponse",
10457 xpath_ctx);
10458 if (!result) {
10459 err = IE_ERROR;
10460 goto leave;
10462 /* Empty response */
10463 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10464 char *message_id_locale = _isds_utf82locale((char*) message_id);
10465 isds_printf_message(context,
10466 _("Server did not return any response for ID `%s' "
10467 "on VerifyMessage request"), message_id_locale);
10468 free(message_id_locale);
10469 err = IE_ISDS;
10470 goto leave;
10472 /* More responses */
10473 if (result->nodesetval->nodeNr > 1) {
10474 char *message_id_locale = _isds_utf82locale((char*) message_id);
10475 isds_printf_message(context,
10476 _("Server did return more responses for ID `%s' "
10477 "on VerifyMessage request"), message_id_locale);
10478 free(message_id_locale);
10479 err = IE_ISDS;
10480 goto leave;
10482 /* One response */
10483 xpath_ctx->node = result->nodesetval->nodeTab[0];
10485 /* Extract the hash */
10486 err = find_and_extract_DmHash(context, hash, xpath_ctx);
10488 leave:
10489 if (err) {
10490 isds_hash_free(hash);
10493 xmlXPathFreeObject(result);
10494 xmlXPathFreeContext(xpath_ctx);
10496 free(code);
10497 free(status_message);
10498 xmlFreeDoc(response);
10500 if (!err)
10501 isds_log(ILF_ISDS, ILL_DEBUG,
10502 _("VerifyMessage request processed by server "
10503 "successfully.\n")
10505 #else /* not HAVE_LIBCURL */
10506 err = IE_NOTSUP;
10507 #endif
10508 return err;
10512 /* Erase message specified by @message_id from long term storage. Other
10513 * message cannot be erased on user request.
10514 * @context is session context
10515 * @message_id is message identifier.
10516 * @incoming is true for incoming message, false for outgoing message.
10517 * @return
10518 * IE_SUCCESS if message has ben removed
10519 * IE_INVAL if message does not exist in long term storage or message
10520 * belongs to different box
10521 * TODO: IE_NOEPRM if user has no permission to erase a message */
10522 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
10523 const char *message_id, _Bool incoming) {
10524 isds_error err = IE_SUCCESS;
10525 #if HAVE_LIBCURL
10526 xmlNodePtr request = NULL, node;
10527 xmlNsPtr isds_ns = NULL;
10528 xmlDocPtr response = NULL;
10529 xmlChar *code = NULL, *status_message = NULL;
10530 #endif
10532 if (!context) return IE_INVALID_CONTEXT;
10533 zfree(context->long_message);
10534 if (NULL == message_id) return IE_INVAL;
10536 /* Check if connection is established
10537 * TODO: This check should be done downstairs. */
10538 if (!context->curl) return IE_CONNECTION_CLOSED;
10540 #if HAVE_LIBCURL
10541 /* Build request */
10542 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
10543 if (!request) {
10544 isds_log_message(context,
10545 _("Could build EraseMessage request"));
10546 return IE_ERROR;
10548 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10549 if(!isds_ns) {
10550 isds_log_message(context, _("Could not create ISDS name space"));
10551 xmlFreeNode(request);
10552 return IE_ERROR;
10554 xmlSetNs(request, isds_ns);
10556 err = validate_message_id_length(context, (xmlChar *) message_id);
10557 if (err) goto leave;
10558 INSERT_STRING(request, "dmID", message_id);
10560 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
10563 /* Send request */
10564 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
10565 "message ID %s to ISDS\n"), message_id);
10566 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
10567 xmlFreeNode(request); request = NULL;
10569 if (err) {
10570 isds_log(ILF_ISDS, ILL_DEBUG,
10571 _("Processing ISDS response on EraseMessage request "
10572 "failed\n"));
10573 goto leave;
10576 /* Check for response status */
10577 err = isds_response_status(context, SERVICE_DM_INFO, response,
10578 &code, &status_message, NULL);
10579 if (err) {
10580 isds_log(ILF_ISDS, ILL_DEBUG,
10581 _("ISDS response on EraseMessage request is missing "
10582 "status\n"));
10583 goto leave;
10586 /* Check server status code */
10587 if (!xmlStrcmp(code, BAD_CAST "1211")) {
10588 isds_log_message(context, _("Message to erase belongs to other box"));
10589 err = IE_INVAL;
10590 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
10591 isds_log_message(context, _("Message to erase is not saved in "
10592 "long term storage or the direction does not match"));
10593 err = IE_INVAL;
10594 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
10595 char *code_locale = _isds_utf82locale((char*) code);
10596 char *message_locale = _isds_utf82locale((char*) status_message);
10597 isds_log(ILF_ISDS, ILL_DEBUG,
10598 _("Server refused EraseMessage request "
10599 "(code=%s, message=%s)\n"),
10600 code_locale, message_locale);
10601 isds_log_message(context, message_locale);
10602 free(code_locale);
10603 free(message_locale);
10604 err = IE_ISDS;
10605 goto leave;
10608 leave:
10609 free(code);
10610 free(status_message);
10611 xmlFreeDoc(response);
10612 xmlFreeNode(request);
10614 if (!err)
10615 isds_log(ILF_ISDS, ILL_DEBUG,
10616 _("EraseMessage request processed by server "
10617 "successfully.\n")
10619 #else /* not HAVE_LIBCURL */
10620 err = IE_NOTSUP;
10621 #endif
10622 return err;
10626 /* Mark message as read. This is a transactional commit function to acknowledge
10627 * to ISDS the message has been downloaded and processed by client properly.
10628 * @context is session context
10629 * @message_id is message identifier. */
10630 isds_error isds_mark_message_read(struct isds_ctx *context,
10631 const char *message_id) {
10633 isds_error err = IE_SUCCESS;
10634 #if HAVE_LIBCURL
10635 xmlDocPtr response = NULL;
10636 xmlChar *code = NULL, *status_message = NULL;
10637 #endif
10639 if (!context) return IE_INVALID_CONTEXT;
10640 zfree(context->long_message);
10642 #if HAVE_LIBCURL
10643 /* Do request and check for success */
10644 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10645 BAD_CAST "MarkMessageAsDownloaded", message_id,
10646 &response, NULL, NULL, &code, &status_message);
10648 free(code);
10649 free(status_message);
10650 xmlFreeDoc(response);
10652 if (!err)
10653 isds_log(ILF_ISDS, ILL_DEBUG,
10654 _("MarkMessageAsDownloaded request processed by server "
10655 "successfully.\n")
10657 #else /* not HAVE_LIBCURL */
10658 err = IE_NOTSUP;
10659 #endif
10660 return err;
10664 /* Mark message as received by recipient. This is applicable only to
10665 * commercial message. Use envelope->dmType message member to distinguish
10666 * commercial message from government message. Government message is
10667 * received automatically (by law), commercial message on recipient request.
10668 * @context is session context
10669 * @message_id is message identifier. */
10670 isds_error isds_mark_message_received(struct isds_ctx *context,
10671 const char *message_id) {
10673 isds_error err = IE_SUCCESS;
10674 #if HAVE_LIBCURL
10675 xmlDocPtr response = NULL;
10676 xmlChar *code = NULL, *status_message = NULL;
10677 #endif
10679 if (!context) return IE_INVALID_CONTEXT;
10680 zfree(context->long_message);
10682 #if HAVE_LIBCURL
10683 /* Do request and check for success */
10684 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10685 BAD_CAST "ConfirmDelivery", message_id,
10686 &response, NULL, NULL, &code, &status_message);
10688 free(code);
10689 free(status_message);
10690 xmlFreeDoc(response);
10692 if (!err)
10693 isds_log(ILF_ISDS, ILL_DEBUG,
10694 _("ConfirmDelivery request processed by server "
10695 "successfully.\n")
10697 #else /* not HAVE_LIBCURL */
10698 err = IE_NOTSUP;
10699 #endif
10700 return err;
10704 /* Send document for authorized conversion into Czech POINT system.
10705 * This is public anonymous service, no log-in necessary. Special context is
10706 * used to reuse keep-a-live HTTPS connection.
10707 * @context is Czech POINT session context. DO NOT use context connected to
10708 * ISDS server. Use new context or context used by this function previously.
10709 * @document is document to convert. Only data, data_length, dmFileDescr and
10710 * is_xml members are significant. Be ware that not all document formats can be
10711 * converted (signed PDF 1.3 and higher only (2010-02 state)).
10712 * @id is reallocated identifier assigned by Czech POINT system to
10713 * your document on submit. Use is to tell it to Czech POINT officer.
10714 * @date is reallocated document submit date (submitted documents
10715 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
10716 * value. */
10717 isds_error czp_convert_document(struct isds_ctx *context,
10718 const struct isds_document *document,
10719 char **id, struct tm **date) {
10720 isds_error err = IE_SUCCESS;
10721 #if HAVE_LIBCURL
10722 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
10723 xmlNodePtr request = NULL, node;
10724 xmlDocPtr response = NULL;
10726 xmlXPathContextPtr xpath_ctx = NULL;
10727 xmlXPathObjectPtr result = NULL;
10728 long int status = -1;
10729 long int *status_ptr = &status;
10730 char *string = NULL;
10731 #endif
10734 if (!context) return IE_INVALID_CONTEXT;
10735 zfree(context->long_message);
10736 if (!document || !id || !date) return IE_INVAL;
10738 if (document->is_xml) {
10739 isds_log_message(context,
10740 _("XML documents cannot be submitted to conversion"));
10741 return IE_NOTSUP;
10744 /* Free output arguments */
10745 zfree(*id);
10746 zfree(*date);
10748 #if HAVE_LIBCURL
10749 /* Store configuration */
10750 context->type = CTX_TYPE_CZP;
10751 free(context->url);
10752 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
10753 if (!(context->url))
10754 return IE_NOMEM;
10756 /* Prepare CURL handle if not yet connected */
10757 if (!context->curl) {
10758 context->curl = curl_easy_init();
10759 if (!(context->curl))
10760 return IE_ERROR;
10763 /* Build conversion request */
10764 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
10765 if (!request) {
10766 isds_log_message(context,
10767 _("Could not build Czech POINT conversion request"));
10768 return IE_ERROR;
10770 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
10771 if(!deposit_ns) {
10772 isds_log_message(context,
10773 _("Could not create Czech POINT deposit name space"));
10774 xmlFreeNode(request);
10775 return IE_ERROR;
10777 xmlSetNs(request, deposit_ns);
10779 /* Insert children. They are in empty namespace! */
10780 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
10781 if(!empty_ns) {
10782 isds_log_message(context, _("Could not create empty name space"));
10783 err = IE_ERROR;
10784 goto leave;
10786 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
10787 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
10788 document->dmFileDescr);
10790 /* Document encoded in Base64 */
10791 err = insert_base64_encoded_string(context, request, empty_ns, "document",
10792 document->data, document->data_length);
10793 if (err) goto leave;
10795 isds_log(ILF_ISDS, ILL_DEBUG,
10796 _("Submitting document for conversion into Czech POINT deposit"));
10798 /* Send conversion request */
10799 err = _czp_czpdeposit(context, request, &response);
10800 xmlFreeNode(request); request = NULL;
10802 if (err) {
10803 czp_do_close_connection(context);
10804 goto leave;
10808 /* Extract response */
10809 xpath_ctx = xmlXPathNewContext(response);
10810 if (!xpath_ctx) {
10811 err = IE_ERROR;
10812 goto leave;
10814 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10815 err = IE_ERROR;
10816 goto leave;
10818 result = xmlXPathEvalExpression(
10819 BAD_CAST "/deposit:saveDocumentResponse/return",
10820 xpath_ctx);
10821 if (!result) {
10822 err = IE_ERROR;
10823 goto leave;
10825 /* Empty response */
10826 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10827 isds_printf_message(context,
10828 _("Missing `return' element in Czech POINT deposit response"));
10829 err = IE_ISDS;
10830 goto leave;
10832 /* More responses */
10833 if (result->nodesetval->nodeNr > 1) {
10834 isds_printf_message(context,
10835 _("Multiple `return' element in Czech POINT deposit response"));
10836 err = IE_ISDS;
10837 goto leave;
10839 /* One response */
10840 xpath_ctx->node = result->nodesetval->nodeTab[0];
10842 /* Get status */
10843 EXTRACT_LONGINT("status", status_ptr, 1);
10844 if (status) {
10845 EXTRACT_STRING("statusMsg", string);
10846 char *string_locale = _isds_utf82locale(string);
10847 isds_printf_message(context,
10848 _("Czech POINT deposit refused document for conversion "
10849 "(code=%ld, message=%s)"),
10850 status, string_locale);
10851 free(string_locale);
10852 err = IE_ISDS;
10853 goto leave;
10856 /* Get document ID */
10857 EXTRACT_STRING("documentID", *id);
10859 /* Get submit date */
10860 EXTRACT_STRING("dateInserted", string);
10861 if (string) {
10862 *date = calloc(1, sizeof(**date));
10863 if (!*date) {
10864 err = IE_NOMEM;
10865 goto leave;
10867 err = _isds_datestring2tm((xmlChar *)string, *date);
10868 if (err) {
10869 if (err == IE_NOTSUP) {
10870 err = IE_ISDS;
10871 char *string_locale = _isds_utf82locale(string);
10872 isds_printf_message(context,
10873 _("Invalid dateInserted value: %s"), string_locale);
10874 free(string_locale);
10876 goto leave;
10880 leave:
10881 free(string);
10882 xmlXPathFreeObject(result);
10883 xmlXPathFreeContext(xpath_ctx);
10885 xmlFreeDoc(response);
10886 xmlFreeNode(request);
10888 if (!err) {
10889 char *id_locale = _isds_utf82locale((char *) *id);
10890 isds_log(ILF_ISDS, ILL_DEBUG,
10891 _("Document %s has been submitted for conversion "
10892 "to server successfully\n"), id_locale);
10893 free(id_locale);
10895 #else /* not HAVE_LIBCURL */
10896 err = IE_NOTSUP;
10897 #endif
10898 return err;
10902 /* Close possibly opened connection to Czech POINT document deposit.
10903 * @context is Czech POINT session context. */
10904 isds_error czp_close_connection(struct isds_ctx *context) {
10905 if (!context) return IE_INVALID_CONTEXT;
10906 zfree(context->long_message);
10907 #if HAVE_LIBCURL
10908 return czp_do_close_connection(context);
10909 #else
10910 return IE_NOTSUP;
10911 #endif
10915 /* Send request for new box creation in testing ISDS instance.
10916 * It's not possible to request for a production box currently, as it
10917 * communicates via e-mail.
10918 * XXX: This function does not work either. Server complains about invalid
10919 * e-mail address.
10920 * XXX: Remove context->type hacks in isds.c and validator.c when removing
10921 * this function
10922 * @context is special session context for box creation request. DO NOT use
10923 * standard context as it could reveal your password. Use fresh new context or
10924 * context previously used by this function.
10925 * @box is box description to create including single primary user (in case of
10926 * FO box type). It outputs box ID assigned by ISDS in dbID element.
10927 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
10928 * box, or contact address of PFO box owner). The email member is mandatory as
10929 * it will be used to deliver credentials.
10930 * @former_names is former name of box owner. Pass NULL if you don't care.
10931 * @approval is optional external approval of box manipulation
10932 * @refnumber is reallocated serial number of request assigned by ISDS. Use
10933 * NULL, if you don't care.*/
10934 isds_error isds_request_new_testing_box(struct isds_ctx *context,
10935 struct isds_DbOwnerInfo *box, const struct isds_list *users,
10936 const char *former_names, const struct isds_approval *approval,
10937 char **refnumber) {
10938 isds_error err = IE_SUCCESS;
10939 #if HAVE_LIBCURL
10940 xmlNodePtr request = NULL;
10941 xmlDocPtr response = NULL;
10942 xmlXPathContextPtr xpath_ctx = NULL;
10943 xmlXPathObjectPtr result = NULL;
10944 #endif
10947 if (!context) return IE_INVALID_CONTEXT;
10948 zfree(context->long_message);
10949 if (!box) return IE_INVAL;
10951 #if HAVE_LIBCURL
10952 if (!box->email || box->email[0] == '\0') {
10953 isds_log_message(context, _("E-mail field is mandatory"));
10954 return IE_INVAL;
10957 /* Scratch box ID */
10958 zfree(box->dbID);
10960 /* Store configuration */
10961 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
10962 free(context->url);
10963 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
10964 if (!(context->url))
10965 return IE_NOMEM;
10967 /* Prepare CURL handle if not yet connected */
10968 if (!context->curl) {
10969 context->curl = curl_easy_init();
10970 if (!(context->curl))
10971 return IE_ERROR;
10974 /* Build CreateDataBox request */
10975 err = build_CreateDBInput_request(context,
10976 &request, BAD_CAST "CreateDataBox",
10977 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
10978 if (err) goto leave;
10980 /* Send it to server and process response */
10981 err = send_destroy_request_check_response(context,
10982 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
10983 &response, (xmlChar **) refnumber, NULL);
10984 if (err) goto leave;
10986 /* Extract box ID */
10987 xpath_ctx = xmlXPathNewContext(response);
10988 if (!xpath_ctx) {
10989 err = IE_ERROR;
10990 goto leave;
10992 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10993 err = IE_ERROR;
10994 goto leave;
10996 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
10998 leave:
10999 xmlXPathFreeObject(result);
11000 xmlXPathFreeContext(xpath_ctx);
11001 xmlFreeDoc(response);
11002 xmlFreeNode(request);
11004 if (!err) {
11005 isds_log(ILF_ISDS, ILL_DEBUG,
11006 _("CreateDataBox request processed by server successfully.\n"));
11008 #else /* not HAVE_LIBCURL */
11009 err = IE_NOTSUP;
11010 #endif
11012 return err;
11016 /* Submit CMS signed message to ISDS to verify its originality. This is
11017 * stronger form of isds_verify_message_hash() because ISDS does more checks
11018 * than simple one (potentialy old weak) hash comparison.
11019 * @context is session context
11020 * @message is memory with raw CMS signed message bit stream
11021 * @length is @message size in bytes
11022 * @return
11023 * IE_SUCCESS if message originates in ISDS
11024 * IE_NOTEQUAL if message is unknown to ISDS
11025 * other code for other errors */
11026 isds_error isds_authenticate_message(struct isds_ctx *context,
11027 const void *message, size_t length) {
11028 isds_error err = IE_SUCCESS;
11029 #if HAVE_LIBCURL
11030 xmlNsPtr isds_ns = NULL;
11031 xmlNodePtr request = NULL;
11032 xmlDocPtr response = NULL;
11033 xmlXPathContextPtr xpath_ctx = NULL;
11034 xmlXPathObjectPtr result = NULL;
11035 _Bool *authentic = NULL;
11036 #endif
11038 if (!context) return IE_INVALID_CONTEXT;
11039 zfree(context->long_message);
11040 if (!message || length == 0) return IE_INVAL;
11042 #if HAVE_LIBCURL
11043 /* Check if connection is established
11044 * TODO: This check should be done downstairs. */
11045 if (!context->curl) return IE_CONNECTION_CLOSED;
11048 /* Build AuthenticateMessage request */
11049 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11050 if (!request) {
11051 isds_log_message(context,
11052 _("Could not build AuthenticateMessage request"));
11053 return IE_ERROR;
11055 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11056 if(!isds_ns) {
11057 isds_log_message(context, _("Could not create ISDS name space"));
11058 xmlFreeNode(request);
11059 return IE_ERROR;
11061 xmlSetNs(request, isds_ns);
11063 /* Insert Base64 encoded message */
11064 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11065 message, length);
11066 if (err) goto leave;
11068 /* Send request to server and process response */
11069 err = send_destroy_request_check_response(context,
11070 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11071 &response, NULL, NULL);
11072 if (err) goto leave;
11075 /* ISDS has decided */
11076 xpath_ctx = xmlXPathNewContext(response);
11077 if (!xpath_ctx) {
11078 err = IE_ERROR;
11079 goto leave;
11081 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11082 err = IE_ERROR;
11083 goto leave;
11086 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11088 if (!authentic) {
11089 isds_log_message(context,
11090 _("Server did not return any response on "
11091 "AuthenticateMessage request"));
11092 err = IE_ISDS;
11093 goto leave;
11095 if (*authentic) {
11096 isds_log(ILF_ISDS, ILL_DEBUG,
11097 _("ISDS authenticated the message successfully\n"));
11098 } else {
11099 isds_log_message(context, _("ISDS does not know the message"));
11100 err = IE_NOTEQUAL;
11104 leave:
11105 free(authentic);
11106 xmlXPathFreeObject(result);
11107 xmlXPathFreeContext(xpath_ctx);
11109 xmlFreeDoc(response);
11110 xmlFreeNode(request);
11111 #else /* not HAVE_LIBCURL */
11112 err = IE_NOTSUP;
11113 #endif
11115 return err;
11119 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11120 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11121 * be re-signed.
11122 * @context is session context
11123 * @input_data is memory with raw CMS signed message or delivery info bit
11124 * stream to re-sign
11125 * @input_length is @input_data size in bytes
11126 * @output_data is pointer to auto-allocated memory where to store re-signed
11127 * input data blob. Caller must free it.
11128 * @output_data is pointer where to store @output_data size in bytes
11129 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11130 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11131 * @return
11132 * IE_SUCCESS if CMS blob has been re-signed successfully
11133 * other code for other errors */
11134 isds_error isds_resign_message(struct isds_ctx *context,
11135 const void *input_data, size_t input_length,
11136 void **output_data, size_t *output_length, struct tm **valid_to) {
11137 isds_error err = IE_SUCCESS;
11138 #if HAVE_LIBCURL
11139 xmlNsPtr isds_ns = NULL;
11140 xmlNodePtr request = NULL;
11141 xmlDocPtr response = NULL;
11142 xmlXPathContextPtr xpath_ctx = NULL;
11143 xmlXPathObjectPtr result = NULL;
11144 char *string = NULL;
11145 const xmlChar *codes[] = {
11146 BAD_CAST "2200",
11147 BAD_CAST "2201",
11148 BAD_CAST "2204",
11149 BAD_CAST "2207",
11150 NULL
11152 const char *meanings[] = {
11153 "Message is bad",
11154 "Message is not original",
11155 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11156 "Time stamp could not been generated in time"
11158 const isds_error errors[] = {
11159 IE_INVAL,
11160 IE_NOTUNIQ,
11161 IE_INVAL,
11162 IE_ISDS,
11164 struct code_map_isds_error map = {
11165 .codes = codes,
11166 .meanings = meanings,
11167 .errors = errors
11169 #endif
11171 if (NULL != output_data) *output_data = NULL;
11172 if (NULL != output_length) *output_length = 0;
11173 if (NULL != valid_to) *valid_to = NULL;
11175 if (NULL == context) return IE_INVALID_CONTEXT;
11176 zfree(context->long_message);
11177 if (NULL == input_data || 0 == input_length) {
11178 isds_log_message(context, _("Empty CMS blob on input"));
11179 return IE_INVAL;
11181 if (NULL == output_data || NULL == output_length) {
11182 isds_log_message(context,
11183 _("NULL pointer provided for output CMS blob"));
11184 return IE_INVAL;
11187 #if HAVE_LIBCURL
11188 /* Check if connection is established
11189 * TODO: This check should be done downstairs. */
11190 if (!context->curl) return IE_CONNECTION_CLOSED;
11193 /* Build Re-signISDSDocument request */
11194 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11195 if (!request) {
11196 isds_log_message(context,
11197 _("Could not build Re-signISDSDocument request"));
11198 return IE_ERROR;
11200 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11201 if(!isds_ns) {
11202 isds_log_message(context, _("Could not create ISDS name space"));
11203 xmlFreeNode(request);
11204 return IE_ERROR;
11206 xmlSetNs(request, isds_ns);
11208 /* Insert Base64 encoded CMS blob */
11209 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11210 input_data, input_length);
11211 if (err) goto leave;
11213 /* Send request to server and process response */
11214 err = send_destroy_request_check_response(context,
11215 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11216 &response, NULL, &map);
11217 if (err) goto leave;
11220 /* Extract re-signed data */
11221 xpath_ctx = xmlXPathNewContext(response);
11222 if (!xpath_ctx) {
11223 err = IE_ERROR;
11224 goto leave;
11226 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11227 err = IE_ERROR;
11228 goto leave;
11230 result = xmlXPathEvalExpression(
11231 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11232 if (!result) {
11233 err = IE_ERROR;
11234 goto leave;
11236 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11237 isds_log_message(context,
11238 _("Missing Re-signISDSDocumentResponse element"));
11239 err = IE_ISDS;
11240 goto leave;
11242 if (result->nodesetval->nodeNr > 1) {
11243 isds_log_message(context,
11244 _("Multiple Re-signISDSDocumentResponse element"));
11245 err = IE_ISDS;
11246 goto leave;
11248 xpath_ctx->node = result->nodesetval->nodeTab[0];
11249 xmlXPathFreeObject(result); result = NULL;
11251 EXTRACT_STRING("isds:dmResultDoc", string);
11252 /* Decode non-empty data */
11253 if (NULL != string && string[0] != '\0') {
11254 *output_length = _isds_b64decode(string, output_data);
11255 if (*output_length == (size_t) -1) {
11256 isds_log_message(context,
11257 _("Error while Base64-decoding re-signed data"));
11258 err = IE_ERROR;
11259 goto leave;
11261 } else {
11262 isds_log_message(context, _("Server did not send re-signed data"));
11263 err = IE_ISDS;
11264 goto leave;
11266 zfree(string);
11268 if (NULL != valid_to) {
11269 /* Get time stamp expiration date */
11270 EXTRACT_STRING("isds:dmValidTo", string);
11271 if (NULL != string) {
11272 *valid_to = calloc(1, sizeof(**valid_to));
11273 if (!*valid_to) {
11274 err = IE_NOMEM;
11275 goto leave;
11277 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11278 if (err) {
11279 if (err == IE_NOTSUP) {
11280 err = IE_ISDS;
11281 char *string_locale = _isds_utf82locale(string);
11282 isds_printf_message(context,
11283 _("Invalid dmValidTo value: %s"), string_locale);
11284 free(string_locale);
11286 goto leave;
11291 leave:
11292 free(string);
11294 xmlXPathFreeObject(result);
11295 xmlXPathFreeContext(xpath_ctx);
11297 xmlFreeDoc(response);
11298 xmlFreeNode(request);
11299 #else /* not HAVE_LIBCURL */
11300 err = IE_NOTSUP;
11301 #endif
11303 return err;
11306 #undef INSERT_ELEMENT
11307 #undef CHECK_FOR_STRING_LENGTH
11308 #undef INSERT_STRING_ATTRIBUTE
11309 #undef INSERT_ULONGINTNOPTR
11310 #undef INSERT_ULONGINT
11311 #undef INSERT_LONGINT
11312 #undef INSERT_BOOLEAN
11313 #undef INSERT_SCALAR_BOOLEAN
11314 #undef INSERT_STRING
11315 #undef INSERT_STRING_WITH_NS
11316 #undef EXTRACT_STRING_ATTRIBUTE
11317 #undef EXTRACT_ULONGINT
11318 #undef EXTRACT_LONGINT
11319 #undef EXTRACT_BOOLEAN
11320 #undef EXTRACT_STRING
11323 /* Compute hash of message from raw representation and store it into envelope.
11324 * Original hash structure will be destroyed in envelope.
11325 * @context is session context
11326 * @message is message carrying raw XML message blob
11327 * @algorithm is desired hash algorithm to use */
11328 isds_error isds_compute_message_hash(struct isds_ctx *context,
11329 struct isds_message *message, const isds_hash_algorithm algorithm) {
11330 isds_error err = IE_SUCCESS;
11331 const char *nsuri;
11332 void *xml_stream = NULL;
11333 size_t xml_stream_length;
11334 size_t phys_start, phys_end;
11335 char *phys_path = NULL;
11336 struct isds_hash *new_hash = NULL;
11339 if (!context) return IE_INVALID_CONTEXT;
11340 zfree(context->long_message);
11341 if (!message) return IE_INVAL;
11343 if (!message->raw) {
11344 isds_log_message(context,
11345 _("Message does not carry raw representation"));
11346 return IE_INVAL;
11349 switch (message->raw_type) {
11350 case RAWTYPE_INCOMING_MESSAGE:
11351 nsuri = ISDS_NS;
11352 xml_stream = message->raw;
11353 xml_stream_length = message->raw_length;
11354 break;
11356 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11357 nsuri = SISDS_INCOMING_NS;
11358 xml_stream = message->raw;
11359 xml_stream_length = message->raw_length;
11360 break;
11362 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11363 nsuri = SISDS_INCOMING_NS;
11364 err = _isds_extract_cms_data(context,
11365 message->raw, message->raw_length,
11366 &xml_stream, &xml_stream_length);
11367 if (err) goto leave;
11368 break;
11370 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11371 nsuri = SISDS_OUTGOING_NS;
11372 xml_stream = message->raw;
11373 xml_stream_length = message->raw_length;
11374 break;
11376 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11377 nsuri = SISDS_OUTGOING_NS;
11378 err = _isds_extract_cms_data(context,
11379 message->raw, message->raw_length,
11380 &xml_stream, &xml_stream_length);
11381 if (err) goto leave;
11382 break;
11384 default:
11385 isds_log_message(context, _("Bad raw representation type"));
11386 return IE_INVAL;
11387 break;
11391 /* XXX: Hash is computed from original string representing isds:dmDm
11392 * subtree. That means no encoding, white space, xmlns attributes changes.
11393 * In other words, input for hash can be invalid XML stream. */
11394 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
11395 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11396 PHYSXML_ELEMENT_SEPARATOR,
11397 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
11398 PHYSXML_ELEMENT_SEPARATOR
11399 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
11400 err = IE_NOMEM;
11401 goto leave;
11403 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11404 phys_path, &phys_start, &phys_end);
11405 zfree(phys_path);
11406 if (err) {
11407 isds_log_message(context,
11408 _("Substring with isds:dmDM element could not be located "
11409 "in raw message"));
11410 goto leave;
11414 /* Compute hash */
11415 new_hash = calloc(1, sizeof(*new_hash));
11416 if (!new_hash) {
11417 err = IE_NOMEM;
11418 goto leave;
11420 new_hash->algorithm = algorithm;
11421 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
11422 new_hash);
11423 if (err) {
11424 isds_log_message(context, _("Could not compute message hash"));
11425 goto leave;
11428 /* Save computed hash */
11429 if (!message->envelope) {
11430 message->envelope = calloc(1, sizeof(*message->envelope));
11431 if (!message->envelope) {
11432 err = IE_NOMEM;
11433 goto leave;
11436 isds_hash_free(&message->envelope->hash);
11437 message->envelope->hash = new_hash;
11439 leave:
11440 if (err) {
11441 isds_hash_free(&new_hash);
11444 free(phys_path);
11445 if (xml_stream != message->raw) free(xml_stream);
11446 return err;
11450 /* Compare two hashes.
11451 * @h1 is first hash
11452 * @h2 is another hash
11453 * @return
11454 * IE_SUCCESS if hashes equal
11455 * IE_NOTUNIQ if hashes are comparable, but they don't equal
11456 * IE_ENUM if not comparable, but both structures defined
11457 * IE_INVAL if some of the structures are undefined (NULL)
11458 * IE_ERROR if internal error occurs */
11459 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
11460 if (h1 == NULL || h2 == NULL) return IE_INVAL;
11461 if (h1->algorithm != h2->algorithm) return IE_ENUM;
11462 if (h1->length != h2->length) return IE_ERROR;
11463 if (h1->length > 0 && !h1->value) return IE_ERROR;
11464 if (h2->length > 0 && !h2->value) return IE_ERROR;
11466 for (int i = 0; i < h1->length; i++) {
11467 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
11468 return IE_NOTEQUAL;
11470 return IE_SUCCESS;
11474 /* Check message has gone through ISDS by comparing message hash stored in
11475 * ISDS and locally computed hash. You must provide message with valid raw
11476 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
11477 * This is convenient wrapper for isds_download_message_hash(),
11478 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
11479 * @context is session context
11480 * @message is message with valid raw and envelope member; envelope->hash
11481 * member will be changed during function run. Use envelope on heap only.
11482 * @return
11483 * IE_SUCCESS if message originates in ISDS
11484 * IE_NOTEQUAL if message is unknown to ISDS
11485 * other code for other errors */
11486 isds_error isds_verify_message_hash(struct isds_ctx *context,
11487 struct isds_message *message) {
11488 isds_error err = IE_SUCCESS;
11489 struct isds_hash *downloaded_hash = NULL;
11491 if (!context) return IE_INVALID_CONTEXT;
11492 zfree(context->long_message);
11493 if (!message) return IE_INVAL;
11495 if (!message->envelope) {
11496 isds_log_message(context,
11497 _("Given message structure is missing envelope"));
11498 return IE_INVAL;
11500 if (!message->raw) {
11501 isds_log_message(context,
11502 _("Given message structure is missing raw representation"));
11503 return IE_INVAL;
11506 err = isds_download_message_hash(context, message->envelope->dmID,
11507 &downloaded_hash);
11508 if (err) goto leave;
11510 err = isds_compute_message_hash(context, message,
11511 downloaded_hash->algorithm);
11512 if (err) goto leave;
11514 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
11516 leave:
11517 isds_hash_free(&downloaded_hash);
11518 return err;
11522 /* Search for document by document ID in list of documents. IDs are compared
11523 * as UTF-8 string.
11524 * @documents is list of isds_documents
11525 * @id is document identifier
11526 * @return first matching document or NULL. */
11527 const struct isds_document *isds_find_document_by_id(
11528 const struct isds_list *documents, const char *id) {
11529 const struct isds_list *item;
11530 const struct isds_document *document;
11532 for (item = documents; item; item = item->next) {
11533 document = (struct isds_document *) item->data;
11534 if (!document) continue;
11536 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
11537 return document;
11540 return NULL;
11544 /* Normalize @mime_type to be proper MIME type.
11545 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
11546 * guess regular MIME type (e.g. "application/pdf").
11547 * @mime_type is UTF-8 encoded MIME type to fix
11548 * @return original @mime_type if no better interpretation exists, or
11549 * constant static UTF-8 encoded string with proper MIME type. */
11550 const char *isds_normalize_mime_type(const char *mime_type) {
11551 if (!mime_type) return NULL;
11553 for (int offset = 0;
11554 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
11555 offset += 2) {
11556 if (!xmlStrcasecmp((const xmlChar*) mime_type,
11557 extension_map_mime[offset]))
11558 return (const char *) extension_map_mime[offset + 1];
11561 return mime_type;
11565 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
11566 struct isds_message **message);
11567 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
11568 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
11569 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
11570 struct isds_address **address);
11572 int isds_message_free(struct isds_message **message);
11573 int isds_address_free(struct isds_address **address);
11577 /* Makes known all relevant namespaces to given XPath context
11578 * @xpath_ctx is XPath context
11579 * @message_ns selects proper message name space. Unsigned and signed
11580 * messages and delivery info's differ in prefix and URI. */
11581 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
11582 const message_ns_type message_ns) {
11583 const xmlChar *message_namespace = NULL;
11585 if (!xpath_ctx) return IE_ERROR;
11587 switch(message_ns) {
11588 case MESSAGE_NS_1:
11589 message_namespace = BAD_CAST ISDS1_NS; break;
11590 case MESSAGE_NS_UNSIGNED:
11591 message_namespace = BAD_CAST ISDS_NS; break;
11592 case MESSAGE_NS_SIGNED_INCOMING:
11593 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
11594 case MESSAGE_NS_SIGNED_OUTGOING:
11595 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
11596 case MESSAGE_NS_SIGNED_DELIVERY:
11597 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
11598 default:
11599 return IE_ENUM;
11602 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
11603 return IE_ERROR;
11604 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
11605 return IE_ERROR;
11606 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
11607 return IE_ERROR;
11608 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
11609 return IE_ERROR;
11610 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
11611 return IE_ERROR;
11612 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
11613 return IE_ERROR;
11614 return IE_SUCCESS;