test: Check for isds_init() failure in tests
[libisds.git] / src / isds.c
blob0aee0f4242bf2490d5197997febffd611c2c8fe8
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 /* Locators */
20 /* Base URL of production ISDS instance */
21 const char isds_locator[] = "https://ws1.mojedatovaschranka.cz/";
22 const char isds_cert_locator[] = "https://ws1c.mojedatovaschranka.cz/";
23 const char isds_otp_locator[] = "https://www.mojedatovaschranka.cz/";
25 /* Base URL of production ISDS instance */
26 const char isds_testing_locator[] = "https://ws1.czebox.cz/";
27 const char isds_cert_testing_locator[] = "https://ws1c.czebox.cz/";
28 const char isds_otp_testing_locator[] = "https://www.czebox.cz/";
30 /* Extension to MIME type map */
31 static const xmlChar *extension_map_mime[] = {
32 BAD_CAST "cer", BAD_CAST "application/x-x509-ca-cert",
33 BAD_CAST "crt", BAD_CAST "application/x-x509-ca-cert",
34 BAD_CAST "der", BAD_CAST "application/x-x509-ca-cert",
35 BAD_CAST "doc", BAD_CAST "application/msword",
36 BAD_CAST "docx", BAD_CAST "application/vnd.openxmlformats-officedocument."
37 "wordprocessingml.document",
38 BAD_CAST "dbf", BAD_CAST "application/octet-stream",
39 BAD_CAST "prj", BAD_CAST "application/octet-stream",
40 BAD_CAST "qix", BAD_CAST "application/octet-stream",
41 BAD_CAST "sbn", BAD_CAST "application/octet-stream",
42 BAD_CAST "sbx", BAD_CAST "application/octet-stream",
43 BAD_CAST "shp", BAD_CAST "application/octet-stream",
44 BAD_CAST "shx", BAD_CAST "application/octet-stream",
45 BAD_CAST "dgn", BAD_CAST "application/octet-stream",
46 BAD_CAST "dwg", BAD_CAST "image/vnd.dwg",
47 BAD_CAST "edi", BAD_CAST "application/edifact",
48 BAD_CAST "fo", BAD_CAST "application/vnd.software602.filler.form+xml",
49 BAD_CAST "gfs", BAD_CAST "application/xml",
50 BAD_CAST "gml", BAD_CAST "application/xml",
51 BAD_CAST "gif", BAD_CAST "image/gif",
52 BAD_CAST "htm", BAD_CAST "text/html",
53 BAD_CAST "html", BAD_CAST "text/html",
54 BAD_CAST "isdoc", BAD_CAST "text/isdoc",
55 BAD_CAST "isdocx", BAD_CAST "text/isdocx",
56 BAD_CAST "jfif", BAD_CAST "image/jpeg",
57 BAD_CAST "jpg", BAD_CAST "image/jpeg",
58 BAD_CAST "jpeg", BAD_CAST "image/jpeg",
59 BAD_CAST "mpeg", BAD_CAST "video/mpeg",
60 BAD_CAST "mpeg1", BAD_CAST "video/mpeg",
61 BAD_CAST "mpeg2", BAD_CAST "video/mpeg",
62 BAD_CAST "mpg", BAD_CAST "video/mpeg",
63 BAD_CAST "mp2", BAD_CAST "audio/mpeg",
64 BAD_CAST "mp3", BAD_CAST "audio/mpeg",
65 BAD_CAST "odp", BAD_CAST "application/vnd.oasis.opendocument.presentation",
66 BAD_CAST "ods", BAD_CAST "application/vnd.oasis.opendocument.spreadsheet",
67 BAD_CAST "odt", BAD_CAST "application/vnd.oasis.opendocument.text",
68 BAD_CAST "pdf", BAD_CAST "application/pdf",
69 BAD_CAST "p7b", BAD_CAST "application/pkcs7-certificates",
70 BAD_CAST "p7c", BAD_CAST "application/pkcs7-mime",
71 BAD_CAST "p7m", BAD_CAST "application/pkcs7-mime",
72 BAD_CAST "p7f", BAD_CAST "application/pkcs7-signature",
73 BAD_CAST "p7s", BAD_CAST "application/pkcs7-signature",
74 BAD_CAST "pk7", BAD_CAST "application/pkcs7-mime",
75 BAD_CAST "png", BAD_CAST "image/png",
76 BAD_CAST "ppt", BAD_CAST "application/vnd.ms-powerpoint",
77 BAD_CAST "pptx", BAD_CAST "application/vnd.openxmlformats-officedocument."
78 "presentationml.presentation",
79 BAD_CAST "rtf", BAD_CAST "application/rtf",
80 BAD_CAST "tif", BAD_CAST "image/tiff",
81 BAD_CAST "tiff", BAD_CAST "image/tiff",
82 BAD_CAST "tsr", BAD_CAST "application/timestamp-reply",
83 BAD_CAST "tst", BAD_CAST "application/timestamp-reply",
84 BAD_CAST "txt", BAD_CAST "text/plain",
85 BAD_CAST "wav", BAD_CAST "audio/wav",
86 BAD_CAST "xls", BAD_CAST "application/vnd.ms-excel",
87 BAD_CAST "xlsx", BAD_CAST "application/vnd.openxmlformats-officedocument."
88 "spreadsheetml.sheet",
89 BAD_CAST "xml", BAD_CAST "application/xml",
90 BAD_CAST "xsd", BAD_CAST "application/xml",
91 BAD_CAST "zfo", BAD_CAST "application/vnd.software602.filler.form-xml-zip"
94 /* Structure type to hold conversion table from status code to isds_error and
95 * long message */
96 struct code_map_isds_error {
97 const xmlChar **codes; /* NULL terminated array of status codes */
98 const char **meanings; /* Mapping to non-localized long messages */
99 const isds_error *errors; /* Mapping to isds_error code */
102 /* Deallocate structure isds_pki_credentials and NULL it.
103 * Pass-phrase is discarded.
104 * @pki credentials to to free */
105 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
106 if(!pki || !*pki) return;
108 free((*pki)->engine);
109 free((*pki)->certificate);
110 free((*pki)->key);
112 if ((*pki)->passphrase) {
113 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
114 free((*pki)->passphrase);
117 zfree((*pki));
121 /* Free isds_list with all member data.
122 * @list list to free, on return will be NULL */
123 void isds_list_free(struct isds_list **list) {
124 struct isds_list *item, *next_item;
126 if (!list || !*list) return;
128 for(item = *list; item; item = next_item) {
129 if (item->destructor) (item->destructor)(&(item->data));
130 next_item = item->next;
131 free(item);
134 *list = NULL;
138 /* Deallocate structure isds_hash and NULL it.
139 * @hash hash to to free */
140 void isds_hash_free(struct isds_hash **hash) {
141 if(!hash || !*hash) return;
142 free((*hash)->value);
143 zfree((*hash));
147 /* Deallocate structure isds_PersonName recursively and NULL it */
148 static void isds_PersonName_free(struct isds_PersonName **person_name) {
149 if (!person_name || !*person_name) return;
151 free((*person_name)->pnFirstName);
152 free((*person_name)->pnMiddleName);
153 free((*person_name)->pnLastName);
154 free((*person_name)->pnLastNameAtBirth);
156 free(*person_name);
157 *person_name = NULL;
161 /* Deallocate structure isds_BirthInfo recursively and NULL it */
162 static void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
163 if (!birth_info || !*birth_info) return;
165 free((*birth_info)->biDate);
166 free((*birth_info)->biCity);
167 free((*birth_info)->biCounty);
168 free((*birth_info)->biState);
170 free(*birth_info);
171 *birth_info = NULL;
175 /* Deallocate structure isds_Address recursively and NULL it */
176 static void isds_Address_free(struct isds_Address **address) {
177 if (!address || !*address) return;
179 free((*address)->adCity);
180 free((*address)->adStreet);
181 free((*address)->adNumberInStreet);
182 free((*address)->adNumberInMunicipality);
183 free((*address)->adZipCode);
184 free((*address)->adState);
186 free(*address);
187 *address = NULL;
191 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
192 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
193 if (!db_owner_info || !*db_owner_info) return;
195 free((*db_owner_info)->dbID);
196 free((*db_owner_info)->dbType);
197 free((*db_owner_info)->ic);
198 isds_PersonName_free(&((*db_owner_info)->personName));
199 free((*db_owner_info)->firmName);
200 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
201 isds_Address_free(&((*db_owner_info)->address));
202 free((*db_owner_info)->nationality);
203 free((*db_owner_info)->email);
204 free((*db_owner_info)->telNumber);
205 free((*db_owner_info)->identifier);
206 free((*db_owner_info)->registryCode);
207 free((*db_owner_info)->dbState);
208 free((*db_owner_info)->dbEffectiveOVM);
210 free(*db_owner_info);
211 *db_owner_info = NULL;
214 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
215 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
216 if (!db_user_info || !*db_user_info) return;
218 free((*db_user_info)->userID);
219 free((*db_user_info)->userType);
220 free((*db_user_info)->userPrivils);
221 isds_PersonName_free(&((*db_user_info)->personName));
222 isds_Address_free(&((*db_user_info)->address));
223 free((*db_user_info)->biDate);
224 free((*db_user_info)->ic);
225 free((*db_user_info)->firmName);
226 free((*db_user_info)->caStreet);
227 free((*db_user_info)->caCity);
228 free((*db_user_info)->caZipCode);
229 free((*db_user_info)->caState);
231 zfree(*db_user_info);
235 /* Deallocate struct isds_event recursively and NULL it */
236 void isds_event_free(struct isds_event **event) {
237 if (!event || !*event) return;
239 free((*event)->time);
240 free((*event)->type);
241 free((*event)->description);
242 zfree(*event);
246 /* Deallocate struct isds_envelope recursively and NULL it */
247 void isds_envelope_free(struct isds_envelope **envelope) {
248 if (!envelope || !*envelope) return;
250 free((*envelope)->dmID);
251 free((*envelope)->dbIDSender);
252 free((*envelope)->dmSender);
253 free((*envelope)->dmSenderAddress);
254 free((*envelope)->dmSenderType);
255 free((*envelope)->dmRecipient);
256 free((*envelope)->dmRecipientAddress);
257 free((*envelope)->dmAmbiguousRecipient);
258 free((*envelope)->dmType);
260 free((*envelope)->dmOrdinal);
261 free((*envelope)->dmMessageStatus);
262 free((*envelope)->dmDeliveryTime);
263 free((*envelope)->dmAcceptanceTime);
264 isds_hash_free(&(*envelope)->hash);
265 free((*envelope)->timestamp);
266 isds_list_free(&(*envelope)->events);
268 free((*envelope)->dmSenderOrgUnit);
269 free((*envelope)->dmSenderOrgUnitNum);
270 free((*envelope)->dbIDRecipient);
271 free((*envelope)->dmRecipientOrgUnit);
272 free((*envelope)->dmRecipientOrgUnitNum);
273 free((*envelope)->dmToHands);
274 free((*envelope)->dmAnnotation);
275 free((*envelope)->dmRecipientRefNumber);
276 free((*envelope)->dmSenderRefNumber);
277 free((*envelope)->dmRecipientIdent);
278 free((*envelope)->dmSenderIdent);
280 free((*envelope)->dmLegalTitleLaw);
281 free((*envelope)->dmLegalTitleYear);
282 free((*envelope)->dmLegalTitleSect);
283 free((*envelope)->dmLegalTitlePar);
284 free((*envelope)->dmLegalTitlePoint);
286 free((*envelope)->dmPersonalDelivery);
287 free((*envelope)->dmAllowSubstDelivery);
289 free((*envelope)->dmOVM);
290 free((*envelope)->dmPublishOwnID);
292 free(*envelope);
293 *envelope = NULL;
297 /* Deallocate struct isds_message recursively and NULL it */
298 void isds_message_free(struct isds_message **message) {
299 if (!message || !*message) return;
301 free((*message)->raw);
302 isds_envelope_free(&((*message)->envelope));
303 isds_list_free(&((*message)->documents));
304 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
306 free(*message);
307 *message = NULL;
311 /* Deallocate struct isds_document recursively and NULL it */
312 void isds_document_free(struct isds_document **document) {
313 if (!document || !*document) return;
315 if (!(*document)->is_xml) {
316 free((*document)->data);
318 free((*document)->dmMimeType);
319 free((*document)->dmFileGuid);
320 free((*document)->dmUpFileGuid);
321 free((*document)->dmFileDescr);
322 free((*document)->dmFormat);
324 free(*document);
325 *document = NULL;
329 /* Deallocate struct isds_message_copy recursively and NULL it */
330 void isds_message_copy_free(struct isds_message_copy **copy) {
331 if (!copy || !*copy) return;
333 free((*copy)->dbIDRecipient);
334 free((*copy)->dmRecipientOrgUnit);
335 free((*copy)->dmRecipientOrgUnitNum);
336 free((*copy)->dmToHands);
338 free((*copy)->dmStatus);
339 free((*copy)->dmID);
341 zfree(*copy);
345 /* Deallocate struct isds_message_status_change recursively and NULL it */
346 void isds_message_status_change_free(
347 struct isds_message_status_change **message_status_change) {
348 if (!message_status_change || !*message_status_change) return;
350 free((*message_status_change)->dmID);
351 free((*message_status_change)->time);
352 free((*message_status_change)->dmMessageStatus);
354 zfree(*message_status_change);
358 /* Deallocate struct isds_approval recursively and NULL it */
359 void isds_approval_free(struct isds_approval **approval) {
360 if (!approval || !*approval) return;
362 free((*approval)->refference);
364 zfree(*approval);
368 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
369 * The email string is deallocated too. */
370 void isds_credentials_delivery_free(
371 struct isds_credentials_delivery **credentials_delivery) {
372 if (!credentials_delivery || !*credentials_delivery) return;
374 free((*credentials_delivery)->email);
375 free((*credentials_delivery)->token);
376 free((*credentials_delivery)->new_user_name);
378 zfree(*credentials_delivery);
382 /* Deallocate struct isds_commercial_permission recursively and NULL it */
383 void isds_commercial_permission_free(
384 struct isds_commercial_permission **permission) {
385 if (NULL == permission || NULL == *permission) return;
387 free((*permission)->recipient);
388 free((*permission)->payer);
389 free((*permission)->expiration);
390 free((*permission)->count);
391 free((*permission)->reply_identifier);
393 zfree(*permission);
397 /* Deallocate struct isds_credit_event recursively and NULL it */
398 void isds_credit_event_free(struct isds_credit_event **event) {
399 if (NULL == event || NULL == *event) return;
401 free((*event)->time);
402 switch ((*event)->type) {
403 case ISDS_CREDIT_CHARGED:
404 free((*event)->details.charged.transaction);
405 break;
406 case ISDS_CREDIT_DISCHARGED:
407 free((*event)->details.discharged.transaction);
408 break;
409 case ISDS_CREDIT_MESSAGE_SENT:
410 free((*event)->details.message_sent.recipient);
411 free((*event)->details.message_sent.message_id);
412 break;
413 case ISDS_CREDIT_STORAGE_SET:
414 free((*event)->details.storage_set.new_valid_from);
415 free((*event)->details.storage_set.new_valid_to);
416 free((*event)->details.storage_set.old_capacity);
417 free((*event)->details.storage_set.old_valid_from);
418 free((*event)->details.storage_set.old_valid_to);
419 free((*event)->details.storage_set.initiator);
420 break;
421 case ISDS_CREDIT_EXPIRED:
422 break;
425 zfree(*event);
429 /* *DUP_OR_ERROR macros needs error label */
430 #define STRDUP_OR_ERROR(new, template) { \
431 if (!template) { \
432 (new) = NULL; \
433 } else { \
434 (new) = strdup(template); \
435 if (!new) goto error; \
439 #define FLATDUP_OR_ERROR(new, template) { \
440 if (!template) { \
441 (new) = NULL; \
442 } else { \
443 (new) = malloc(sizeof(*(new))); \
444 if (!new) goto error; \
445 memcpy((new), (template), sizeof(*(template))); \
449 /* Copy structure isds_pki_credentials recursively. */
450 struct isds_pki_credentials *isds_pki_credentials_duplicate(
451 const struct isds_pki_credentials *template) {
452 struct isds_pki_credentials *new = NULL;
454 if(!template) return NULL;
456 new = calloc(1, sizeof(*new));
457 if (!new) return NULL;
459 STRDUP_OR_ERROR(new->engine, template->engine);
460 new->certificate_format = template->certificate_format;
461 STRDUP_OR_ERROR(new->certificate, template->certificate);
462 new->key_format = template->key_format;
463 STRDUP_OR_ERROR(new->key, template->key);
464 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
466 return new;
468 error:
469 isds_pki_credentials_free(&new);
470 return NULL;
474 /* Copy structure isds_PersonName recursively */
475 struct isds_PersonName *isds_PersonName_duplicate(
476 const struct isds_PersonName *template) {
477 struct isds_PersonName *new = NULL;
479 if (!template) return NULL;
481 new = calloc(1, sizeof(*new));
482 if (!new) return NULL;
484 STRDUP_OR_ERROR(new->pnFirstName, template->pnFirstName);
485 STRDUP_OR_ERROR(new->pnMiddleName, template->pnMiddleName);
486 STRDUP_OR_ERROR(new->pnLastName, template->pnLastName);
487 STRDUP_OR_ERROR(new->pnLastNameAtBirth, template->pnLastNameAtBirth);
489 return new;
491 error:
492 isds_PersonName_free(&new);
493 return NULL;
497 /* Copy structure isds_BirthInfo recursively */
498 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
499 const struct isds_BirthInfo *template) {
500 struct isds_BirthInfo *new = NULL;
502 if (!template) return NULL;
504 new = calloc(1, sizeof(*new));
505 if (!new) return NULL;
507 FLATDUP_OR_ERROR(new->biDate, template->biDate);
508 STRDUP_OR_ERROR(new->biCity, template->biCity);
509 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
510 STRDUP_OR_ERROR(new->biState, template->biState);
512 return new;
514 error:
515 isds_BirthInfo_free(&new);
516 return NULL;
520 /* Copy structure isds_Address recursively */
521 struct isds_Address *isds_Address_duplicate(
522 const struct isds_Address *template) {
523 struct isds_Address *new = NULL;
525 if (!template) return NULL;
527 new = calloc(1, sizeof(*new));
528 if (!new) return NULL;
530 STRDUP_OR_ERROR(new->adCity, template->adCity);
531 STRDUP_OR_ERROR(new->adStreet, template->adStreet);
532 STRDUP_OR_ERROR(new->adNumberInStreet, template->adNumberInStreet);
533 STRDUP_OR_ERROR(new->adNumberInMunicipality,
534 template->adNumberInMunicipality);
535 STRDUP_OR_ERROR(new->adZipCode, template->adZipCode);
536 STRDUP_OR_ERROR(new->adState, template->adState);
538 return new;
540 error:
541 isds_Address_free(&new);
542 return NULL;
546 /* Copy structure isds_DbOwnerInfo recursively */
547 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
548 const struct isds_DbOwnerInfo *template) {
549 struct isds_DbOwnerInfo *new = NULL;
550 if (!template) return NULL;
552 new = calloc(1, sizeof(*new));
553 if (!new) return NULL;
555 STRDUP_OR_ERROR(new->dbID, template->dbID);
556 FLATDUP_OR_ERROR(new->dbType, template->dbType);
557 STRDUP_OR_ERROR(new->ic, template->ic);
559 if (template->personName) {
560 if (!(new->personName =
561 isds_PersonName_duplicate(template->personName)))
562 goto error;
565 STRDUP_OR_ERROR(new->firmName, template->firmName);
567 if (template->birthInfo) {
568 if (!(new->birthInfo =
569 isds_BirthInfo_duplicate(template->birthInfo)))
570 goto error;
573 if (template->address) {
574 if (!(new->address = isds_Address_duplicate(template->address)))
575 goto error;
578 STRDUP_OR_ERROR(new->nationality, template->nationality);
579 STRDUP_OR_ERROR(new->email, template->email);
580 STRDUP_OR_ERROR(new->telNumber, template->telNumber);
581 STRDUP_OR_ERROR(new->identifier, template->identifier);
582 STRDUP_OR_ERROR(new->registryCode, template->registryCode);
583 FLATDUP_OR_ERROR(new->dbState, template->dbState);
584 FLATDUP_OR_ERROR(new->dbEffectiveOVM, template->dbEffectiveOVM);
585 FLATDUP_OR_ERROR(new->dbOpenAddressing, template->dbOpenAddressing);
587 return new;
589 error:
590 isds_DbOwnerInfo_free(&new);
591 return NULL;
595 /* Copy structure isds_DbUserInfo recursively */
596 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
597 const struct isds_DbUserInfo *template) {
598 struct isds_DbUserInfo *new = NULL;
599 if (!template) return NULL;
601 new = calloc(1, sizeof(*new));
602 if (!new) return NULL;
604 STRDUP_OR_ERROR(new->userID, template->userID);
605 FLATDUP_OR_ERROR(new->userType, template->userType);
606 FLATDUP_OR_ERROR(new->userPrivils, template->userPrivils);
608 if (template->personName) {
609 if (!(new->personName =
610 isds_PersonName_duplicate(template->personName)))
611 goto error;
614 if (template->address) {
615 if (!(new->address = isds_Address_duplicate(template->address)))
616 goto error;
619 FLATDUP_OR_ERROR(new->biDate, template->biDate);
620 STRDUP_OR_ERROR(new->ic, template->ic);
621 STRDUP_OR_ERROR(new->firmName, template->firmName);
622 STRDUP_OR_ERROR(new->caStreet, template->caStreet);
623 STRDUP_OR_ERROR(new->caCity, template->caCity);
624 STRDUP_OR_ERROR(new->caZipCode, template->caZipCode);
625 STRDUP_OR_ERROR(new->caState, template->caState);
627 return new;
629 error:
630 isds_DbUserInfo_free(&new);
631 return NULL;
634 #undef FLATDUP_OR_ERROR
635 #undef STRDUP_OR_ERROR
638 /* Logs libxml2 errors. Should be registered to libxml2 library.
639 * @ctx is unused currently
640 * @msg is printf-like formated message from libxml2 (UTF-8?)
641 * @... are variadic arguments for @msg */
642 static void log_xml(void *ctx, const char *msg, ...) {
643 va_list ap;
644 char *text = NULL;
646 if (!msg) return;
648 va_start(ap, msg);
649 isds_vasprintf(&text, msg, ap);
650 va_end(ap);
652 if (text)
653 isds_log(ILF_XML, ILL_ERR, "%s", text);
654 free(text);
658 /* Initialize ISDS library.
659 * Global function, must be called before other functions.
660 * If it fails you can not use ISDS library and must call isds_cleanup() to
661 * free partially initialized global variables. */
662 isds_error isds_init(void) {
663 /* NULL global variables */
664 log_facilities = ILF_ALL;
665 log_level = ILL_WARNING;
666 log_callback = NULL;
667 log_callback_data = NULL;
669 #if ENABLE_NLS
670 /* Initialize gettext */
671 bindtextdomain(PACKAGE, LOCALEDIR);
672 #endif
674 #if HAVE_LIBCURL
675 /* Initialize CURL */
676 if (curl_global_init(CURL_GLOBAL_ALL)) {
677 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
678 return IE_ERROR;
680 #endif /* HAVE_LIBCURL */
682 /* Initialize gpg-error because of gpgme and ksba */
683 if (gpg_err_init()) {
684 isds_log(ILF_ISDS, ILL_CRIT,
685 _("gpg-error library initialization failed\n"));
686 return IE_ERROR;
689 /* Initialize GPGME */
690 if (_isds_init_gpgme(&version_gpgme)) {
691 isds_log(ILF_ISDS, ILL_CRIT,
692 _("GPGME library initialization failed\n"));
693 return IE_ERROR;
696 /* Initialize gcrypt */
697 if (_isds_init_gcrypt(&version_gcrypt)) {
698 isds_log(ILF_ISDS, ILL_CRIT,
699 _("gcrypt library initialization failed\n"));
700 return IE_ERROR;
703 /* This can _exit() current program. Find not so assertive check. */
704 LIBXML_TEST_VERSION;
705 xmlSetGenericErrorFunc(NULL, log_xml);
707 /* Check expat */
708 if (_isds_init_expat(&version_expat)) {
709 isds_log(ILF_ISDS, ILL_CRIT,
710 _("expat library initialization failed\n"));
711 return IE_ERROR;
714 /* Allocate global variables */
717 return IE_SUCCESS;
721 /* Deinitialize ISDS library.
722 * Global function, must be called as last library function. */
723 isds_error isds_cleanup(void) {
724 /* XML */
725 xmlCleanupParser();
727 #if HAVE_LIBCURL
728 /* Curl */
729 curl_global_cleanup();
730 #endif
732 return IE_SUCCESS;
736 /* Return version string of this library. Version of dependencies can be
737 * embedded. Do no try to parse it. You must free it. */
738 char *isds_version(void) {
739 char *buffer = NULL;
741 isds_asprintf(&buffer,
742 #if HAVE_LIBCURL
743 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
744 #else
745 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
746 #endif
747 PACKAGE_VERSION,
748 #if HAVE_LIBCURL
749 curl_version(),
750 #endif
751 version_gpgme, version_gcrypt,
752 version_expat, xmlParserVersion);
753 return buffer;
757 /* Return text description of ISDS error */
758 const char *isds_strerror(const isds_error error) {
759 switch (error) {
760 case IE_SUCCESS:
761 return(_("Success")); break;
762 case IE_ERROR:
763 return(_("Unspecified error")); break;
764 case IE_NOTSUP:
765 return(_("Not supported")); break;
766 case IE_INVAL:
767 return(_("Invalid value")); break;
768 case IE_INVALID_CONTEXT:
769 return(_("Invalid context")); break;
770 case IE_NOT_LOGGED_IN:
771 return(_("Not logged in")); break;
772 case IE_CONNECTION_CLOSED:
773 return(_("Connection closed")); break;
774 case IE_TIMED_OUT:
775 return(_("Timed out")); break;
776 case IE_NOEXIST:
777 return(_("Not exist")); break;
778 case IE_NOMEM:
779 return(_("Out of memory")); break;
780 case IE_NETWORK:
781 return(_("Network problem")); break;
782 case IE_HTTP:
783 return(_("HTTP problem")); break;
784 case IE_SOAP:
785 return(_("SOAP problem")); break;
786 case IE_XML:
787 return(_("XML problem")); break;
788 case IE_ISDS:
789 return(_("ISDS server problem")); break;
790 case IE_ENUM:
791 return(_("Invalid enum value")); break;
792 case IE_DATE:
793 return(_("Invalid date value")); break;
794 case IE_2BIG:
795 return(_("Too big")); break;
796 case IE_2SMALL:
797 return(_("Too small")); break;
798 case IE_NOTUNIQ:
799 return(_("Value not unique")); break;
800 case IE_NOTEQUAL:
801 return(_("Values not equal")); break;
802 case IE_PARTIAL_SUCCESS:
803 return(_("Some suboperations failed")); break;
804 case IE_ABORTED:
805 return(_("Operation aborted")); break;
806 case IE_SECURITY:
807 return(_("Security problem")); break;
808 default:
809 return(_("Unknown error"));
814 /* Create ISDS context.
815 * Each context can be used for different sessions to (possibly) different
816 * ISDS server with different credentials. */
817 struct isds_ctx *isds_ctx_create(void) {
818 struct isds_ctx *context;
819 context = malloc(sizeof(*context));
820 if (context) memset(context, 0, sizeof(*context));
821 return context;
824 #if HAVE_LIBCURL
825 /* Close possibly opened connection to Czech POINT document deposit without
826 * resetting long_message buffer.
827 * XXX: Do not use czp_close_connection() if you do not want to destroy log
828 * message.
829 * @context is Czech POINT session context. */
830 static isds_error czp_do_close_connection(struct isds_ctx *context) {
831 if (!context) return IE_INVALID_CONTEXT;
832 _isds_close_connection(context);
833 return IE_SUCCESS;
837 /* Discard credentials.
838 * @context is ISDS context
839 * @discard_saved_username is true for removing saved username, false for
840 * keeping it.
841 * Only that. It does not cause log out, connection close or similar. */
842 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context,
843 _Bool discard_saved_username) {
844 if(!context) return IE_INVALID_CONTEXT;
846 if (context->username) {
847 memset(context->username, 0, strlen(context->username));
848 zfree(context->username);
850 if (context->password) {
851 memset(context->password, 0, strlen(context->password));
852 zfree(context->password);
854 isds_pki_credentials_free(&context->pki_credentials);
855 if (discard_saved_username && context->saved_username) {
856 memset(context->saved_username, 0, strlen(context->saved_username));
857 zfree(context->saved_username);
860 return IE_SUCCESS;
862 #endif /* HAVE_LIBCURL */
865 /* Destroy ISDS context and free memory.
866 * @context will be NULLed on success. */
867 isds_error isds_ctx_free(struct isds_ctx **context) {
868 if (!context || !*context) {
869 return IE_INVALID_CONTEXT;
872 #if HAVE_LIBCURL
873 /* Discard credentials and close connection */
874 switch ((*context)->type) {
875 case CTX_TYPE_NONE: break;
876 case CTX_TYPE_ISDS: isds_logout(*context); break;
877 case CTX_TYPE_CZP:
878 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
879 czp_do_close_connection(*context); break;
882 /* For sure */
883 _isds_discard_credentials(*context, 1);
885 /* Free other structures */
886 free((*context)->tls_verify_server);
887 free((*context)->tls_ca_file);
888 free((*context)->tls_ca_dir);
889 free((*context)->tls_crl_file);
890 #endif /* HAVE_LIBCURL */
891 free((*context)->long_message);
893 free(*context);
894 *context = NULL;
895 return IE_SUCCESS;
899 /* Return long message text produced by library function, e.g. detailed error
900 * message. Returned pointer is only valid until new library function is
901 * called for the same context. Could be NULL, especially if NULL context is
902 * supplied. Return string is locale encoded. */
903 char *isds_long_message(const struct isds_ctx *context) {
904 if (!context) return NULL;
905 return context->long_message;
909 /* Stores message into context' long_message buffer.
910 * Application can pick the message up using isds_long_message().
911 * NULL @message truncates the buffer but does not deallocate it.
912 * @message is coded in locale encoding */
913 _hidden isds_error isds_log_message(struct isds_ctx *context,
914 const char *message) {
915 char *buffer;
916 size_t length;
918 if (!context) return IE_INVALID_CONTEXT;
920 /* FIXME: Check for integer overflow */
921 length = 1 + ((message) ? strlen(message) : 0);
922 buffer = realloc(context->long_message, length);
923 if (!buffer) return IE_NOMEM;
925 if (message)
926 strcpy(buffer, message);
927 else
928 *buffer = '\0';
930 context->long_message = buffer;
931 return IE_SUCCESS;
935 /* Appends message into context' long_message buffer.
936 * Application can pick the message up using isds_long_message().
937 * NULL message has void effect. */
938 _hidden isds_error isds_append_message(struct isds_ctx *context,
939 const char *message) {
940 char *buffer;
941 size_t old_length, length;
943 if (!context) return IE_INVALID_CONTEXT;
944 if (!message) return IE_SUCCESS;
945 if (!context->long_message)
946 return isds_log_message(context, message);
948 old_length = strlen(context->long_message);
949 /* FIXME: Check for integer overflow */
950 length = 1 + old_length + strlen(message);
951 buffer = realloc(context->long_message, length);
952 if (!buffer) return IE_NOMEM;
954 strcpy(buffer + old_length, message);
956 context->long_message = buffer;
957 return IE_SUCCESS;
961 /* Stores formatted message into context' long_message buffer.
962 * Application can pick the message up using isds_long_message(). */
963 _hidden isds_error isds_printf_message(struct isds_ctx *context,
964 const char *format, ...) {
965 va_list ap;
966 int length;
968 if (!context) return IE_INVALID_CONTEXT;
969 va_start(ap, format);
970 length = isds_vasprintf(&(context->long_message), format, ap);
971 va_end(ap);
973 return (length < 0) ? IE_ERROR: IE_SUCCESS;
977 /* Set logging up.
978 * @facilities is bit mask of isds_log_facility values,
979 * @level is verbosity level. */
980 void isds_set_logging(const unsigned int facilities,
981 const isds_log_level level) {
982 log_facilities = facilities;
983 log_level = level;
987 /* Register callback function libisds calls when new global log message is
988 * produced by library. Library logs to stderr by default.
989 * @callback is function provided by application libisds will call. See type
990 * definition for @callback argument explanation. Pass NULL to revert logging to
991 * default behaviour.
992 * @data is application specific data @callback gets as last argument */
993 void isds_set_log_callback(isds_log_callback callback, void *data) {
994 log_callback = callback;
995 log_callback_data = data;
999 /* Log @message in class @facility with log @level into global log. @message
1000 * is printf(3) formatting string, variadic arguments may be necessary.
1001 * For debugging purposes. */
1002 _hidden isds_error isds_log(const isds_log_facility facility,
1003 const isds_log_level level, const char *message, ...) {
1004 va_list ap;
1005 char *buffer = NULL;
1006 int length;
1008 if (level > log_level) return IE_SUCCESS;
1009 if (!(log_facilities & facility)) return IE_SUCCESS;
1010 if (!message) return IE_INVAL;
1012 if (log_callback) {
1013 /* Pass message to application supplied callback function */
1014 va_start(ap, message);
1015 length = isds_vasprintf(&buffer, message, ap);
1016 va_end(ap);
1018 if (length == -1) {
1019 return IE_ERROR;
1021 if (length > 0) {
1022 log_callback(facility, level, buffer, length, log_callback_data);
1024 free(buffer);
1025 } else {
1026 /* Default: Log it to stderr */
1027 va_start(ap, message);
1028 vfprintf(stderr, message, ap);
1029 va_end(ap);
1030 /* Line buffered printf is default.
1031 * fflush(stderr);*/
1034 return IE_SUCCESS;
1038 /* Set timeout in milliseconds for each network job like connecting to server
1039 * or sending message. Use 0 to disable timeout limits. */
1040 isds_error isds_set_timeout(struct isds_ctx *context,
1041 const unsigned int timeout) {
1042 if (!context) return IE_INVALID_CONTEXT;
1043 zfree(context->long_message);
1045 #if HAVE_LIBCURL
1046 context->timeout = timeout;
1048 if (context->curl) {
1049 CURLcode curl_err;
1051 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
1052 if (!curl_err)
1053 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1054 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
1055 context->timeout);
1056 #else
1057 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
1058 context->timeout / 1000);
1059 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1060 if (curl_err) return IE_ERROR;
1063 return IE_SUCCESS;
1064 #else /* not HAVE_LIBCURL */
1065 return IE_NOTSUP;
1066 #endif
1070 /* Register callback function libisds calls periodically during HTTP data
1071 * transfer.
1072 * @context is session context
1073 * @callback is function provided by application libisds will call. See type
1074 * definition for @callback argument explanation.
1075 * @data is application specific data @callback gets as last argument */
1076 isds_error isds_set_progress_callback(struct isds_ctx *context,
1077 isds_progress_callback callback, void *data) {
1078 if (!context) return IE_INVALID_CONTEXT;
1079 zfree(context->long_message);
1081 #if HAVE_LIBCURL
1082 context->progress_callback = callback;
1083 context->progress_callback_data = data;
1085 return IE_SUCCESS;
1086 #else /* not HAVE_LIBCURL */
1087 return IE_NOTSUP;
1088 #endif
1092 /* Change context settings.
1093 * @context is context which setting will be applied to
1094 * @option is name of option. It determines the type of last argument. See
1095 * isds_option definition for more info.
1096 * @... is value of new setting. Type is determined by @option
1097 * */
1098 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1099 ...) {
1100 isds_error err = IE_SUCCESS;
1101 va_list ap;
1102 #if HAVE_LIBCURL
1103 char *pointer, *string;
1104 #endif
1106 if (!context) return IE_INVALID_CONTEXT;
1107 zfree(context->long_message);
1109 va_start(ap, option);
1111 #define REPLACE_VA_BOOLEAN(destination) { \
1112 if (!(destination)) { \
1113 (destination) = malloc(sizeof(*(destination))); \
1114 if (!(destination)) { \
1115 err = IE_NOMEM; goto leave; \
1118 *(destination) = (_Bool) !!va_arg(ap, int); \
1121 #define REPLACE_VA_STRING(destination) { \
1122 string = va_arg(ap, char *); \
1123 if (string) { \
1124 pointer = realloc((destination), 1 + strlen(string)); \
1125 if (!pointer) { err = IE_NOMEM; goto leave; } \
1126 strcpy(pointer, string); \
1127 (destination) = pointer; \
1128 } else { \
1129 free(destination); \
1130 (destination) = NULL; \
1134 switch (option) {
1135 case IOPT_TLS_VERIFY_SERVER:
1136 #if HAVE_LIBCURL
1137 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1138 #else
1139 err = IE_NOTSUP; goto leave;
1140 #endif
1141 break;
1142 case IOPT_TLS_CA_FILE:
1143 #if HAVE_LIBCURL
1144 REPLACE_VA_STRING(context->tls_ca_file);
1145 #else
1146 err = IE_NOTSUP; goto leave;
1147 #endif
1148 break;
1149 case IOPT_TLS_CA_DIRECTORY:
1150 #if HAVE_LIBCURL
1151 REPLACE_VA_STRING(context->tls_ca_dir);
1152 #else
1153 err = IE_NOTSUP; goto leave;
1154 #endif
1155 break;
1156 case IOPT_TLS_CRL_FILE:
1157 #if HAVE_LIBCURL
1158 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1159 REPLACE_VA_STRING(context->tls_crl_file);
1160 #else
1161 isds_log_message(context,
1162 _("Curl library does not support CRL definition"));
1163 err = IE_NOTSUP;
1164 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1165 #else
1166 err = IE_NOTSUP; goto leave;
1167 #endif /* not HAVE_LIBCURL */
1168 break;
1169 case IOPT_NORMALIZE_MIME_TYPE:
1170 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1171 break;
1173 default:
1174 err = IE_ENUM; goto leave;
1177 #undef REPLACE_VA_STRING
1178 #undef REPLACE_VA_BOOLEAN
1180 leave:
1181 va_end(ap);
1182 return err;
1186 #if HAVE_LIBCURL
1187 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1188 * Destination for NULL argument will not be touched.
1189 * Destination pointers must be freed before calling this function.
1190 * If @username is @context->saved_username, the saved_username will not be
1191 * replaced. The saved_username is clobbered only if context has set otp
1192 * member.
1193 * Return IE_SUCCESS on success. */
1194 static isds_error _isds_store_credentials(struct isds_ctx *context,
1195 const char *username, const char *password,
1196 const struct isds_pki_credentials *pki_credentials) {
1197 if (NULL == context) return IE_INVALID_CONTEXT;
1199 /* FIXME: mlock password
1200 * (I have a library) */
1202 if (username) {
1203 context->username = strdup(username);
1204 if (context->otp && context->saved_username != username)
1205 context->saved_username = strdup(username);
1207 if (password) {
1208 if (NULL == context->otp_credentials)
1209 context->password = strdup(password);
1210 else
1211 context->password = _isds_astrcat(password,
1212 context->otp_credentials->otp_code);
1214 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1216 if ((NULL != username && NULL == context->username) ||
1217 (NULL != password && NULL == context->password) ||
1218 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1219 (context->otp && NULL != context->username &&
1220 NULL == context->saved_username)) {
1221 return IE_NOMEM;
1224 return IE_SUCCESS;
1226 #endif
1229 /* Connect and log into ISDS server.
1230 * All required arguments will be copied, you do not have to keep them after
1231 * that.
1232 * ISDS supports six different authentication methods. Exact method is
1233 * selected on @username, @password, @pki_credentials, and @otp arguments:
1234 * - If @pki_credentials == NULL, @username and @password must be supplied
1235 * and then
1236 * - If @otp == NULL, simple authentication by username and password will
1237 * be proceeded.
1238 * - If @otp != NULL, authentication by username and password and OTP
1239 * will be used.
1240 * - If @pki_credentials != NULL, then
1241 * - If @username == NULL, only certificate will be used
1242 * - If @username != NULL, then
1243 * - If @password == NULL, then certificate will be used and
1244 * @username shifts meaning to box ID. This is used for hosted
1245 * services.
1246 * - Otherwise all three arguments will be used.
1247 * Please note, that different cases require different certificate type
1248 * (system qualified one or commercial non qualified one). This library
1249 * does not check such political issues. Please see ISDS Specification
1250 * for more details.
1251 * @url is base address of ISDS web service. Pass extern isds_locator
1252 * variable to use production ISDS instance without client certificate
1253 * authentication (or extern isds_cert_locator with client certificate
1254 * authentication or extern isds_otp_locators with OTP authentication).
1255 * Passing NULL has the same effect, autoselection between isds_locator,
1256 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1257 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1258 * isds_otp_testing_locator) variable to select testing instance.
1259 * @username is user name of ISDS user or box ID
1260 * @password is user's secret password
1261 * @pki_credentials defines public key cryptographic material to use in client
1262 * authentication.
1263 * @otp selects one-time password authentication method to use, defines OTP
1264 * code (if known) and returns fine grade resolution of OTP procedure.
1265 * @return:
1266 * IE_SUCCESS if authentication succeeds
1267 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1268 * requested, fine grade reason will be set into @otp->resolution. Error
1269 * message from server can be obtained by isds_long_message() call.
1270 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1271 * server has sent OTP code through side channel. Application is expected to
1272 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1273 * this call to complete second phase of TOTP authentication;
1274 * or other appropriate error. */
1275 isds_error isds_login(struct isds_ctx *context, const char *url,
1276 const char *username, const char *password,
1277 const struct isds_pki_credentials *pki_credentials,
1278 struct isds_otp *otp) {
1279 #if HAVE_LIBCURL
1280 isds_error err = IE_NOT_LOGGED_IN;
1281 isds_error soap_err;
1282 xmlNsPtr isds_ns = NULL;
1283 xmlNodePtr request = NULL;
1284 xmlNodePtr response = NULL;
1285 #endif /* HAVE_LIBCURL */
1287 if (!context) return IE_INVALID_CONTEXT;
1288 zfree(context->long_message);
1290 #if HAVE_LIBCURL
1291 /* Close connection if already logged in */
1292 if (context->curl) {
1293 _isds_close_connection(context);
1296 /* Store configuration */
1297 context->type = CTX_TYPE_ISDS;
1298 zfree(context->url);
1300 /* Mangle base URI according to requested authentication method */
1301 if (NULL == pki_credentials) {
1302 isds_log(ILF_SEC, ILL_INFO,
1303 _("Selected authentication method: no certificate, "
1304 "username and password\n"));
1305 if (!username || !password) {
1306 isds_log_message(context,
1307 _("Both username and password must be supplied"));
1308 return IE_INVAL;
1310 context->otp_credentials = otp;
1311 context->otp = (NULL != context->otp_credentials);
1313 if (!context->otp) {
1314 /* Default locator is official system (without certificate or
1315 * OTP) */
1316 context->url = strdup((NULL != url) ? url : isds_locator);
1317 } else {
1318 const char *authenticator_uri = NULL;
1319 if (!url) url = isds_otp_locator;
1320 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1321 switch (context->otp_credentials->method) {
1322 case OTP_HMAC:
1323 isds_log(ILF_SEC, ILL_INFO,
1324 _("Selected authentication method: "
1325 "HMAC-based one-time password\n"));
1326 authenticator_uri =
1327 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1328 break;
1329 case OTP_TIME:
1330 isds_log(ILF_SEC, ILL_INFO,
1331 _("Selected authentication method: "
1332 "Time-based one-time password\n"));
1333 if (context->otp_credentials->otp_code == NULL) {
1334 isds_log(ILF_SEC, ILL_INFO,
1335 _("OTP code has not been provided by "
1336 "application, requesting server for "
1337 "new one.\n"));
1338 authenticator_uri =
1339 "%1$sas/processLogin?type=totp&sendSms=true&"
1340 "uri=%1$sapps/";
1341 } else {
1342 isds_log(ILF_SEC, ILL_INFO,
1343 _("OTP code has been provided by "
1344 "application, not requesting server "
1345 "for new one.\n"));
1346 authenticator_uri =
1347 "%1$sas/processLogin?type=totp&"
1348 "uri=%1$sapps/";
1350 break;
1351 default:
1352 isds_log_message(context,
1353 _("Unknown one-time password authentication "
1354 "method requested by application"));
1355 return IE_ENUM;
1357 if (-1 == isds_asprintf(&context->url, authenticator_uri, url))
1358 return IE_NOMEM;
1360 } else {
1361 /* Default locator is official system (with client certificate) */
1362 context->otp = 0;
1363 context->otp_credentials = NULL;
1364 if (!url) url = isds_cert_locator;
1366 if (!username) {
1367 isds_log(ILF_SEC, ILL_INFO,
1368 _("Selected authentication method: system certificate, "
1369 "no username and no password\n"));
1370 password = NULL;
1371 context->url = _isds_astrcat(url, "cert/");
1372 } else {
1373 if (!password) {
1374 isds_log(ILF_SEC, ILL_INFO,
1375 _("Selected authentication method: system certificate, "
1376 "box ID and no password\n"));
1377 context->url = _isds_astrcat(url, "hspis/");
1378 } else {
1379 isds_log(ILF_SEC, ILL_INFO,
1380 _("Selected authentication method: commercial "
1381 "certificate, username and password\n"));
1382 context->url = _isds_astrcat(url, "certds/");
1386 if (!(context->url))
1387 return IE_NOMEM;
1389 /* Prepare CURL handle */
1390 context->curl = curl_easy_init();
1391 if (!(context->curl))
1392 return IE_ERROR;
1394 /* Build log-in request */
1395 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1396 if (!request) {
1397 isds_log_message(context, _("Could not build ISDS log-in request"));
1398 return IE_ERROR;
1400 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1401 if(!isds_ns) {
1402 isds_log_message(context, _("Could not create ISDS name space"));
1403 xmlFreeNode(request);
1404 return IE_ERROR;
1406 xmlSetNs(request, isds_ns);
1408 /* Store credentials */
1409 _isds_discard_credentials(context, 1);
1410 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1411 _isds_discard_credentials(context, 1);
1412 xmlFreeNode(request);
1413 return IE_NOMEM;
1416 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1417 username, url);
1419 /* Send log-in request */
1420 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1422 if (context->otp) {
1423 /* Revert context URL from OTP authentication service URL to OTP web
1424 * service base URL for subsequent calls. Potenial isds_login() retry
1425 * will re-set context URL again. */
1426 zfree(context->url);
1427 context->url = _isds_astrcat(url, "apps/");
1428 if (context->url == NULL) {
1429 soap_err = IE_NOMEM;
1431 /* Detach pointer to OTP credentials from context */
1432 context->otp_credentials = NULL;
1435 /* Remove credentials */
1436 _isds_discard_credentials(context, 0);
1438 /* Destroy log-in request */
1439 xmlFreeNode(request);
1441 if (soap_err) {
1442 xmlFreeNodeList(response);
1443 _isds_close_connection(context);
1444 return soap_err;
1447 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1448 * authentication succeeded if soap_err == IE_SUCCESS */
1449 err = IE_SUCCESS;
1451 xmlFreeNodeList(response);
1453 if (!err)
1454 isds_log(ILF_ISDS, ILL_DEBUG,
1455 _("User %s has been logged into server %s successfully\n"),
1456 username, url);
1457 return err;
1458 #else /* not HAVE_LIBCURL */
1459 return IE_NOTSUP;
1460 #endif
1464 /* Log out from ISDS server discards credentials and connection configuration. */
1465 isds_error isds_logout(struct isds_ctx *context) {
1466 if (!context) return IE_INVALID_CONTEXT;
1467 zfree(context->long_message);
1469 #if HAVE_LIBCURL
1470 if (context->curl) {
1471 if (context->otp) {
1472 isds_error err = _isds_invalidate_otp_cookie(context);
1473 if (err) return err;
1476 /* Close connection */
1477 _isds_close_connection(context);
1479 /* Discard credentials for sure. They should not survive isds_login(),
1480 * even successful .*/
1481 _isds_discard_credentials(context, 1);
1482 zfree(context->url);
1484 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1485 } else {
1486 _isds_discard_credentials(context, 1);
1488 return IE_SUCCESS;
1489 #else /* not HAVE_LIBCURL */
1490 return IE_NOTSUP;
1491 #endif
1495 /* Verify connection to ISDS is alive and server is responding.
1496 * Send dummy request to ISDS and expect dummy response. */
1497 isds_error isds_ping(struct isds_ctx *context) {
1498 #if HAVE_LIBCURL
1499 isds_error soap_err;
1500 xmlNsPtr isds_ns = NULL;
1501 xmlNodePtr request = NULL;
1502 xmlNodePtr response = NULL;
1503 #endif /* HAVE_LIBCURL */
1505 if (!context) return IE_INVALID_CONTEXT;
1506 zfree(context->long_message);
1508 #if HAVE_LIBCURL
1509 /* Check if connection is established */
1510 if (!context->curl) return IE_CONNECTION_CLOSED;
1513 /* Build dummy request */
1514 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1515 if (!request) {
1516 isds_log_message(context, _("Could build ISDS dummy request"));
1517 return IE_ERROR;
1519 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1520 if(!isds_ns) {
1521 isds_log_message(context, _("Could not create ISDS name space"));
1522 xmlFreeNode(request);
1523 return IE_ERROR;
1525 xmlSetNs(request, isds_ns);
1527 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1529 /* Sent dummy request */
1530 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1532 /* Destroy log-in request */
1533 xmlFreeNode(request);
1535 if (soap_err) {
1536 isds_log(ILF_ISDS, ILL_DEBUG,
1537 _("ISDS server could not be contacted\n"));
1538 xmlFreeNodeList(response);
1539 return soap_err;
1542 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1543 * authentication succeeded if soap_err == IE_SUCCESS */
1544 /* TODO: ISDS documentation does not specify response body.
1545 * However real server sends back DummyOperationResponse */
1548 xmlFreeNodeList(response);
1550 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1552 return IE_SUCCESS;
1553 #else /* not HAVE_LIBCURL */
1554 return IE_NOTSUP;
1555 #endif
1559 /* Send bogus request to ISDS.
1560 * Just for test purposes */
1561 isds_error isds_bogus_request(struct isds_ctx *context) {
1562 #if HAVE_LIBCURL
1563 isds_error err;
1564 xmlNsPtr isds_ns = NULL;
1565 xmlNodePtr request = NULL;
1566 xmlDocPtr response = NULL;
1567 xmlChar *code = NULL, *message = NULL;
1568 #endif
1570 if (!context) return IE_INVALID_CONTEXT;
1571 zfree(context->long_message);
1573 #if HAVE_LIBCURL
1574 /* Check if connection is established */
1575 if (!context->curl) {
1576 /* Testing printf message */
1577 isds_printf_message(context, "%s", _("I said connection closed"));
1578 return IE_CONNECTION_CLOSED;
1582 /* Build dummy request */
1583 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1584 if (!request) {
1585 isds_log_message(context, _("Could build ISDS bogus request"));
1586 return IE_ERROR;
1588 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1589 if(!isds_ns) {
1590 isds_log_message(context, _("Could not create ISDS name space"));
1591 xmlFreeNode(request);
1592 return IE_ERROR;
1594 xmlSetNs(request, isds_ns);
1596 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1598 /* Sent bogus request */
1599 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1601 /* Destroy request */
1602 xmlFreeNode(request);
1604 if (err) {
1605 isds_log(ILF_ISDS, ILL_DEBUG,
1606 _("Processing ISDS response on bogus request failed\n"));
1607 xmlFreeDoc(response);
1608 return err;
1611 /* Check for response status */
1612 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1613 &code, &message, NULL);
1614 if (err) {
1615 isds_log(ILF_ISDS, ILL_DEBUG,
1616 _("ISDS response on bogus request is missing status\n"));
1617 free(code);
1618 free(message);
1619 xmlFreeDoc(response);
1620 return err;
1622 if (xmlStrcmp(code, BAD_CAST "0000")) {
1623 char *code_locale = _isds_utf82locale((char*)code);
1624 char *message_locale = _isds_utf82locale((char*)message);
1625 isds_log(ILF_ISDS, ILL_DEBUG,
1626 _("Server refused bogus request (code=%s, message=%s)\n"),
1627 code_locale, message_locale);
1628 /* XXX: Literal error messages from ISDS are Czech messages
1629 * (English sometimes) in UTF-8. It's hard to catch them for
1630 * translation. Successfully gettextized would return in locale
1631 * encoding, unsuccessfully translated would pass in UTF-8. */
1632 isds_log_message(context, message_locale);
1633 free(code_locale);
1634 free(message_locale);
1635 free(code);
1636 free(message);
1637 xmlFreeDoc(response);
1638 return IE_ISDS;
1642 free(code);
1643 free(message);
1644 xmlFreeDoc(response);
1646 isds_log(ILF_ISDS, ILL_DEBUG,
1647 _("Bogus message accepted by server. This should not happen.\n"));
1649 return IE_SUCCESS;
1650 #else /* not HAVE_LIBCURL */
1651 return IE_NOTSUP;
1652 #endif
1656 #if HAVE_LIBCURL
1657 /* Serialize XML subtree to buffer preserving XML indentation.
1658 * @context is session context
1659 * @subtree is XML element to be serialized (with children)
1660 * @buffer is automatically reallocated buffer where serialize to
1661 * @length is size of serialized stream in bytes
1662 * @return standard error code, free @buffer in case of error */
1663 static isds_error serialize_subtree(struct isds_ctx *context,
1664 xmlNodePtr subtree, void **buffer, size_t *length) {
1665 isds_error err = IE_SUCCESS;
1666 xmlBufferPtr xml_buffer = NULL;
1667 xmlSaveCtxtPtr save_ctx = NULL;
1668 xmlDocPtr subtree_doc = NULL;
1669 xmlNodePtr subtree_copy;
1670 xmlNsPtr isds_ns;
1671 void *new_buffer;
1673 if (!context) return IE_INVALID_CONTEXT;
1674 if (!buffer) return IE_INVAL;
1675 zfree(*buffer);
1676 if (!subtree || !length) return IE_INVAL;
1678 /* Make temporary XML document with @subtree root element */
1679 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1680 * It can result in not well-formed on invalid XML tree (e.g. name space
1681 * prefix definition can miss. */
1682 /*FIXME */
1684 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1685 if (!subtree_doc) {
1686 isds_log_message(context, _("Could not build temporary document"));
1687 err = IE_ERROR;
1688 goto leave;
1691 /* XXX: Copy subtree and attach the copy to document.
1692 * One node can not bee attached into more document at the same time.
1693 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1694 * automatically.
1695 * XXX: Check xmlSaveTree() too. */
1696 subtree_copy = xmlCopyNodeList(subtree);
1697 if (!subtree_copy) {
1698 isds_log_message(context, _("Could not copy subtree"));
1699 err = IE_ERROR;
1700 goto leave;
1702 xmlDocSetRootElement(subtree_doc, subtree_copy);
1704 /* Only this way we get namespace definition as @xmlns:isds,
1705 * otherwise we get namespace prefix without definition */
1706 /* FIXME: Don't overwrite original default namespace */
1707 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1708 if(!isds_ns) {
1709 isds_log_message(context, _("Could not create ISDS name space"));
1710 err = IE_ERROR;
1711 goto leave;
1713 xmlSetNs(subtree_copy, isds_ns);
1716 /* Serialize the document into buffer */
1717 xml_buffer = xmlBufferCreate();
1718 if (!xml_buffer) {
1719 isds_log_message(context, _("Could not create xmlBuffer"));
1720 err = IE_ERROR;
1721 goto leave;
1723 /* Last argument 0 means to not format the XML tree */
1724 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1725 if (!save_ctx) {
1726 isds_log_message(context, _("Could not create XML serializer"));
1727 err = IE_ERROR;
1728 goto leave;
1730 /* XXX: According LibXML documentation, this function does not return
1731 * meaningful value yet */
1732 xmlSaveDoc(save_ctx, subtree_doc);
1733 if (-1 == xmlSaveFlush(save_ctx)) {
1734 isds_log_message(context,
1735 _("Could not serialize XML subtree"));
1736 err = IE_ERROR;
1737 goto leave;
1739 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1740 * even after xmlSaveFlush(). Thus close it here */
1741 xmlSaveClose(save_ctx); save_ctx = NULL;
1744 /* Store and detach buffer from xml_buffer */
1745 *buffer = xml_buffer->content;
1746 *length = xml_buffer->use;
1747 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1749 /* Shrink buffer */
1750 new_buffer = realloc(*buffer, *length);
1751 if (new_buffer) *buffer = new_buffer;
1753 leave:
1754 if (err) {
1755 zfree(*buffer);
1756 *length = 0;
1759 xmlSaveClose(save_ctx);
1760 xmlBufferFree(xml_buffer);
1761 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1762 return err;
1764 #endif /* HAVE_LIBCURL */
1767 #if 0
1768 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1769 * @context is session context
1770 * @document is original document where @nodeset points to
1771 * @nodeset is XPath node set to dump (recursively)
1772 * @buffer is automatically reallocated buffer where serialize to
1773 * @length is size of serialized stream in bytes
1774 * @return standard error code, free @buffer in case of error */
1775 static isds_error dump_nodeset(struct isds_ctx *context,
1776 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1777 void **buffer, size_t *length) {
1778 isds_error err = IE_SUCCESS;
1779 xmlBufferPtr xml_buffer = NULL;
1780 void *new_buffer;
1782 if (!context) return IE_INVALID_CONTEXT;
1783 if (!buffer) return IE_INVAL;
1784 zfree(*buffer);
1785 if (!document || !nodeset || !length) return IE_INVAL;
1786 *length = 0;
1788 /* Empty node set results into NULL buffer */
1789 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1790 goto leave;
1793 /* Resulting the document into buffer */
1794 xml_buffer = xmlBufferCreate();
1795 if (!xml_buffer) {
1796 isds_log_message(context, _("Could not create xmlBuffer"));
1797 err = IE_ERROR;
1798 goto leave;
1801 /* Iterate over all nodes */
1802 for (int i = 0; i < nodeset->nodeNr; i++) {
1803 /* Serialize node.
1804 * XXX: xmlNodeDump() appends to xml_buffer. */
1805 if (-1 ==
1806 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1807 isds_log_message(context, _("Could not dump XML node"));
1808 err = IE_ERROR;
1809 goto leave;
1813 /* Store and detach buffer from xml_buffer */
1814 *buffer = xml_buffer->content;
1815 *length = xml_buffer->use;
1816 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1818 /* Shrink buffer */
1819 new_buffer = realloc(*buffer, *length);
1820 if (new_buffer) *buffer = new_buffer;
1823 leave:
1824 if (err) {
1825 zfree(*buffer);
1826 *length = 0;
1829 xmlBufferFree(xml_buffer);
1830 return err;
1832 #endif
1834 #if 0
1835 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1836 * @context is session context
1837 * @document is original document where @nodeset points to
1838 * @nodeset is XPath node set to dump (recursively)
1839 * @buffer is automatically reallocated buffer where serialize to
1840 * @length is size of serialized stream in bytes
1841 * @return standard error code, free @buffer in case of error */
1842 static isds_error dump_nodeset(struct isds_ctx *context,
1843 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1844 void **buffer, size_t *length) {
1845 isds_error err = IE_SUCCESS;
1846 xmlBufferPtr xml_buffer = NULL;
1847 xmlSaveCtxtPtr save_ctx = NULL;
1848 void *new_buffer;
1850 if (!context) return IE_INVALID_CONTEXT;
1851 if (!buffer) return IE_INVAL;
1852 zfree(*buffer);
1853 if (!document || !nodeset || !length) return IE_INVAL;
1854 *length = 0;
1856 /* Empty node set results into NULL buffer */
1857 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1858 goto leave;
1861 /* Resulting the document into buffer */
1862 xml_buffer = xmlBufferCreate();
1863 if (!xml_buffer) {
1864 isds_log_message(context, _("Could not create xmlBuffer"));
1865 err = IE_ERROR;
1866 goto leave;
1868 if (xmlSubstituteEntitiesDefault(1)) {
1869 isds_log_message(context, _("Could not disable attribute escaping"));
1870 err = IE_ERROR;
1871 goto leave;
1873 /* Last argument means:
1874 * 0 to not format the XML tree
1875 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1876 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1877 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1878 if (!save_ctx) {
1879 isds_log_message(context, _("Could not create XML serializer"));
1880 err = IE_ERROR;
1881 goto leave;
1883 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1884 isds_log_message(context, _("Could not disable attribute escaping"));
1885 err = IE_ERROR;
1886 goto leave;
1890 /* Iterate over all nodes */
1891 for (int i = 0; i < nodeset->nodeNr; i++) {
1892 /* Serialize node.
1893 * XXX: xmlNodeDump() appends to xml_buffer. */
1894 /*if (-1 ==
1895 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1897 /* XXX: According LibXML documentation, this function does not return
1898 * meaningful value yet */
1899 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1900 if (-1 == xmlSaveFlush(save_ctx)) {
1901 isds_log_message(context,
1902 _("Could not serialize XML subtree"));
1903 err = IE_ERROR;
1904 goto leave;
1908 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1909 * even after xmlSaveFlush(). Thus close it here */
1910 xmlSaveClose(save_ctx); save_ctx = NULL;
1912 /* Store and detach buffer from xml_buffer */
1913 *buffer = xml_buffer->content;
1914 *length = xml_buffer->use;
1915 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1917 /* Shrink buffer */
1918 new_buffer = realloc(*buffer, *length);
1919 if (new_buffer) *buffer = new_buffer;
1921 leave:
1922 if (err) {
1923 zfree(*buffer);
1924 *length = 0;
1927 xmlSaveClose(save_ctx);
1928 xmlBufferFree(xml_buffer);
1929 return err;
1931 #endif
1934 #if HAVE_LIBCURL
1935 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1936 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1937 if (!string || !type) return IE_INVAL;
1939 if (!xmlStrcmp(string, BAD_CAST "FO"))
1940 *type = DBTYPE_FO;
1941 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1942 *type = DBTYPE_PFO;
1943 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1944 *type = DBTYPE_PFO_ADVOK;
1945 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1946 *type = DBTYPE_PFO_DANPOR;
1947 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1948 *type = DBTYPE_PFO_INSSPR;
1949 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1950 *type = DBTYPE_PO;
1951 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1952 *type = DBTYPE_PO_ZAK;
1953 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1954 *type = DBTYPE_PO_REQ;
1955 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1956 *type = DBTYPE_OVM;
1957 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1958 *type = DBTYPE_OVM_NOTAR;
1959 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1960 *type = DBTYPE_OVM_EXEKUT;
1961 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1962 *type = DBTYPE_OVM_REQ;
1963 else
1964 return IE_ENUM;
1965 return IE_SUCCESS;
1969 /* Convert ISDS dbType enum @type to UTF-8 string.
1970 * @Return pointer to static string, or NULL if unknown enum value */
1971 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1972 switch(type) {
1973 /* DBTYPE_SYSTEM is invalid value from point of view of public
1974 * SOAP interface. */
1975 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1976 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1977 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1978 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1979 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1980 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1981 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1982 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1983 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1984 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1985 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1986 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1987 default: return NULL; break;
1992 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
1993 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1994 if (!string || !type) return IE_INVAL;
1996 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1997 *type = USERTYPE_PRIMARY;
1998 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1999 *type = USERTYPE_ENTRUSTED;
2000 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2001 *type = USERTYPE_ADMINISTRATOR;
2002 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2003 *type = USERTYPE_OFFICIAL;
2004 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2005 *type = USERTYPE_OFFICIAL_CERT;
2006 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2007 *type = USERTYPE_LIQUIDATOR;
2008 else
2009 return IE_ENUM;
2010 return IE_SUCCESS;
2014 /* Convert ISDS userType enum @type to UTF-8 string.
2015 * @Return pointer to static string, or NULL if unknown enum value */
2016 static const xmlChar *isds_UserType2string(const isds_UserType type) {
2017 switch(type) {
2018 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
2019 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
2020 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
2021 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
2022 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
2023 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
2024 default: return NULL; break;
2029 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2030 static isds_error string2isds_sender_type(const xmlChar *string,
2031 isds_sender_type *type) {
2032 if (!string || !type) return IE_INVAL;
2034 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2035 *type = SENDERTYPE_PRIMARY;
2036 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2037 *type = SENDERTYPE_ENTRUSTED;
2038 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2039 *type = SENDERTYPE_ADMINISTRATOR;
2040 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2041 *type = SENDERTYPE_OFFICIAL;
2042 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2043 *type = SENDERTYPE_VIRTUAL;
2044 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2045 *type = SENDERTYPE_OFFICIAL_CERT;
2046 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2047 *type = SENDERTYPE_LIQUIDATOR;
2048 else
2049 return IE_ENUM;
2050 return IE_SUCCESS;
2054 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2055 static isds_error string2isds_payment_type(const xmlChar *string,
2056 isds_payment_type *type) {
2057 if (!string || !type) return IE_INVAL;
2059 if (!xmlStrcmp(string, BAD_CAST "K"))
2060 *type = PAYMENT_SENDER;
2061 else if (!xmlStrcmp(string, BAD_CAST "O"))
2062 *type = PAYMENT_RESPONSE;
2063 else if (!xmlStrcmp(string, BAD_CAST "G"))
2064 *type = PAYMENT_SPONSOR;
2065 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2066 *type = PAYMENT_SPONSOR_LIMITED;
2067 else if (!xmlStrcmp(string, BAD_CAST "D"))
2068 *type = PAYMENT_SPONSOR_EXTERNAL;
2069 else if (!xmlStrcmp(string, BAD_CAST "E"))
2070 *type = PAYMENT_STAMP;
2071 else
2072 return IE_ENUM;
2073 return IE_SUCCESS;
2077 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2078 * ciEventType is integer but we convert it from string representation
2079 * directly. */
2080 static isds_error string2isds_credit_event_type(const xmlChar *string,
2081 isds_credit_event_type *type) {
2082 if (!string || !type) return IE_INVAL;
2084 if (!xmlStrcmp(string, BAD_CAST "1"))
2085 *type = ISDS_CREDIT_CHARGED;
2086 else if (!xmlStrcmp(string, BAD_CAST "2"))
2087 *type = ISDS_CREDIT_DISCHARGED;
2088 else if (!xmlStrcmp(string, BAD_CAST "3"))
2089 *type = ISDS_CREDIT_MESSAGE_SENT;
2090 else if (!xmlStrcmp(string, BAD_CAST "4"))
2091 *type = ISDS_CREDIT_STORAGE_SET;
2092 else if (!xmlStrcmp(string, BAD_CAST "5"))
2093 *type = ISDS_CREDIT_EXPIRED;
2094 else
2095 return IE_ENUM;
2096 return IE_SUCCESS;
2100 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2101 * @Return pointer to static string, or NULL if unknown enum value */
2102 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2103 switch(type) {
2104 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2105 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2106 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2107 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2108 default: return NULL; break;
2111 #endif /* HAVE_LIBCURL */
2114 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2115 * @Return IE_ENUM if @string is not valid enum member */
2116 static isds_error string2isds_FileMetaType(const xmlChar *string,
2117 isds_FileMetaType *type) {
2118 if (!string || !type) return IE_INVAL;
2120 if (!xmlStrcmp(string, BAD_CAST "main"))
2121 *type = FILEMETATYPE_MAIN;
2122 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2123 *type = FILEMETATYPE_ENCLOSURE;
2124 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2125 *type = FILEMETATYPE_SIGNATURE;
2126 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2127 *type = FILEMETATYPE_META;
2128 else
2129 return IE_ENUM;
2130 return IE_SUCCESS;
2134 /* Convert UTF-8 @string to ISDS hash @algorithm.
2135 * @Return IE_ENUM if @string is not valid enum member */
2136 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2137 isds_hash_algorithm *algorithm) {
2138 if (!string || !algorithm) return IE_INVAL;
2140 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2141 *algorithm = HASH_ALGORITHM_MD5;
2142 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2143 *algorithm = HASH_ALGORITHM_SHA_1;
2144 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2145 *algorithm = HASH_ALGORITHM_SHA_224;
2146 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2147 *algorithm = HASH_ALGORITHM_SHA_256;
2148 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2149 *algorithm = HASH_ALGORITHM_SHA_384;
2150 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2151 *algorithm = HASH_ALGORITHM_SHA_512;
2152 else
2153 return IE_ENUM;
2154 return IE_SUCCESS;
2158 #if HAVE_LIBCURL
2159 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2160 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2161 if (!time || !string) return IE_INVAL;
2163 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2164 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2165 return IE_ERROR;
2167 return IE_SUCCESS;
2171 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2172 * respects the @time microseconds too. */
2173 static isds_error timeval2timestring(const struct timeval *time,
2174 xmlChar **string) {
2175 struct tm broken;
2177 if (!time || !string) return IE_INVAL;
2179 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
2180 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2182 /* TODO: small negative year should be formatted as "-0012". This is not
2183 * true for glibc "%04d". We should implement it.
2184 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
2185 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2186 if (-1 == isds_asprintf((char **) string,
2187 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
2188 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2189 broken.tm_hour, broken.tm_min, broken.tm_sec,
2190 time->tv_usec))
2191 return IE_ERROR;
2193 return IE_SUCCESS;
2195 #endif /* HAVE_LIBCURL */
2198 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2199 * It respects microseconds too.
2200 * In case of error, @time will be freed. */
2201 static isds_error timestring2timeval(const xmlChar *string,
2202 struct timeval **time) {
2203 struct tm broken;
2204 char *offset, *delim, *endptr;
2205 char subseconds[7];
2206 int offset_hours, offset_minutes;
2207 int i;
2208 #ifdef _WIN32
2209 int tmp;
2210 #endif
2212 if (!time) return IE_INVAL;
2213 if (!string) {
2214 zfree(*time);
2215 return IE_INVAL;
2218 memset(&broken, 0, sizeof(broken));
2220 if (!*time) {
2221 *time = calloc(1, sizeof(**time));
2222 if (!*time) return IE_NOMEM;
2223 } else {
2224 memset(*time, 0, sizeof(**time));
2228 /* xsd:date is ISO 8601 string, thus ASCII */
2229 /*TODO: negative year */
2231 #ifdef _WIN32
2232 i = 0;
2233 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2234 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2235 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2236 &i)) < 6) {
2237 zfree(*time);
2238 return IE_DATE;
2241 broken.tm_year -= 1900;
2242 broken.tm_mon--;
2243 offset = (char*)string + i;
2244 #else
2245 /* Parse date and time without subseconds and offset */
2246 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2247 if (!offset) {
2248 zfree(*time);
2249 return IE_DATE;
2251 #endif
2253 /* Get subseconds */
2254 if (*offset == '.' ) {
2255 offset++;
2257 /* Copy first 6 digits, pad it with zeros.
2258 * XXX: It truncates longer number, no round.
2259 * Current server implementation uses only millisecond resolution. */
2260 /* TODO: isdigit() is locale sensitive */
2261 for (i = 0;
2262 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
2263 i++, offset++) {
2264 subseconds[i] = *offset;
2266 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
2267 subseconds[i] = '0';
2269 subseconds[6] = '\0';
2271 /* Convert it into integer */
2272 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
2273 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
2274 (*time)->tv_usec == LONG_MAX) {
2275 zfree(*time);
2276 return IE_DATE;
2279 /* move to the zone offset delimiter or signal NULL*/
2280 delim = strchr(offset, '-');
2281 if (!delim)
2282 delim = strchr(offset, '+');
2283 if (!delim)
2284 delim = strchr(offset, 'Z');
2285 offset = delim;
2288 /* Get zone offset */
2289 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2290 * "" equals to "Z" and it means UTC zone. */
2291 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2292 * colon separator */
2293 if (offset && (*offset == '-' || *offset == '+')) {
2294 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2295 zfree(*time);
2296 return IE_DATE;
2298 if (*offset == '+') {
2299 broken.tm_hour -= offset_hours;
2300 broken.tm_min -= offset_minutes;
2301 } else {
2302 broken.tm_hour += offset_hours;
2303 broken.tm_min += offset_minutes;
2307 /* Convert to time_t */
2308 (*time)->tv_sec = _isds_timegm(&broken);
2309 if ((*time)->tv_sec == (time_t) -1) {
2310 zfree(*time);
2311 return IE_DATE;
2314 return IE_SUCCESS;
2318 /* Convert unsigned int into isds_message_status.
2319 * @context is session context
2320 * @number is pointer to number value. NULL will be treated as invalid value.
2321 * @status is automatically reallocated status
2322 * @return IE_SUCCESS, or error code and free status */
2323 static isds_error uint2isds_message_status(struct isds_ctx *context,
2324 const unsigned long int *number, isds_message_status **status) {
2325 if (!context) return IE_INVALID_CONTEXT;
2326 if (!status) return IE_INVAL;
2328 free(*status); *status = NULL;
2329 if (!number) return IE_INVAL;
2331 if (*number < 1 || *number > 10) {
2332 isds_printf_message(context, _("Invalid message status value: %lu"),
2333 *number);
2334 return IE_ENUM;
2337 *status = malloc(sizeof(**status));
2338 if (!*status) return IE_NOMEM;
2340 **status = 1 << *number;
2341 return IE_SUCCESS;
2345 /* Convert event description string into isds_event members type and
2346 * description
2347 * @string is raw event description starting with event prefix
2348 * @event is structure where to store type and stripped description to
2349 * @return standard error code, unknown prefix is not classified as an error.
2350 * */
2351 static isds_error eventstring2event(const xmlChar *string,
2352 struct isds_event* event) {
2353 const xmlChar *known_prefixes[] = {
2354 BAD_CAST "EV0:",
2355 BAD_CAST "EV1:",
2356 BAD_CAST "EV2:",
2357 BAD_CAST "EV3:",
2358 BAD_CAST "EV4:",
2359 BAD_CAST "EV5:",
2360 BAD_CAST "EV11:",
2361 BAD_CAST "EV12:",
2362 BAD_CAST "EV13:"
2364 const isds_event_type types[] = {
2365 EVENT_ENTERED_SYSTEM,
2366 EVENT_ACCEPTED_BY_RECIPIENT,
2367 EVENT_ACCEPTED_BY_FICTION,
2368 EVENT_UNDELIVERABLE,
2369 EVENT_COMMERCIAL_ACCEPTED,
2370 EVENT_DELIVERED,
2371 EVENT_PRIMARY_LOGIN,
2372 EVENT_ENTRUSTED_LOGIN,
2373 EVENT_SYSCERT_LOGIN
2375 unsigned int index;
2376 size_t length;
2378 if (!string || !event) return IE_INVAL;
2380 if (!event->type) {
2381 event->type = malloc(sizeof(*event->type));
2382 if (!(event->type)) return IE_NOMEM;
2384 zfree(event->description);
2386 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2387 index++) {
2388 length = xmlUTF8Strlen(known_prefixes[index]);
2390 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2391 /* Prefix is known */
2392 *event->type = types[index];
2394 /* Strip prefix from description and spaces */
2395 /* TODO: Recognize all white spaces from UCS blank class and
2396 * operate on UTF-8 chars. */
2397 for (; string[length] != '\0' && string[length] == ' '; length++);
2398 event->description = strdup((char *) (string + length));
2399 if (!(event->description)) return IE_NOMEM;
2401 return IE_SUCCESS;
2405 /* Unknown event prefix.
2406 * XSD allows any string */
2407 char *string_locale = _isds_utf82locale((char *) string);
2408 isds_log(ILF_ISDS, ILL_WARNING,
2409 _("Unknown delivery info event prefix: %s\n"), string_locale);
2410 free(string_locale);
2412 *event->type = EVENT_UKNOWN;
2413 event->description = strdup((char *) string);
2414 if (!(event->description)) return IE_NOMEM;
2416 return IE_SUCCESS;
2420 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2421 * and leave label */
2422 #define EXTRACT_STRING(element, string) { \
2423 xmlXPathFreeObject(result); \
2424 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2425 if (NULL == (result)) { \
2426 err = IE_ERROR; \
2427 goto leave; \
2429 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2430 if (result->nodesetval->nodeNr > 1) { \
2431 isds_printf_message(context, _("Multiple %s element"), element); \
2432 err = IE_ERROR; \
2433 goto leave; \
2435 (string) = (char *) \
2436 xmlXPathCastNodeSetToString(result->nodesetval); \
2437 if (NULL == (string)) { \
2438 err = IE_ERROR; \
2439 goto leave; \
2444 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2446 char *string = NULL; \
2447 EXTRACT_STRING(element, string); \
2449 if (string) { \
2450 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2451 if (!(booleanPtr)) { \
2452 free(string); \
2453 err = IE_NOMEM; \
2454 goto leave; \
2457 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2458 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2459 *(booleanPtr) = 1; \
2460 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2461 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2462 *(booleanPtr) = 0; \
2463 else { \
2464 char *string_locale = _isds_utf82locale((char*)string); \
2465 isds_printf_message(context, \
2466 _("%s value is not valid boolean: %s"), \
2467 element, string_locale); \
2468 free(string_locale); \
2469 free(string); \
2470 err = IE_ERROR; \
2471 goto leave; \
2474 free(string); \
2478 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2480 char *string = NULL; \
2481 EXTRACT_STRING(element, string); \
2482 if (string) { \
2483 long int number; \
2484 char *endptr; \
2486 number = strtol((char*)string, &endptr, 10); \
2488 if (*endptr != '\0') { \
2489 char *string_locale = _isds_utf82locale((char *)string); \
2490 isds_printf_message(context, \
2491 _("%s is not valid integer: %s"), \
2492 element, string_locale); \
2493 free(string_locale); \
2494 free(string); \
2495 err = IE_ISDS; \
2496 goto leave; \
2499 if (number == LONG_MIN || number == LONG_MAX) { \
2500 char *string_locale = _isds_utf82locale((char *)string); \
2501 isds_printf_message(context, \
2502 _("%s value out of range of long int: %s"), \
2503 element, string_locale); \
2504 free(string_locale); \
2505 free(string); \
2506 err = IE_ERROR; \
2507 goto leave; \
2510 free(string); string = NULL; \
2512 if (!(preallocated)) { \
2513 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2514 if (!(longintPtr)) { \
2515 err = IE_NOMEM; \
2516 goto leave; \
2519 *(longintPtr) = number; \
2523 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2525 char *string = NULL; \
2526 EXTRACT_STRING(element, string); \
2527 if (string) { \
2528 long int number; \
2529 char *endptr; \
2531 number = strtol((char*)string, &endptr, 10); \
2533 if (*endptr != '\0') { \
2534 char *string_locale = _isds_utf82locale((char *)string); \
2535 isds_printf_message(context, \
2536 _("%s is not valid integer: %s"), \
2537 element, string_locale); \
2538 free(string_locale); \
2539 free(string); \
2540 err = IE_ISDS; \
2541 goto leave; \
2544 if (number == LONG_MIN || number == LONG_MAX) { \
2545 char *string_locale = _isds_utf82locale((char *)string); \
2546 isds_printf_message(context, \
2547 _("%s value out of range of long int: %s"), \
2548 element, string_locale); \
2549 free(string_locale); \
2550 free(string); \
2551 err = IE_ERROR; \
2552 goto leave; \
2555 free(string); string = NULL; \
2556 if (number < 0) { \
2557 isds_printf_message(context, \
2558 _("%s value is negative: %ld"), element, number); \
2559 err = IE_ERROR; \
2560 goto leave; \
2563 if (!(preallocated)) { \
2564 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2565 if (!(ulongintPtr)) { \
2566 err = IE_NOMEM; \
2567 goto leave; \
2570 *(ulongintPtr) = number; \
2574 #define EXTRACT_DATE(element, tmPtr) { \
2575 char *string = NULL; \
2576 EXTRACT_STRING(element, string); \
2577 if (NULL != string) { \
2578 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2579 if (NULL == (tmPtr)) { \
2580 free(string); \
2581 err = IE_NOMEM; \
2582 goto leave; \
2584 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2585 if (err) { \
2586 if (err == IE_NOTSUP) { \
2587 err = IE_ISDS; \
2588 char *string_locale = _isds_utf82locale(string); \
2589 char *element_locale = _isds_utf82locale(element); \
2590 isds_printf_message(context, _("Invalid %s value: %s"), \
2591 element_locale, string_locale); \
2592 free(string_locale); \
2593 free(element_locale); \
2595 free(string); \
2596 goto leave; \
2598 free(string); \
2602 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2603 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2604 NULL); \
2605 if ((required) && (!string)) { \
2606 char *attribute_locale = _isds_utf82locale(attribute); \
2607 char *element_locale = \
2608 _isds_utf82locale((char *)xpath_ctx->node->name); \
2609 isds_printf_message(context, \
2610 _("Could not extract required %s attribute value from " \
2611 "%s element"), attribute_locale, element_locale); \
2612 free(element_locale); \
2613 free(attribute_locale); \
2614 err = IE_ERROR; \
2615 goto leave; \
2620 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2622 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2623 (xmlChar *) (string)); \
2624 if (!node) { \
2625 isds_printf_message(context, \
2626 _("Could not add %s child to %s element"), \
2627 element, (parent)->name); \
2628 err = IE_ERROR; \
2629 goto leave; \
2633 #define INSERT_STRING(parent, element, string) \
2634 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2636 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2638 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2639 else { INSERT_STRING(parent, element, "false"); } \
2642 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2644 if (booleanPtr) { \
2645 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2646 } else { \
2647 INSERT_STRING(parent, element, NULL); \
2651 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2652 if ((longintPtr)) { \
2653 /* FIXME: locale sensitive */ \
2654 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2655 err = IE_NOMEM; \
2656 goto leave; \
2658 INSERT_STRING(parent, element, buffer) \
2659 free(buffer); (buffer) = NULL; \
2660 } else { INSERT_STRING(parent, element, NULL) } \
2663 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2664 if ((ulongintPtr)) { \
2665 /* FIXME: locale sensitive */ \
2666 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2667 err = IE_NOMEM; \
2668 goto leave; \
2670 INSERT_STRING(parent, element, buffer) \
2671 free(buffer); (buffer) = NULL; \
2672 } else { INSERT_STRING(parent, element, NULL) } \
2675 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2677 /* FIXME: locale sensitive */ \
2678 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2679 err = IE_NOMEM; \
2680 goto leave; \
2682 INSERT_STRING(parent, element, buffer) \
2683 free(buffer); (buffer) = NULL; \
2686 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2687 * new attribute. */
2688 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2690 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2691 (xmlChar *) (string)); \
2692 if (!attribute_node) { \
2693 isds_printf_message(context, _("Could not add %s " \
2694 "attribute to %s element"), \
2695 (attribute), (parent)->name); \
2696 err = IE_ERROR; \
2697 goto leave; \
2701 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2702 if (string) { \
2703 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2704 if (length > (maximum)) { \
2705 isds_printf_message(context, \
2706 ngettext("%s has more than %d characters", \
2707 "%s has more than %d characters", (maximum)), \
2708 (name), (maximum)); \
2709 err = IE_2BIG; \
2710 goto leave; \
2712 if (length < (minimum)) { \
2713 isds_printf_message(context, \
2714 ngettext("%s has less than %d characters", \
2715 "%s has less than %d characters", (minimum)), \
2716 (name), (minimum)); \
2717 err = IE_2SMALL; \
2718 goto leave; \
2723 #define INSERT_ELEMENT(child, parent, element) \
2725 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2726 if (!(child)) { \
2727 isds_printf_message(context, \
2728 _("Could not add %s child to %s element"), \
2729 (element), (parent)->name); \
2730 err = IE_ERROR; \
2731 goto leave; \
2736 /* Find child element by name in given XPath context and switch context onto
2737 * it. The child must be uniq and must exist. Otherwise fails.
2738 * @context is ISDS context
2739 * @child is child element name
2740 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2741 * into it child. In error case, the @xpath_ctx keeps original value. */
2742 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2743 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2744 isds_error err = IE_SUCCESS;
2745 xmlXPathObjectPtr result = NULL;
2747 if (!context) return IE_INVALID_CONTEXT;
2748 if (!child || !xpath_ctx) return IE_INVAL;
2750 /* Find child */
2751 result = xmlXPathEvalExpression(child, xpath_ctx);
2752 if (!result) {
2753 err = IE_XML;
2754 goto leave;
2757 /* No match */
2758 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2759 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2760 char *child_locale = _isds_utf82locale((char*) child);
2761 isds_printf_message(context,
2762 _("%s element does not contain %s child"),
2763 parent_locale, child_locale);
2764 free(child_locale);
2765 free(parent_locale);
2766 err = IE_NOEXIST;
2767 goto leave;
2770 /* More matches */
2771 if (result->nodesetval->nodeNr > 1) {
2772 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2773 char *child_locale = _isds_utf82locale((char*) child);
2774 isds_printf_message(context,
2775 _("%s element contains multiple %s children"),
2776 parent_locale, child_locale);
2777 free(child_locale);
2778 free(parent_locale);
2779 err = IE_NOTUNIQ;
2780 goto leave;
2783 /* Switch context */
2784 xpath_ctx->node = result->nodesetval->nodeTab[0];
2786 leave:
2787 xmlXPathFreeObject(result);
2788 return err;
2793 #if HAVE_LIBCURL
2794 /* Find and convert XSD:gPersonName group in current node into structure
2795 * @context is ISDS context
2796 * @personName is automatically reallocated person name structure. If no member
2797 * value is found, will be freed.
2798 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2799 * elements
2800 * In case of error @personName will be freed. */
2801 static isds_error extract_gPersonName(struct isds_ctx *context,
2802 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2803 isds_error err = IE_SUCCESS;
2804 xmlXPathObjectPtr result = NULL;
2806 if (!context) return IE_INVALID_CONTEXT;
2807 if (!personName) return IE_INVAL;
2808 isds_PersonName_free(personName);
2809 if (!xpath_ctx) return IE_INVAL;
2812 *personName = calloc(1, sizeof(**personName));
2813 if (!*personName) {
2814 err = IE_NOMEM;
2815 goto leave;
2818 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2819 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2820 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2821 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2823 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2824 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2825 isds_PersonName_free(personName);
2827 leave:
2828 if (err) isds_PersonName_free(personName);
2829 xmlXPathFreeObject(result);
2830 return err;
2834 /* Find and convert XSD:gAddress group in current node into structure
2835 * @context is ISDS context
2836 * @address is automatically reallocated address structure. If no member
2837 * value is found, will be freed.
2838 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2839 * elements
2840 * In case of error @address will be freed. */
2841 static isds_error extract_gAddress(struct isds_ctx *context,
2842 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2843 isds_error err = IE_SUCCESS;
2844 xmlXPathObjectPtr result = NULL;
2846 if (!context) return IE_INVALID_CONTEXT;
2847 if (!address) return IE_INVAL;
2848 isds_Address_free(address);
2849 if (!xpath_ctx) return IE_INVAL;
2852 *address = calloc(1, sizeof(**address));
2853 if (!*address) {
2854 err = IE_NOMEM;
2855 goto leave;
2858 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2859 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2860 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2861 EXTRACT_STRING("isds:adNumberInMunicipality",
2862 (*address)->adNumberInMunicipality);
2863 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2864 EXTRACT_STRING("isds:adState", (*address)->adState);
2866 if (!(*address)->adCity && !(*address)->adStreet &&
2867 !(*address)->adNumberInStreet &&
2868 !(*address)->adNumberInMunicipality &&
2869 !(*address)->adZipCode && !(*address)->adState)
2870 isds_Address_free(address);
2872 leave:
2873 if (err) isds_Address_free(address);
2874 xmlXPathFreeObject(result);
2875 return err;
2879 /* Find and convert isds:biDate element in current node into structure
2880 * @context is ISDS context
2881 * @biDate is automatically reallocated birth date structure. If no member
2882 * value is found, will be freed.
2883 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2884 * element
2885 * In case of error @biDate will be freed. */
2886 static isds_error extract_BiDate(struct isds_ctx *context,
2887 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2888 isds_error err = IE_SUCCESS;
2889 xmlXPathObjectPtr result = NULL;
2890 char *string = NULL;
2892 if (!context) return IE_INVALID_CONTEXT;
2893 if (!biDate) return IE_INVAL;
2894 zfree(*biDate);
2895 if (!xpath_ctx) return IE_INVAL;
2897 EXTRACT_STRING("isds:biDate", string);
2898 if (string) {
2899 *biDate = calloc(1, sizeof(**biDate));
2900 if (!*biDate) {
2901 err = IE_NOMEM;
2902 goto leave;
2904 err = _isds_datestring2tm((xmlChar *)string, *biDate);
2905 if (err) {
2906 if (err == IE_NOTSUP) {
2907 err = IE_ISDS;
2908 char *string_locale = _isds_utf82locale(string);
2909 isds_printf_message(context,
2910 _("Invalid isds:biDate value: %s"), string_locale);
2911 free(string_locale);
2913 goto leave;
2917 leave:
2918 if (err) zfree(*biDate);
2919 free(string);
2920 xmlXPathFreeObject(result);
2921 return err;
2925 /* Convert isds:dBOwnerInfo XML tree into structure
2926 * @context is ISDS context
2927 * @db_owner_info is automatically reallocated box owner info structure
2928 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2929 * In case of error @db_owner_info will be freed. */
2930 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2931 struct isds_DbOwnerInfo **db_owner_info,
2932 xmlXPathContextPtr xpath_ctx) {
2933 isds_error err = IE_SUCCESS;
2934 xmlXPathObjectPtr result = NULL;
2935 char *string = NULL;
2937 if (!context) return IE_INVALID_CONTEXT;
2938 if (!db_owner_info) return IE_INVAL;
2939 isds_DbOwnerInfo_free(db_owner_info);
2940 if (!xpath_ctx) return IE_INVAL;
2943 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2944 if (!*db_owner_info) {
2945 err = IE_NOMEM;
2946 goto leave;
2949 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2951 EXTRACT_STRING("isds:dbType", string);
2952 if (string) {
2953 (*db_owner_info)->dbType =
2954 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2955 if (!(*db_owner_info)->dbType) {
2956 err = IE_NOMEM;
2957 goto leave;
2959 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2960 if (err) {
2961 zfree((*db_owner_info)->dbType);
2962 if (err == IE_ENUM) {
2963 err = IE_ISDS;
2964 char *string_locale = _isds_utf82locale(string);
2965 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2966 string_locale);
2967 free(string_locale);
2969 goto leave;
2971 zfree(string);
2974 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2976 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2977 xpath_ctx);
2978 if (err) goto leave;
2980 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2982 (*db_owner_info)->birthInfo =
2983 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2984 if (!(*db_owner_info)->birthInfo) {
2985 err = IE_NOMEM;
2986 goto leave;
2988 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2989 xpath_ctx);
2990 if (err) goto leave;
2991 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2992 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2993 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2994 if (!(*db_owner_info)->birthInfo->biDate &&
2995 !(*db_owner_info)->birthInfo->biCity &&
2996 !(*db_owner_info)->birthInfo->biCounty &&
2997 !(*db_owner_info)->birthInfo->biState)
2998 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3000 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3001 if (err) goto leave;
3003 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3004 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3005 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3006 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3007 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3009 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3011 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3012 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3013 (*db_owner_info)->dbOpenAddressing);
3015 leave:
3016 if (err) isds_DbOwnerInfo_free(db_owner_info);
3017 free(string);
3018 xmlXPathFreeObject(result);
3019 return err;
3023 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3024 * @context is session context
3025 * @owner is libisds structure with box description
3026 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3027 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3028 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
3030 isds_error err = IE_SUCCESS;
3031 xmlNodePtr node;
3032 xmlChar *string = NULL;
3034 if (!context) return IE_INVALID_CONTEXT;
3035 if (!owner || !db_owner_info) return IE_INVAL;
3038 /* Build XSD:tDbOwnerInfo */
3039 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3040 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3042 /* dbType */
3043 if (owner->dbType) {
3044 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
3045 if (!type_string) {
3046 isds_printf_message(context, _("Invalid dbType value: %d"),
3047 *(owner->dbType));
3048 err = IE_ENUM;
3049 goto leave;
3051 INSERT_STRING(db_owner_info, "dbType", type_string);
3053 INSERT_STRING(db_owner_info, "ic", owner->ic);
3054 if (owner->personName) {
3055 INSERT_STRING(db_owner_info, "pnFirstName",
3056 owner->personName->pnFirstName);
3057 INSERT_STRING(db_owner_info, "pnMiddleName",
3058 owner->personName->pnMiddleName);
3059 INSERT_STRING(db_owner_info, "pnLastName",
3060 owner->personName->pnLastName);
3061 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3062 owner->personName->pnLastNameAtBirth);
3064 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3065 if (owner->birthInfo) {
3066 if (owner->birthInfo->biDate) {
3067 if (!tm2datestring(owner->birthInfo->biDate, &string))
3068 INSERT_STRING(db_owner_info, "biDate", string);
3069 free(string); string = NULL;
3071 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
3072 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
3073 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
3075 if (owner->address) {
3076 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
3077 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
3078 INSERT_STRING(db_owner_info, "adNumberInStreet",
3079 owner->address->adNumberInStreet);
3080 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3081 owner->address->adNumberInMunicipality);
3082 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
3083 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
3085 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3086 INSERT_STRING(db_owner_info, "email", owner->email);
3087 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3089 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3090 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3092 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3093 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3095 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3097 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3098 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3099 owner->dbOpenAddressing);
3101 leave:
3102 free(string);
3103 return err;
3107 /* Convert XSD:tDbUserInfo XML tree into structure
3108 * @context is ISDS context
3109 * @db_user_info is automatically reallocated user info structure
3110 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3111 * In case of error @db_user_info will be freed. */
3112 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3113 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3114 isds_error err = IE_SUCCESS;
3115 xmlXPathObjectPtr result = NULL;
3116 char *string = NULL;
3118 if (!context) return IE_INVALID_CONTEXT;
3119 if (!db_user_info) return IE_INVAL;
3120 isds_DbUserInfo_free(db_user_info);
3121 if (!xpath_ctx) return IE_INVAL;
3124 *db_user_info = calloc(1, sizeof(**db_user_info));
3125 if (!*db_user_info) {
3126 err = IE_NOMEM;
3127 goto leave;
3130 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3132 EXTRACT_STRING("isds:userType", string);
3133 if (string) {
3134 (*db_user_info)->userType =
3135 calloc(1, sizeof(*((*db_user_info)->userType)));
3136 if (!(*db_user_info)->userType) {
3137 err = IE_NOMEM;
3138 goto leave;
3140 err = string2isds_UserType((xmlChar *)string,
3141 (*db_user_info)->userType);
3142 if (err) {
3143 zfree((*db_user_info)->userType);
3144 if (err == IE_ENUM) {
3145 err = IE_ISDS;
3146 char *string_locale = _isds_utf82locale(string);
3147 isds_printf_message(context,
3148 _("Unknown isds:userType value: %s"), string_locale);
3149 free(string_locale);
3151 goto leave;
3153 zfree(string);
3156 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3158 (*db_user_info)->personName =
3159 calloc(1, sizeof(*((*db_user_info)->personName)));
3160 if (!(*db_user_info)->personName) {
3161 err = IE_NOMEM;
3162 goto leave;
3165 err = extract_gPersonName(context, &(*db_user_info)->personName,
3166 xpath_ctx);
3167 if (err) goto leave;
3169 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3170 if (err) goto leave;
3172 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3173 if (err) goto leave;
3175 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3176 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3178 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3179 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3180 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3182 /* ???: Default value is "CZ" according specification. Should we provide
3183 * it? */
3184 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3186 leave:
3187 if (err) isds_DbUserInfo_free(db_user_info);
3188 free(string);
3189 xmlXPathFreeObject(result);
3190 return err;
3194 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3195 * @context is session context
3196 * @user is libisds structure with user description
3197 * @db_user_info is XML element of XSD:tDbUserInfo */
3198 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3199 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3201 isds_error err = IE_SUCCESS;
3202 xmlNodePtr node;
3203 xmlChar *string = NULL;
3205 if (!context) return IE_INVALID_CONTEXT;
3206 if (!user || !db_user_info) return IE_INVAL;
3208 /* Build XSD:tDbUserInfo */
3209 if (user->personName) {
3210 INSERT_STRING(db_user_info, "pnFirstName",
3211 user->personName->pnFirstName);
3212 INSERT_STRING(db_user_info, "pnMiddleName",
3213 user->personName->pnMiddleName);
3214 INSERT_STRING(db_user_info, "pnLastName",
3215 user->personName->pnLastName);
3216 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3217 user->personName->pnLastNameAtBirth);
3219 if (user->address) {
3220 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3221 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3222 INSERT_STRING(db_user_info, "adNumberInStreet",
3223 user->address->adNumberInStreet);
3224 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3225 user->address->adNumberInMunicipality);
3226 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3227 INSERT_STRING(db_user_info, "adState", user->address->adState);
3229 if (user->biDate) {
3230 if (!tm2datestring(user->biDate, &string))
3231 INSERT_STRING(db_user_info, "biDate", string);
3232 zfree(string);
3234 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3235 INSERT_STRING(db_user_info, "userID", user->userID);
3237 /* userType */
3238 if (user->userType) {
3239 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3240 if (!type_string) {
3241 isds_printf_message(context, _("Invalid userType value: %d"),
3242 *(user->userType));
3243 err = IE_ENUM;
3244 goto leave;
3246 INSERT_STRING(db_user_info, "userType", type_string);
3249 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3250 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3251 INSERT_STRING(db_user_info, "ic", user->ic);
3252 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3253 INSERT_STRING(db_user_info, "firmName", user->firmName);
3254 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3255 INSERT_STRING(db_user_info, "caCity", user->caCity);
3256 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3257 INSERT_STRING(db_user_info, "caState", user->caState);
3259 leave:
3260 free(string);
3261 return err;
3265 /* Convert XSD:tPDZRec XML tree into structure
3266 * @context is ISDS context
3267 * @permission is automatically reallocated commercial permission structure
3268 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3269 * In case of error @permission will be freed. */
3270 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3271 struct isds_commercial_permission **permission,
3272 xmlXPathContextPtr xpath_ctx) {
3273 isds_error err = IE_SUCCESS;
3274 xmlXPathObjectPtr result = NULL;
3275 char *string = NULL;
3277 if (!context) return IE_INVALID_CONTEXT;
3278 if (!permission) return IE_INVAL;
3279 isds_commercial_permission_free(permission);
3280 if (!xpath_ctx) return IE_INVAL;
3283 *permission = calloc(1, sizeof(**permission));
3284 if (!*permission) {
3285 err = IE_NOMEM;
3286 goto leave;
3289 EXTRACT_STRING("isds:PDZType", string);
3290 if (string) {
3291 err = string2isds_payment_type((xmlChar *)string,
3292 &(*permission)->type);
3293 if (err) {
3294 if (err == IE_ENUM) {
3295 err = IE_ISDS;
3296 char *string_locale = _isds_utf82locale(string);
3297 isds_printf_message(context,
3298 _("Unknown isds:PDZType value: %s"), string_locale);
3299 free(string_locale);
3301 goto leave;
3303 zfree(string);
3306 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3307 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3309 EXTRACT_STRING("isds:PDZExpire", string);
3310 if (string) {
3311 err = timestring2timeval((xmlChar *) string,
3312 &((*permission)->expiration));
3313 if (err) {
3314 char *string_locale = _isds_utf82locale(string);
3315 if (err == IE_DATE) err = IE_ISDS;
3316 isds_printf_message(context,
3317 _("Could not convert PDZExpire as ISO time: %s"),
3318 string_locale);
3319 free(string_locale);
3320 goto leave;
3322 zfree(string);
3325 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3326 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3328 leave:
3329 if (err) isds_commercial_permission_free(permission);
3330 free(string);
3331 xmlXPathFreeObject(result);
3332 return err;
3336 /* Convert XSD:tCiRecord XML tree into structure
3337 * @context is ISDS context
3338 * @event is automatically reallocated commercial credit event structure
3339 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3340 * In case of error @event will be freed. */
3341 static isds_error extract_CiRecord(struct isds_ctx *context,
3342 struct isds_credit_event **event,
3343 xmlXPathContextPtr xpath_ctx) {
3344 isds_error err = IE_SUCCESS;
3345 xmlXPathObjectPtr result = NULL;
3346 char *string = NULL;
3347 long int *number_ptr;
3349 if (!context) return IE_INVALID_CONTEXT;
3350 if (!event) return IE_INVAL;
3351 isds_credit_event_free(event);
3352 if (!xpath_ctx) return IE_INVAL;
3355 *event = calloc(1, sizeof(**event));
3356 if (!*event) {
3357 err = IE_NOMEM;
3358 goto leave;
3361 EXTRACT_STRING("isds:ciEventTime", string);
3362 if (string) {
3363 err = timestring2timeval((xmlChar *) string,
3364 &(*event)->time);
3365 if (err) {
3366 char *string_locale = _isds_utf82locale(string);
3367 if (err == IE_DATE) err = IE_ISDS;
3368 isds_printf_message(context,
3369 _("Could not convert ciEventTime as ISO time: %s"),
3370 string_locale);
3371 free(string_locale);
3372 goto leave;
3374 zfree(string);
3377 EXTRACT_STRING("isds:ciEventType", string);
3378 if (string) {
3379 err = string2isds_credit_event_type((xmlChar *)string,
3380 &(*event)->type);
3381 if (err) {
3382 if (err == IE_ENUM) {
3383 err = IE_ISDS;
3384 char *string_locale = _isds_utf82locale(string);
3385 isds_printf_message(context,
3386 _("Unknown isds:ciEventType value: %s"), string_locale);
3387 free(string_locale);
3389 goto leave;
3391 zfree(string);
3394 number_ptr = &((*event)->credit_change);
3395 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3396 number_ptr = &(*event)->new_credit;
3397 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3399 switch((*event)->type) {
3400 case ISDS_CREDIT_CHARGED:
3401 EXTRACT_STRING("isds:ciTransID",
3402 (*event)->details.charged.transaction);
3403 break;
3404 case ISDS_CREDIT_DISCHARGED:
3405 EXTRACT_STRING("isds:ciTransID",
3406 (*event)->details.discharged.transaction);
3407 break;
3408 case ISDS_CREDIT_MESSAGE_SENT:
3409 EXTRACT_STRING("isds:ciRecipientID",
3410 (*event)->details.message_sent.recipient);
3411 EXTRACT_STRING("isds:ciPDZID",
3412 (*event)->details.message_sent.message_id);
3413 break;
3414 case ISDS_CREDIT_STORAGE_SET:
3415 number_ptr = &((*event)->details.storage_set.new_capacity);
3416 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3417 EXTRACT_DATE("isds:ciNewFrom",
3418 (*event)->details.storage_set.new_valid_from);
3419 EXTRACT_DATE("isds:ciNewTo",
3420 (*event)->details.storage_set.new_valid_to);
3421 EXTRACT_LONGINT("isds:ciOldCapacity",
3422 (*event)->details.storage_set.old_capacity, 0);
3423 EXTRACT_DATE("isds:ciOldFrom",
3424 (*event)->details.storage_set.old_valid_from);
3425 EXTRACT_DATE("isds:ciOldTo",
3426 (*event)->details.storage_set.old_valid_to);
3427 EXTRACT_STRING("isds:ciDoneBy",
3428 (*event)->details.storage_set.initiator);
3429 break;
3430 case ISDS_CREDIT_EXPIRED:
3431 break;
3434 leave:
3435 if (err) isds_credit_event_free(event);
3436 free(string);
3437 xmlXPathFreeObject(result);
3438 return err;
3442 #endif /* HAVE_LIBCURL */
3445 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3446 * isds_envelope structure. The envelope is automatically allocated but not
3447 * reallocated. The date are just appended into envelope structure.
3448 * @context is ISDS context
3449 * @envelope is automatically allocated message envelope structure
3450 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3451 * In case of error @envelope will be freed. */
3452 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3453 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3454 isds_error err = IE_SUCCESS;
3455 xmlXPathObjectPtr result = NULL;
3457 if (!context) return IE_INVALID_CONTEXT;
3458 if (!envelope) return IE_INVAL;
3459 if (!xpath_ctx) return IE_INVAL;
3462 if (!*envelope) {
3463 /* Allocate envelope */
3464 *envelope = calloc(1, sizeof(**envelope));
3465 if (!*envelope) {
3466 err = IE_NOMEM;
3467 goto leave;
3469 } else {
3470 /* Else free former data */
3471 zfree((*envelope)->dmSenderOrgUnit);
3472 zfree((*envelope)->dmSenderOrgUnitNum);
3473 zfree((*envelope)->dbIDRecipient);
3474 zfree((*envelope)->dmRecipientOrgUnit);
3475 zfree((*envelope)->dmRecipientOrgUnitNum);
3476 zfree((*envelope)->dmToHands);
3477 zfree((*envelope)->dmAnnotation);
3478 zfree((*envelope)->dmRecipientRefNumber);
3479 zfree((*envelope)->dmSenderRefNumber);
3480 zfree((*envelope)->dmRecipientIdent);
3481 zfree((*envelope)->dmSenderIdent);
3482 zfree((*envelope)->dmLegalTitleLaw);
3483 zfree((*envelope)->dmLegalTitleYear);
3484 zfree((*envelope)->dmLegalTitleSect);
3485 zfree((*envelope)->dmLegalTitlePar);
3486 zfree((*envelope)->dmLegalTitlePoint);
3487 zfree((*envelope)->dmPersonalDelivery);
3488 zfree((*envelope)->dmAllowSubstDelivery);
3491 /* Extract envelope elements added by sender or ISDS
3492 * (XSD: gMessageEnvelopeSub type) */
3493 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3494 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3495 (*envelope)->dmSenderOrgUnitNum, 0);
3496 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3497 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3498 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3499 (*envelope)->dmRecipientOrgUnitNum, 0);
3500 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3501 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3502 EXTRACT_STRING("isds:dmRecipientRefNumber",
3503 (*envelope)->dmRecipientRefNumber);
3504 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3505 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3506 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3508 /* Extract envelope elements regarding law reference */
3509 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3510 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3511 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3512 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3513 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3515 /* Extract envelope other elements */
3516 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3517 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3518 (*envelope)->dmAllowSubstDelivery);
3520 leave:
3521 if (err) isds_envelope_free(envelope);
3522 xmlXPathFreeObject(result);
3523 return err;
3528 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3529 * isds_envelope structure. The envelope is automatically allocated but not
3530 * reallocated. The date are just appended into envelope structure.
3531 * @context is ISDS context
3532 * @envelope is automatically allocated message envelope structure
3533 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3534 * In case of error @envelope will be freed. */
3535 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3536 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3537 isds_error err = IE_SUCCESS;
3538 xmlXPathObjectPtr result = NULL;
3540 if (!context) return IE_INVALID_CONTEXT;
3541 if (!envelope) return IE_INVAL;
3542 if (!xpath_ctx) return IE_INVAL;
3545 if (!*envelope) {
3546 /* Allocate envelope */
3547 *envelope = calloc(1, sizeof(**envelope));
3548 if (!*envelope) {
3549 err = IE_NOMEM;
3550 goto leave;
3552 } else {
3553 /* Else free former data */
3554 zfree((*envelope)->dmID);
3555 zfree((*envelope)->dbIDSender);
3556 zfree((*envelope)->dmSender);
3557 zfree((*envelope)->dmSenderAddress);
3558 zfree((*envelope)->dmSenderType);
3559 zfree((*envelope)->dmRecipient);
3560 zfree((*envelope)->dmRecipientAddress);
3561 zfree((*envelope)->dmAmbiguousRecipient);
3564 /* Extract envelope elements added by ISDS
3565 * (XSD: gMessageEnvelope type) */
3566 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3567 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3568 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3569 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3570 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3571 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3572 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3573 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3574 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3575 (*envelope)->dmAmbiguousRecipient);
3577 /* Extract envelope elements added by sender and ISDS
3578 * (XSD: gMessageEnvelope type) */
3579 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3580 if (err) goto leave;
3582 leave:
3583 if (err) isds_envelope_free(envelope);
3584 xmlXPathFreeObject(result);
3585 return err;
3589 /* Convert other envelope elements from XML tree into isds_envelope structure:
3590 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3591 * The envelope is automatically allocated but not reallocated.
3592 * The data are just appended into envelope structure.
3593 * @context is ISDS context
3594 * @envelope is automatically allocated message envelope structure
3595 * @xpath_ctx is XPath context with current node as parent desired elements
3596 * In case of error @envelope will be freed. */
3597 static isds_error append_status_size_times(struct isds_ctx *context,
3598 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3599 isds_error err = IE_SUCCESS;
3600 xmlXPathObjectPtr result = NULL;
3601 char *string = NULL;
3602 unsigned long int *unumber = NULL;
3604 if (!context) return IE_INVALID_CONTEXT;
3605 if (!envelope) return IE_INVAL;
3606 if (!xpath_ctx) return IE_INVAL;
3609 if (!*envelope) {
3610 /* Allocate new */
3611 *envelope = calloc(1, sizeof(**envelope));
3612 if (!*envelope) {
3613 err = IE_NOMEM;
3614 goto leave;
3616 } else {
3617 /* Free old data */
3618 zfree((*envelope)->dmMessageStatus);
3619 zfree((*envelope)->dmAttachmentSize);
3620 zfree((*envelope)->dmDeliveryTime);
3621 zfree((*envelope)->dmAcceptanceTime);
3625 /* dmMessageStatus element is mandatory */
3626 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3627 if (!unumber) {
3628 isds_log_message(context,
3629 _("Missing mandatory sisds:dmMessageStatus integer"));
3630 err = IE_ISDS;
3631 goto leave;
3633 err = uint2isds_message_status(context, unumber,
3634 &((*envelope)->dmMessageStatus));
3635 if (err) {
3636 if (err == IE_ENUM) err = IE_ISDS;
3637 goto leave;
3639 free(unumber); unumber = NULL;
3641 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3644 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3645 if (string) {
3646 err = timestring2timeval((xmlChar *) string,
3647 &((*envelope)->dmDeliveryTime));
3648 if (err) {
3649 char *string_locale = _isds_utf82locale(string);
3650 if (err == IE_DATE) err = IE_ISDS;
3651 isds_printf_message(context,
3652 _("Could not convert dmDeliveryTime as ISO time: %s"),
3653 string_locale);
3654 free(string_locale);
3655 goto leave;
3657 zfree(string);
3660 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3661 if (string) {
3662 err = timestring2timeval((xmlChar *) string,
3663 &((*envelope)->dmAcceptanceTime));
3664 if (err) {
3665 char *string_locale = _isds_utf82locale(string);
3666 if (err == IE_DATE) err = IE_ISDS;
3667 isds_printf_message(context,
3668 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3669 string_locale);
3670 free(string_locale);
3671 goto leave;
3673 zfree(string);
3676 leave:
3677 if (err) isds_envelope_free(envelope);
3678 free(unumber);
3679 free(string);
3680 xmlXPathFreeObject(result);
3681 return err;
3685 /* Convert message type attribute of current element into isds_envelope
3686 * structure.
3687 * TODO: This function can be incorporated into append_status_size_times() as
3688 * they are called always together.
3689 * The envelope is automatically allocated but not reallocated.
3690 * The data are just appended into envelope structure.
3691 * @context is ISDS context
3692 * @envelope is automatically allocated message envelope structure
3693 * @xpath_ctx is XPath context with current node as parent of attribute
3694 * carrying message type
3695 * In case of error @envelope will be freed. */
3696 static isds_error append_message_type(struct isds_ctx *context,
3697 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3698 isds_error err = IE_SUCCESS;
3700 if (!context) return IE_INVALID_CONTEXT;
3701 if (!envelope) return IE_INVAL;
3702 if (!xpath_ctx) return IE_INVAL;
3705 if (!*envelope) {
3706 /* Allocate new */
3707 *envelope = calloc(1, sizeof(**envelope));
3708 if (!*envelope) {
3709 err = IE_NOMEM;
3710 goto leave;
3712 } else {
3713 /* Free old data */
3714 zfree((*envelope)->dmType);
3718 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3720 if (!(*envelope)->dmType) {
3721 /* Use default value */
3722 (*envelope)->dmType = strdup("V");
3723 if (!(*envelope)->dmType) {
3724 err = IE_NOMEM;
3725 goto leave;
3727 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3728 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3729 isds_printf_message(context,
3730 _("Message type in dmType attribute is not 1 character long: "
3731 "%s"),
3732 type_locale);
3733 free(type_locale);
3734 err = IE_ISDS;
3735 goto leave;
3738 leave:
3739 if (err) isds_envelope_free(envelope);
3740 return err;
3744 #if HAVE_LIBCURL
3745 /* Convert dmType isds_envelope member into XML attribute and append it to
3746 * current node.
3747 * @context is ISDS context
3748 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3749 * @dm_envelope is XML element the resulting attribute will be appended to.
3750 * @return error code, in case of error context' message is filled. */
3751 static isds_error insert_message_type(struct isds_ctx *context,
3752 const char *type, xmlNodePtr dm_envelope) {
3753 isds_error err = IE_SUCCESS;
3754 xmlAttrPtr attribute_node;
3756 if (!context) return IE_INVALID_CONTEXT;
3757 if (!dm_envelope) return IE_INVAL;
3759 /* Insert optional message type */
3760 if (type) {
3761 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3762 char *type_locale = _isds_utf82locale(type);
3763 isds_printf_message(context,
3764 _("Message type in envelope is not 1 character long: %s"),
3765 type_locale);
3766 free(type_locale);
3767 err = IE_INVAL;
3768 goto leave;
3770 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3773 leave:
3774 return err;
3776 #endif /* HAVE_LIBCURL */
3779 /* Extract message document into reallocated document structure
3780 * @context is ISDS context
3781 * @document is automatically reallocated message documents structure
3782 * @xpath_ctx is XPath context with current node as isds:dmFile
3783 * In case of error @document will be freed. */
3784 static isds_error extract_document(struct isds_ctx *context,
3785 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3786 isds_error err = IE_SUCCESS;
3787 xmlXPathObjectPtr result = NULL;
3788 xmlNodePtr file_node;
3789 char *string = NULL;
3791 if (!context) return IE_INVALID_CONTEXT;
3792 if (!document) return IE_INVAL;
3793 isds_document_free(document);
3794 if (!xpath_ctx) return IE_INVAL;
3795 file_node = xpath_ctx->node;
3797 *document = calloc(1, sizeof(**document));
3798 if (!*document) {
3799 err = IE_NOMEM;
3800 goto leave;
3803 /* Extract document meta data */
3804 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3805 if (context->normalize_mime_type) {
3806 const char *normalized_type =
3807 isds_normalize_mime_type((*document)->dmMimeType);
3808 if (NULL != normalized_type &&
3809 normalized_type != (*document)->dmMimeType) {
3810 char *new_type = strdup(normalized_type);
3811 if (NULL == new_type) {
3812 isds_printf_message(context,
3813 _("Not enough memory to normalize document MIME type"));
3814 err = IE_NOMEM;
3815 goto leave;
3817 free((*document)->dmMimeType);
3818 (*document)->dmMimeType = new_type;
3822 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3823 err = string2isds_FileMetaType((xmlChar*)string,
3824 &((*document)->dmFileMetaType));
3825 if (err) {
3826 char *meta_type_locale = _isds_utf82locale(string);
3827 isds_printf_message(context,
3828 _("Document has invalid dmFileMetaType attribute value: %s"),
3829 meta_type_locale);
3830 free(meta_type_locale);
3831 err = IE_ISDS;
3832 goto leave;
3834 zfree(string);
3836 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3837 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3838 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3839 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3842 /* Extract document data.
3843 * Base64 encoded blob or XML subtree must be presented. */
3845 /* Check for dmEncodedContent */
3846 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3847 xpath_ctx);
3848 if (!result) {
3849 err = IE_XML;
3850 goto leave;
3853 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3854 /* Here we have Base64 blob */
3855 (*document)->is_xml = 0;
3857 if (result->nodesetval->nodeNr > 1) {
3858 isds_printf_message(context,
3859 _("Document has more dmEncodedContent elements"));
3860 err = IE_ISDS;
3861 goto leave;
3864 xmlXPathFreeObject(result); result = NULL;
3865 EXTRACT_STRING("isds:dmEncodedContent", string);
3867 /* Decode non-empty document */
3868 if (string && string[0] != '\0') {
3869 (*document)->data_length =
3870 _isds_b64decode(string, &((*document)->data));
3871 if ((*document)->data_length == (size_t) -1) {
3872 isds_printf_message(context,
3873 _("Error while Base64-decoding document content"));
3874 err = IE_ERROR;
3875 goto leave;
3878 } else {
3879 /* No Base64 blob, try XML document */
3880 xmlXPathFreeObject(result); result = NULL;
3881 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3882 xpath_ctx);
3883 if (!result) {
3884 err = IE_XML;
3885 goto leave;
3888 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3889 /* Here we have XML document */
3890 (*document)->is_xml = 1;
3892 if (result->nodesetval->nodeNr > 1) {
3893 isds_printf_message(context,
3894 _("Document has more dmXMLContent elements"));
3895 err = IE_ISDS;
3896 goto leave;
3899 /* XXX: We cannot serialize the content simply because:
3900 * - XML document may point out of its scope (e.g. to message
3901 * envelope)
3902 * - isds:dmXMLContent can contain more elements, no element,
3903 * a text node only
3904 * - it's not the XML way
3905 * Thus we provide the only right solution: XML DOM. Let's
3906 * application to cope with this hot potato :) */
3907 (*document)->xml_node_list =
3908 result->nodesetval->nodeTab[0]->children;
3909 } else {
3910 /* No base64 blob, nor XML document */
3911 isds_printf_message(context,
3912 _("Document has no dmEncodedContent, nor dmXMLContent "
3913 "element"));
3914 err = IE_ISDS;
3915 goto leave;
3920 leave:
3921 if (err) isds_document_free(document);
3922 free(string);
3923 xmlXPathFreeObject(result);
3924 xpath_ctx->node = file_node;
3925 return err;
3930 /* Extract message documents into reallocated list of documents
3931 * @context is ISDS context
3932 * @documents is automatically reallocated message documents list structure
3933 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3934 * In case of error @documents will be freed. */
3935 static isds_error extract_documents(struct isds_ctx *context,
3936 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
3937 isds_error err = IE_SUCCESS;
3938 xmlXPathObjectPtr result = NULL;
3939 xmlNodePtr files_node;
3940 struct isds_list *document, *prev_document = NULL;
3942 if (!context) return IE_INVALID_CONTEXT;
3943 if (!documents) return IE_INVAL;
3944 isds_list_free(documents);
3945 if (!xpath_ctx) return IE_INVAL;
3946 files_node = xpath_ctx->node;
3948 /* Find documents */
3949 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
3950 if (!result) {
3951 err = IE_XML;
3952 goto leave;
3955 /* No match */
3956 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3957 isds_printf_message(context,
3958 _("Message does not contain any document"));
3959 err = IE_ISDS;
3960 goto leave;
3964 /* Iterate over documents */
3965 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3967 /* Allocate and append list item */
3968 document = calloc(1, sizeof(*document));
3969 if (!document) {
3970 err = IE_NOMEM;
3971 goto leave;
3973 document->destructor = (void (*)(void **))isds_document_free;
3974 if (i == 0) *documents = document;
3975 else prev_document->next = document;
3976 prev_document = document;
3978 /* Extract document */
3979 xpath_ctx->node = result->nodesetval->nodeTab[i];
3980 err = extract_document(context,
3981 (struct isds_document **) &(document->data), xpath_ctx);
3982 if (err) goto leave;
3986 leave:
3987 if (err) isds_list_free(documents);
3988 xmlXPathFreeObject(result);
3989 xpath_ctx->node = files_node;
3990 return err;
3994 #if HAVE_LIBCURL
3995 /* Convert isds:dmRecord XML tree into structure
3996 * @context is ISDS context
3997 * @envelope is automatically reallocated message envelope structure
3998 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3999 * In case of error @envelope will be freed. */
4000 static isds_error extract_DmRecord(struct isds_ctx *context,
4001 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4002 isds_error err = IE_SUCCESS;
4003 xmlXPathObjectPtr result = NULL;
4005 if (!context) return IE_INVALID_CONTEXT;
4006 if (!envelope) return IE_INVAL;
4007 isds_envelope_free(envelope);
4008 if (!xpath_ctx) return IE_INVAL;
4011 *envelope = calloc(1, sizeof(**envelope));
4012 if (!*envelope) {
4013 err = IE_NOMEM;
4014 goto leave;
4018 /* Extract tRecord data */
4019 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4021 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4022 * dmAcceptanceTime. */
4023 err = append_status_size_times(context, envelope, xpath_ctx);
4024 if (err) goto leave;
4026 /* Extract envelope elements added by sender and ISDS
4027 * (XSD: gMessageEnvelope type) */
4028 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4029 if (err) goto leave;
4031 /* Get message type */
4032 err = append_message_type(context, envelope, xpath_ctx);
4033 if (err) goto leave;
4036 leave:
4037 if (err) isds_envelope_free(envelope);
4038 xmlXPathFreeObject(result);
4039 return err;
4043 /* Convert XSD:tStateChangesRecord type XML tree into structure
4044 * @context is ISDS context
4045 * @changed_status is automatically reallocated message state change structure
4046 * @xpath_ctx is XPath context with current node as element of
4047 * XSD:tStateChangesRecord type
4048 * In case of error @changed_status will be freed. */
4049 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4050 struct isds_message_status_change **changed_status,
4051 xmlXPathContextPtr xpath_ctx) {
4052 isds_error err = IE_SUCCESS;
4053 xmlXPathObjectPtr result = NULL;
4054 unsigned long int *unumber = NULL;
4055 char *string = NULL;
4057 if (!context) return IE_INVALID_CONTEXT;
4058 if (!changed_status) return IE_INVAL;
4059 isds_message_status_change_free(changed_status);
4060 if (!xpath_ctx) return IE_INVAL;
4063 *changed_status = calloc(1, sizeof(**changed_status));
4064 if (!*changed_status) {
4065 err = IE_NOMEM;
4066 goto leave;
4070 /* Extract tGetStateChangesInput data */
4071 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4073 /* dmEventTime is mandatory */
4074 EXTRACT_STRING("isds:dmEventTime", string);
4075 if (string) {
4076 err = timestring2timeval((xmlChar *) string,
4077 &((*changed_status)->time));
4078 if (err) {
4079 char *string_locale = _isds_utf82locale(string);
4080 if (err == IE_DATE) err = IE_ISDS;
4081 isds_printf_message(context,
4082 _("Could not convert dmEventTime as ISO time: %s"),
4083 string_locale);
4084 free(string_locale);
4085 goto leave;
4087 zfree(string);
4090 /* dmMessageStatus element is mandatory */
4091 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4092 if (!unumber) {
4093 isds_log_message(context,
4094 _("Missing mandatory isds:dmMessageStatus integer"));
4095 err = IE_ISDS;
4096 goto leave;
4098 err = uint2isds_message_status(context, unumber,
4099 &((*changed_status)->dmMessageStatus));
4100 if (err) {
4101 if (err == IE_ENUM) err = IE_ISDS;
4102 goto leave;
4104 zfree(unumber);
4107 leave:
4108 free(unumber);
4109 free(string);
4110 if (err) isds_message_status_change_free(changed_status);
4111 xmlXPathFreeObject(result);
4112 return err;
4114 #endif /* HAVE_LIBCURL */
4117 /* Find and convert isds:dmHash XML tree into structure
4118 * @context is ISDS context
4119 * @envelope is automatically reallocated message hash structure
4120 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4121 * In case of error @hash will be freed. */
4122 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4123 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4124 isds_error err = IE_SUCCESS;
4125 xmlNodePtr old_ctx_node;
4126 xmlXPathObjectPtr result = NULL;
4127 char *string = NULL;
4129 if (!context) return IE_INVALID_CONTEXT;
4130 if (!hash) return IE_INVAL;
4131 isds_hash_free(hash);
4132 if (!xpath_ctx) return IE_INVAL;
4134 old_ctx_node = xpath_ctx->node;
4136 *hash = calloc(1, sizeof(**hash));
4137 if (!*hash) {
4138 err = IE_NOMEM;
4139 goto leave;
4142 /* Locate dmHash */
4143 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4144 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4145 err = IE_ISDS;
4146 goto leave;
4148 if (err) {
4149 err = IE_ERROR;
4150 goto leave;
4153 /* Get hash algorithm */
4154 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4155 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4156 if (err) {
4157 if (err == IE_ENUM) {
4158 char *string_locale = _isds_utf82locale(string);
4159 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4160 string_locale);
4161 free(string_locale);
4163 goto leave;
4165 zfree(string);
4167 /* Get hash value */
4168 EXTRACT_STRING(".", string);
4169 if (!string) {
4170 isds_printf_message(context,
4171 _("sisds:dmHash element is missing hash value"));
4172 err = IE_ISDS;
4173 goto leave;
4175 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4176 if ((*hash)->length == (size_t) -1) {
4177 isds_printf_message(context,
4178 _("Error while Base64-decoding hash value"));
4179 err = IE_ERROR;
4180 goto leave;
4183 leave:
4184 if (err) isds_hash_free(hash);
4185 free(string);
4186 xmlXPathFreeObject(result);
4187 xpath_ctx->node = old_ctx_node;
4188 return err;
4192 /* Find and append isds:dmQTimestamp XML tree into envelope.
4193 * Because one service is allowed to miss time-stamp content, and we think
4194 * other could too (flaw in specification), this function is deliberated and
4195 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4196 * @context is ISDS context
4197 * @envelope is automatically allocated envelope structure
4198 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4199 * child
4200 * In case of error @envelope will be freed. */
4201 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4202 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4203 isds_error err = IE_SUCCESS;
4204 xmlXPathObjectPtr result = NULL;
4205 char *string = NULL;
4207 if (!context) return IE_INVALID_CONTEXT;
4208 if (!envelope) return IE_INVAL;
4209 if (!xpath_ctx) {
4210 isds_envelope_free(envelope);
4211 return IE_INVAL;
4214 if (!*envelope) {
4215 *envelope = calloc(1, sizeof(**envelope));
4216 if (!*envelope) {
4217 err = IE_NOMEM;
4218 goto leave;
4220 } else {
4221 zfree((*envelope)->timestamp);
4222 (*envelope)->timestamp_length = 0;
4225 /* Get dmQTimestamp */
4226 EXTRACT_STRING("sisds:dmQTimestamp", string);
4227 if (!string) {
4228 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4229 goto leave;
4231 (*envelope)->timestamp_length =
4232 _isds_b64decode(string, &((*envelope)->timestamp));
4233 if ((*envelope)->timestamp_length == (size_t) -1) {
4234 isds_printf_message(context,
4235 _("Error while Base64-decoding time stamp value"));
4236 err = IE_ERROR;
4237 goto leave;
4240 leave:
4241 if (err) isds_envelope_free(envelope);
4242 free(string);
4243 xmlXPathFreeObject(result);
4244 return err;
4248 /* Convert XSD tReturnedMessage XML tree into message structure.
4249 * It does not store serialized XML tree into message->raw.
4250 * It does store (pointer to) parsed XML tree into message->xml if needed.
4251 * @context is ISDS context
4252 * @include_documents Use true if documents must be extracted
4253 * (tReturnedMessage XSD type), use false if documents shall be omitted
4254 * (tReturnedMessageEnvelope).
4255 * @message is automatically reallocated message structure
4256 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4257 * type
4258 * In case of error @message will be freed. */
4259 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4260 const _Bool include_documents, struct isds_message **message,
4261 xmlXPathContextPtr xpath_ctx) {
4262 isds_error err = IE_SUCCESS;
4263 xmlNodePtr message_node;
4265 if (!context) return IE_INVALID_CONTEXT;
4266 if (!message) return IE_INVAL;
4267 isds_message_free(message);
4268 if (!xpath_ctx) return IE_INVAL;
4271 *message = calloc(1, sizeof(**message));
4272 if (!*message) {
4273 err = IE_NOMEM;
4274 goto leave;
4277 /* Save message XPATH context node */
4278 message_node = xpath_ctx->node;
4281 /* Extract dmDM */
4282 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4283 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4284 if (err) { err = IE_ERROR; goto leave; }
4285 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4286 if (err) goto leave;
4288 if (include_documents) {
4289 struct isds_list *item;
4291 /* Extract dmFiles */
4292 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4293 xpath_ctx);
4294 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4295 err = IE_ISDS; goto leave;
4297 if (err) { err = IE_ERROR; goto leave; }
4298 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4299 if (err) goto leave;
4301 /* Store xmlDoc of this message if needed */
4302 /* Only if we got a XML document in all the documents. */
4303 for (item = (*message)->documents; item; item = item->next) {
4304 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4305 (*message)->xml = xpath_ctx->doc;
4306 break;
4312 /* Restore context to message */
4313 xpath_ctx->node = message_node;
4315 /* Extract dmHash */
4316 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4317 xpath_ctx);
4318 if (err) goto leave;
4320 /* Extract dmQTimestamp, */
4321 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4322 xpath_ctx);
4323 if (err) goto leave;
4325 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4326 * dmAcceptanceTime. */
4327 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4328 if (err) goto leave;
4330 /* Get message type */
4331 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4332 if (err) goto leave;
4334 leave:
4335 if (err) isds_message_free(message);
4336 return err;
4340 /* Extract message event into reallocated isds_event structure
4341 * @context is ISDS context
4342 * @event is automatically reallocated message event structure
4343 * @xpath_ctx is XPath context with current node as isds:dmEvent
4344 * In case of error @event will be freed. */
4345 static isds_error extract_event(struct isds_ctx *context,
4346 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4347 isds_error err = IE_SUCCESS;
4348 xmlXPathObjectPtr result = NULL;
4349 xmlNodePtr event_node;
4350 char *string = NULL;
4352 if (!context) return IE_INVALID_CONTEXT;
4353 if (!event) return IE_INVAL;
4354 isds_event_free(event);
4355 if (!xpath_ctx) return IE_INVAL;
4356 event_node = xpath_ctx->node;
4358 *event = calloc(1, sizeof(**event));
4359 if (!*event) {
4360 err = IE_NOMEM;
4361 goto leave;
4364 /* Extract event data.
4365 * All elements are optional according XSD. That's funny. */
4366 EXTRACT_STRING("sisds:dmEventTime", string);
4367 if (string) {
4368 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4369 if (err) {
4370 char *string_locale = _isds_utf82locale(string);
4371 if (err == IE_DATE) err = IE_ISDS;
4372 isds_printf_message(context,
4373 _("Could not convert dmEventTime as ISO time: %s"),
4374 string_locale);
4375 free(string_locale);
4376 goto leave;
4378 zfree(string);
4381 /* dmEventDescr element has prefix and the rest */
4382 EXTRACT_STRING("sisds:dmEventDescr", string);
4383 if (string) {
4384 err = eventstring2event((xmlChar *) string, *event);
4385 if (err) goto leave;
4386 zfree(string);
4389 leave:
4390 if (err) isds_event_free(event);
4391 free(string);
4392 xmlXPathFreeObject(result);
4393 xpath_ctx->node = event_node;
4394 return err;
4398 /* Convert element of XSD tEventsArray type from XML tree into
4399 * isds_list of isds_event's structure. The list is automatically reallocated.
4400 * @context is ISDS context
4401 * @events is automatically reallocated list of event structures
4402 * @xpath_ctx is XPath context with current node as tEventsArray
4403 * In case of error @events will be freed. */
4404 static isds_error extract_events(struct isds_ctx *context,
4405 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4406 isds_error err = IE_SUCCESS;
4407 xmlXPathObjectPtr result = NULL;
4408 xmlNodePtr events_node;
4409 struct isds_list *event, *prev_event = NULL;
4411 if (!context) return IE_INVALID_CONTEXT;
4412 if (!events) return IE_INVAL;
4413 if (!xpath_ctx) return IE_INVAL;
4414 events_node = xpath_ctx->node;
4416 /* Free old list */
4417 isds_list_free(events);
4419 /* Find events */
4420 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4421 if (!result) {
4422 err = IE_XML;
4423 goto leave;
4426 /* No match */
4427 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4428 isds_printf_message(context,
4429 _("Delivery info does not contain any event"));
4430 err = IE_ISDS;
4431 goto leave;
4435 /* Iterate over events */
4436 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4438 /* Allocate and append list item */
4439 event = calloc(1, sizeof(*event));
4440 if (!event) {
4441 err = IE_NOMEM;
4442 goto leave;
4444 event->destructor = (void (*)(void **))isds_event_free;
4445 if (i == 0) *events = event;
4446 else prev_event->next = event;
4447 prev_event = event;
4449 /* Extract event */
4450 xpath_ctx->node = result->nodesetval->nodeTab[i];
4451 err = extract_event(context,
4452 (struct isds_event **) &(event->data), xpath_ctx);
4453 if (err) goto leave;
4457 leave:
4458 if (err) isds_list_free(events);
4459 xmlXPathFreeObject(result);
4460 xpath_ctx->node = events_node;
4461 return err;
4465 #if HAVE_LIBCURL
4466 /* Insert Base64 encoded data as element with text child.
4467 * @context is session context
4468 * @parent is XML node to append @element with @data as child
4469 * @ns is XML namespace of @element, use NULL to inherit from @parent
4470 * @element is UTF-8 encoded name of new element
4471 * @data is bit stream to encode into @element
4472 * @length is size of @data in bytes
4473 * @return standard error code and fill long error message if needed */
4474 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4475 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4476 const void *data, size_t length) {
4477 isds_error err = IE_SUCCESS;
4478 xmlNodePtr node;
4480 if (!context) return IE_INVALID_CONTEXT;
4481 if (!data && length > 0) return IE_INVAL;
4482 if (!parent || !element) return IE_INVAL;
4484 xmlChar *base64data = NULL;
4485 base64data = (xmlChar *) _isds_b64encode(data, length);
4486 if (!base64data) {
4487 isds_printf_message(context,
4488 ngettext("Not enough memory to encode %zd byte into Base64",
4489 "Not enough memory to encode %zd bytes into Base64",
4490 length),
4491 length);
4492 err = IE_NOMEM;
4493 goto leave;
4495 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4497 leave:
4498 free(base64data);
4499 return err;
4503 /* Convert isds_document structure into XML tree and append to dmFiles node.
4504 * @context is session context
4505 * @document is ISDS document
4506 * @dm_files is XML element the resulting tree will be appended to as a child.
4507 * @return error code, in case of error context' message is filled. */
4508 static isds_error insert_document(struct isds_ctx *context,
4509 struct isds_document *document, xmlNodePtr dm_files) {
4510 isds_error err = IE_SUCCESS;
4511 xmlNodePtr new_file = NULL, file = NULL, node;
4512 xmlAttrPtr attribute_node;
4514 if (!context) return IE_INVALID_CONTEXT;
4515 if (!document || !dm_files) return IE_INVAL;
4517 /* Allocate new dmFile */
4518 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4519 if (!new_file) {
4520 isds_printf_message(context, _("Could not allocate main dmFile"));
4521 err = IE_ERROR;
4522 goto leave;
4524 /* Append the new dmFile.
4525 * XXX: Main document must go first */
4526 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4527 file = xmlAddPrevSibling(dm_files->children, new_file);
4528 else
4529 file = xmlAddChild(dm_files, new_file);
4531 if (!file) {
4532 xmlFreeNode(new_file); new_file = NULL;
4533 isds_printf_message(context, _("Could not add dmFile child to "
4534 "%s element"), dm_files->name);
4535 err = IE_ERROR;
4536 goto leave;
4539 /* @dmMimeType is required */
4540 if (!document->dmMimeType) {
4541 isds_log_message(context,
4542 _("Document is missing mandatory MIME type definition"));
4543 err = IE_INVAL;
4544 goto leave;
4546 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4548 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4549 if (!string) {
4550 isds_printf_message(context,
4551 _("Document has unknown dmFileMetaType: %ld"),
4552 document->dmFileMetaType);
4553 err = IE_ENUM;
4554 goto leave;
4556 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4558 if (document->dmFileGuid) {
4559 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4561 if (document->dmUpFileGuid) {
4562 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4565 /* @dmFileDescr is required */
4566 if (!document->dmFileDescr) {
4567 isds_log_message(context,
4568 _("Document is missing mandatory description (title)"));
4569 err = IE_INVAL;
4570 goto leave;
4572 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4574 if (document->dmFormat) {
4575 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4579 /* Insert content (body) of the document. */
4580 if (document->is_xml) {
4581 /* XML document requested */
4583 /* Allocate new dmXMLContent */
4584 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4585 if (!xmlcontent) {
4586 isds_printf_message(context,
4587 _("Could not allocate dmXMLContent element"));
4588 err = IE_ERROR;
4589 goto leave;
4591 /* Append it */
4592 node = xmlAddChild(file, xmlcontent);
4593 if (!node) {
4594 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4595 isds_printf_message(context,
4596 _("Could not add dmXMLContent child to %s element"),
4597 file->name);
4598 err = IE_ERROR;
4599 goto leave;
4602 /* Copy non-empty node list */
4603 if (document->xml_node_list) {
4604 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4605 document->xml_node_list);
4606 if (!content) {
4607 isds_printf_message(context,
4608 _("Not enough memory to copy XML document"));
4609 err = IE_NOMEM;
4610 goto leave;
4613 if (!xmlAddChildList(node, content)) {
4614 xmlFreeNodeList(content);
4615 isds_printf_message(context,
4616 _("Error while adding XML document into dmXMLContent"));
4617 err = IE_XML;
4618 goto leave;
4620 /* XXX: We cannot free the content here because it's part of node's
4621 * document since now. It will be freed with it automatically. */
4623 } else {
4624 /* Binary document requested */
4625 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4626 document->data, document->data_length);
4627 if (err) goto leave;
4630 leave:
4631 return err;
4635 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4636 * The copy must be preallocated, the date are just appended into structure.
4637 * @context is ISDS context
4638 * @copy is message copy structure
4639 * @xpath_ctx is XPath context with current node as tMStatus */
4640 static isds_error append_TMStatus(struct isds_ctx *context,
4641 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4642 isds_error err = IE_SUCCESS;
4643 xmlXPathObjectPtr result = NULL;
4644 char *code = NULL, *message = NULL;
4646 if (!context) return IE_INVALID_CONTEXT;
4647 if (!copy || !xpath_ctx) return IE_INVAL;
4649 /* Free old values */
4650 zfree(copy->dmStatus);
4651 zfree(copy->dmID);
4653 /* Get error specific to this copy */
4654 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4655 if (!code) {
4656 isds_log_message(context,
4657 _("Missing isds:dmStatusCode under "
4658 "XSD:tMStatus type element"));
4659 err = IE_ISDS;
4660 goto leave;
4663 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4664 /* This copy failed */
4665 copy->error = IE_ISDS;
4666 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4667 if (message) {
4668 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4669 if (!copy->dmStatus) {
4670 copy->dmStatus = code;
4671 code = NULL;
4673 } else {
4674 copy->dmStatus = code;
4675 code = NULL;
4677 } else {
4678 /* This copy succeeded. In this case only, message ID is valid */
4679 copy->error = IE_SUCCESS;
4681 EXTRACT_STRING("isds:dmID", copy->dmID);
4682 if (!copy->dmID) {
4683 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4684 "but did not returned assigned message ID\n"));
4685 err = IE_ISDS;
4689 leave:
4690 free(code);
4691 free(message);
4692 xmlXPathFreeObject(result);
4693 return err;
4697 /* Insert struct isds_approval data (box approval) into XML tree
4698 * @context is session context
4699 * @approval is libisds structure with approval description. NULL is
4700 * acceptable.
4701 * @parent is XML element to append @approval to */
4702 static isds_error insert_GExtApproval(struct isds_ctx *context,
4703 const struct isds_approval *approval, xmlNodePtr parent) {
4705 isds_error err = IE_SUCCESS;
4706 xmlNodePtr node;
4708 if (!context) return IE_INVALID_CONTEXT;
4709 if (!parent) return IE_INVAL;
4711 if (!approval) return IE_SUCCESS;
4713 /* Build XSD:gExtApproval */
4714 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4715 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4717 leave:
4718 return err;
4722 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4723 * code
4724 * @context is session context
4725 * @service_name is name of SERVICE_DB_ACCESS
4726 * @response is reallocated server SOAP body response as XML document
4727 * @raw_response is reallocated bit stream with response body. Use
4728 * NULL if you don't care
4729 * @raw_response_length is size of @raw_response in bytes
4730 * @code is reallocated ISDS status code
4731 * @status_message is reallocated ISDS status message
4732 * @return error coded from lower layer, context message will be set up
4733 * appropriately. */
4734 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4735 const xmlChar *service_name,
4736 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4737 xmlChar **code, xmlChar **status_message) {
4739 isds_error err = IE_SUCCESS;
4740 char *service_name_locale = NULL;
4741 xmlNodePtr request = NULL, node;
4742 xmlNsPtr isds_ns = NULL;
4744 if (!context) return IE_INVALID_CONTEXT;
4745 if (!service_name) return IE_INVAL;
4746 if (!response || !code || !status_message) return IE_INVAL;
4747 if (!raw_response_length && raw_response) return IE_INVAL;
4749 /* Free output argument */
4750 xmlFreeDoc(*response); *response = NULL;
4751 if (raw_response) zfree(*raw_response);
4752 zfree(*code);
4753 zfree(*status_message);
4756 /* Check if connection is established
4757 * TODO: This check should be done downstairs. */
4758 if (!context->curl) return IE_CONNECTION_CLOSED;
4760 service_name_locale = _isds_utf82locale((char*)service_name);
4761 if (!service_name_locale) {
4762 err = IE_NOMEM;
4763 goto leave;
4766 /* Build request */
4767 request = xmlNewNode(NULL, service_name);
4768 if (!request) {
4769 isds_printf_message(context,
4770 _("Could not build %s request"), service_name_locale);
4771 err = IE_ERROR;
4772 goto leave;
4774 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4775 if(!isds_ns) {
4776 isds_log_message(context, _("Could not create ISDS name space"));
4777 err = IE_ERROR;
4778 goto leave;
4780 xmlSetNs(request, isds_ns);
4783 /* Add XSD:tDummyInput child */
4784 INSERT_STRING(request, "dbDummy", NULL);
4787 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4788 service_name_locale);
4790 /* Send request */
4791 err = isds(context, SERVICE_DB_ACCESS, request, response,
4792 raw_response, raw_response_length);
4793 xmlFreeNode(request); request = NULL;
4795 if (err) {
4796 isds_log(ILF_ISDS, ILL_DEBUG,
4797 _("Processing ISDS response on %s request failed\n"),
4798 service_name_locale);
4799 goto leave;
4802 /* Check for response status */
4803 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4804 code, status_message, NULL);
4805 if (err) {
4806 isds_log(ILF_ISDS, ILL_DEBUG,
4807 _("ISDS response on %s request is missing status\n"),
4808 service_name_locale);
4809 goto leave;
4812 /* Request processed, but nothing found */
4813 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4814 char *code_locale = _isds_utf82locale((char*) *code);
4815 char *status_message_locale =
4816 _isds_utf82locale((char*) *status_message);
4817 isds_log(ILF_ISDS, ILL_DEBUG,
4818 _("Server refused %s request (code=%s, message=%s)\n"),
4819 service_name_locale, code_locale, status_message_locale);
4820 isds_log_message(context, status_message_locale);
4821 free(code_locale);
4822 free(status_message_locale);
4823 err = IE_ISDS;
4824 goto leave;
4827 leave:
4828 free(service_name_locale);
4829 xmlFreeNode(request);
4830 return err;
4832 #endif
4835 /* Get data about logged in user and his box. */
4836 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4837 struct isds_DbOwnerInfo **db_owner_info) {
4838 isds_error err = IE_SUCCESS;
4839 #if HAVE_LIBCURL
4840 xmlDocPtr response = NULL;
4841 xmlChar *code = NULL, *message = NULL;
4842 xmlXPathContextPtr xpath_ctx = NULL;
4843 xmlXPathObjectPtr result = NULL;
4844 char *string = NULL;
4845 #endif
4847 if (!context) return IE_INVALID_CONTEXT;
4848 zfree(context->long_message);
4849 if (!db_owner_info) return IE_INVAL;
4850 isds_DbOwnerInfo_free(db_owner_info);
4852 #if HAVE_LIBCURL
4853 /* Check if connection is established */
4854 if (!context->curl) return IE_CONNECTION_CLOSED;
4857 /* Do request and check for success */
4858 err = build_send_check_dbdummy_request(context,
4859 BAD_CAST "GetOwnerInfoFromLogin",
4860 &response, NULL, NULL, &code, &message);
4861 if (err) goto leave;
4864 /* Extract data */
4865 /* Prepare structure */
4866 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4867 if (!*db_owner_info) {
4868 err = IE_NOMEM;
4869 goto leave;
4871 xpath_ctx = xmlXPathNewContext(response);
4872 if (!xpath_ctx) {
4873 err = IE_ERROR;
4874 goto leave;
4876 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4877 err = IE_ERROR;
4878 goto leave;
4881 /* Set context node */
4882 result = xmlXPathEvalExpression(BAD_CAST
4883 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4884 if (!result) {
4885 err = IE_ERROR;
4886 goto leave;
4888 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4889 isds_log_message(context, _("Missing dbOwnerInfo element"));
4890 err = IE_ISDS;
4891 goto leave;
4893 if (result->nodesetval->nodeNr > 1) {
4894 isds_log_message(context, _("Multiple dbOwnerInfo element"));
4895 err = IE_ISDS;
4896 goto leave;
4898 xpath_ctx->node = result->nodesetval->nodeTab[0];
4899 xmlXPathFreeObject(result); result = NULL;
4901 /* Extract it */
4902 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
4905 leave:
4906 if (err) {
4907 isds_DbOwnerInfo_free(db_owner_info);
4910 free(string);
4911 xmlXPathFreeObject(result);
4912 xmlXPathFreeContext(xpath_ctx);
4914 free(code);
4915 free(message);
4916 xmlFreeDoc(response);
4918 if (!err)
4919 isds_log(ILF_ISDS, ILL_DEBUG,
4920 _("GetOwnerInfoFromLogin request processed by server "
4921 "successfully.\n"));
4922 #else /* not HAVE_LIBCURL */
4923 err = IE_NOTSUP;
4924 #endif
4926 return err;
4930 /* Get data about logged in user. */
4931 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
4932 struct isds_DbUserInfo **db_user_info) {
4933 isds_error err = IE_SUCCESS;
4934 #if HAVE_LIBCURL
4935 xmlDocPtr response = NULL;
4936 xmlChar *code = NULL, *message = NULL;
4937 xmlXPathContextPtr xpath_ctx = NULL;
4938 xmlXPathObjectPtr result = NULL;
4939 #endif
4941 if (!context) return IE_INVALID_CONTEXT;
4942 zfree(context->long_message);
4943 if (!db_user_info) return IE_INVAL;
4944 isds_DbUserInfo_free(db_user_info);
4946 #if HAVE_LIBCURL
4947 /* Check if connection is established */
4948 if (!context->curl) return IE_CONNECTION_CLOSED;
4951 /* Do request and check for success */
4952 err = build_send_check_dbdummy_request(context,
4953 BAD_CAST "GetUserInfoFromLogin",
4954 &response, NULL, NULL, &code, &message);
4955 if (err) goto leave;
4958 /* Extract data */
4959 /* Prepare structure */
4960 *db_user_info = calloc(1, sizeof(**db_user_info));
4961 if (!*db_user_info) {
4962 err = IE_NOMEM;
4963 goto leave;
4965 xpath_ctx = xmlXPathNewContext(response);
4966 if (!xpath_ctx) {
4967 err = IE_ERROR;
4968 goto leave;
4970 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4971 err = IE_ERROR;
4972 goto leave;
4975 /* Set context node */
4976 result = xmlXPathEvalExpression(BAD_CAST
4977 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
4978 if (!result) {
4979 err = IE_ERROR;
4980 goto leave;
4982 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4983 isds_log_message(context, _("Missing dbUserInfo element"));
4984 err = IE_ISDS;
4985 goto leave;
4987 if (result->nodesetval->nodeNr > 1) {
4988 isds_log_message(context, _("Multiple dbUserInfo element"));
4989 err = IE_ISDS;
4990 goto leave;
4992 xpath_ctx->node = result->nodesetval->nodeTab[0];
4993 xmlXPathFreeObject(result); result = NULL;
4995 /* Extract it */
4996 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
4998 leave:
4999 if (err) {
5000 isds_DbUserInfo_free(db_user_info);
5003 xmlXPathFreeObject(result);
5004 xmlXPathFreeContext(xpath_ctx);
5006 free(code);
5007 free(message);
5008 xmlFreeDoc(response);
5010 if (!err)
5011 isds_log(ILF_ISDS, ILL_DEBUG,
5012 _("GetUserInfoFromLogin request processed by server "
5013 "successfully.\n"));
5014 #else /* not HAVE_LIBCURL */
5015 err = IE_NOTSUP;
5016 #endif
5018 return err;
5022 /* Get expiration time of current password
5023 * @context is session context
5024 * @expiration is automatically reallocated time when password expires. If
5025 * password expiration is disables, NULL will be returned. In case of error
5026 * it will be nulled too. */
5027 isds_error isds_get_password_expiration(struct isds_ctx *context,
5028 struct timeval **expiration) {
5029 isds_error err = IE_SUCCESS;
5030 #if HAVE_LIBCURL
5031 xmlDocPtr response = NULL;
5032 xmlChar *code = NULL, *message = NULL;
5033 xmlXPathContextPtr xpath_ctx = NULL;
5034 xmlXPathObjectPtr result = NULL;
5035 char *string = NULL;
5036 #endif
5038 if (!context) return IE_INVALID_CONTEXT;
5039 zfree(context->long_message);
5040 if (!expiration) return IE_INVAL;
5041 zfree(*expiration);
5043 #if HAVE_LIBCURL
5044 /* Check if connection is established */
5045 if (!context->curl) return IE_CONNECTION_CLOSED;
5048 /* Do request and check for success */
5049 err = build_send_check_dbdummy_request(context,
5050 BAD_CAST "GetPasswordInfo",
5051 &response, NULL, NULL, &code, &message);
5052 if (err) goto leave;
5055 /* Extract data */
5056 xpath_ctx = xmlXPathNewContext(response);
5057 if (!xpath_ctx) {
5058 err = IE_ERROR;
5059 goto leave;
5061 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5062 err = IE_ERROR;
5063 goto leave;
5066 /* Set context node */
5067 result = xmlXPathEvalExpression(BAD_CAST
5068 "/isds:GetPasswordInfoResponse", xpath_ctx);
5069 if (!result) {
5070 err = IE_ERROR;
5071 goto leave;
5073 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5074 isds_log_message(context,
5075 _("Missing GetPasswordInfoResponse element"));
5076 err = IE_ISDS;
5077 goto leave;
5079 if (result->nodesetval->nodeNr > 1) {
5080 isds_log_message(context,
5081 _("Multiple GetPasswordInfoResponse element"));
5082 err = IE_ISDS;
5083 goto leave;
5085 xpath_ctx->node = result->nodesetval->nodeTab[0];
5086 xmlXPathFreeObject(result); result = NULL;
5088 /* Extract expiration date */
5089 EXTRACT_STRING("isds:pswExpDate", string);
5090 if (string) {
5091 /* And convert it if any returned. Otherwise expiration is disabled. */
5092 err = timestring2timeval((xmlChar *) string, expiration);
5093 if (err) {
5094 char *string_locale = _isds_utf82locale(string);
5095 if (err == IE_DATE) err = IE_ISDS;
5096 isds_printf_message(context,
5097 _("Could not convert pswExpDate as ISO time: %s"),
5098 string_locale);
5099 free(string_locale);
5100 goto leave;
5104 leave:
5105 if (err) {
5106 if (*expiration) {
5107 zfree(*expiration);
5111 free(string);
5112 xmlXPathFreeObject(result);
5113 xmlXPathFreeContext(xpath_ctx);
5115 free(code);
5116 free(message);
5117 xmlFreeDoc(response);
5119 if (!err)
5120 isds_log(ILF_ISDS, ILL_DEBUG,
5121 _("GetPasswordInfo request processed by server "
5122 "successfully.\n"));
5123 #else /* not HAVE_LIBCURL */
5124 err = IE_NOTSUP;
5125 #endif
5127 return err;
5131 #if HAVE_LIBCURL
5132 /* Request delivering new TOTP code from ISDS through side channel before
5133 * changing password.
5134 * @context is session context
5135 * @password is current password.
5136 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5137 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5138 * function for more details.
5139 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5140 * NULL, if you don't care.
5141 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5142 * error code. */
5143 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5144 const char *password, struct isds_otp *otp, char **refnumber) {
5145 isds_error err = IE_SUCCESS;
5146 char *saved_url = NULL; /* No copy */
5147 #if HAVE_CURL_REAUTHORIZATION_BUG
5148 CURL *saved_curl = NULL; /* No copy */
5149 #endif
5150 xmlNsPtr isds_ns = NULL;
5151 xmlNodePtr request = NULL;
5152 xmlDocPtr response = NULL;
5153 xmlChar *code = NULL, *message = NULL;
5154 const xmlChar *codes[] = {
5155 BAD_CAST "2300",
5156 BAD_CAST "2301",
5157 BAD_CAST "2302"
5159 const char *meanings[] = {
5160 N_("Unexpected error"),
5161 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5162 N_("One-time code could not been sent. Try later again.")
5164 const isds_otp_resolution resolutions[] = {
5165 OTP_RESOLUTION_UNKNOWN,
5166 OTP_RESOLUTION_TO_FAST,
5167 OTP_RESOLUTION_TOTP_NOT_SENT
5170 if (NULL == context) return IE_INVALID_CONTEXT;
5171 zfree(context->long_message);
5172 if (NULL == password) {
5173 isds_log_message(context,
5174 _("Second argument (password) of isds_change_password() "
5175 "is NULL"));
5176 return IE_INVAL;
5179 /* Check if connection is established
5180 * TODO: This check should be done downstairs. */
5181 if (!context->curl) return IE_CONNECTION_CLOSED;
5183 if (!context->otp) {
5184 isds_log_message(context, _("This function requires OTP-authenticated "
5185 "context"));
5186 return IE_INVALID_CONTEXT;
5188 if (NULL == otp) {
5189 isds_log_message(context, _("If one-time password authentication "
5190 "method is in use, requesting new OTP code requires "
5191 "one-time credentials argument either"));
5192 return IE_INVAL;
5194 if (otp->method != OTP_TIME) {
5195 isds_log_message(context, _("Requesting new time-based OTP code from "
5196 "server requires one-time password authentication "
5197 "method"));
5198 return IE_INVAL;
5200 if (otp->otp_code != NULL) {
5201 isds_log_message(context, _("Requesting new time-based OTP code from "
5202 "server requires undefined OTP code member in "
5203 "one-time credentials argument"));
5204 return IE_INVAL;
5208 /* Build request */
5209 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5210 if (!request) {
5211 isds_log_message(context, _("Could not build SendSMSCode request"));
5212 return IE_ERROR;
5214 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5215 if(!isds_ns) {
5216 isds_log_message(context, _("Could not create ISDS name space"));
5217 xmlFreeNode(request);
5218 return IE_ERROR;
5220 xmlSetNs(request, isds_ns);
5222 /* Change URL temporarily for sending this request only */
5224 char *new_url = NULL;
5225 if ((err = _isds_build_url_from_context(context,
5226 "%1$.*2$sasws/changePassword", &new_url))) {
5227 goto leave;
5229 saved_url = context->url;
5230 context->url = new_url;
5233 /* Store credentials for sending this request only */
5234 context->otp_credentials = otp;
5235 _isds_discard_credentials(context, 0);
5236 if ((err = _isds_store_credentials(context, context->saved_username,
5237 password, NULL))) {
5238 _isds_discard_credentials(context, 0);
5239 goto leave;
5241 #if HAVE_CURL_REAUTHORIZATION_BUG
5242 saved_curl = context->curl;
5243 context->curl = curl_easy_init();
5244 if (NULL == context->curl) {
5245 err = IE_ERROR;
5246 goto leave;
5248 if (context->timeout) {
5249 err = isds_set_timeout(context, context->timeout);
5250 if (err) goto leave;
5252 #endif
5254 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5256 /* Sent request */
5257 err = isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5259 /* Remove temporal credentials */
5260 _isds_discard_credentials(context, 0);
5261 /* Detach pointer to OTP credentials from context */
5262 context->otp_credentials = NULL;
5263 /* Keep context->otp true to keep signaling this is OTP session */
5265 /* Destroy request */
5266 xmlFreeNode(request); request = NULL;
5268 if (err) {
5269 isds_log(ILF_ISDS, ILL_DEBUG,
5270 _("Processing ISDS response on SendSMSCode request failed\n"));
5271 goto leave;
5274 /* Check for response status */
5275 err = isds_response_status(context, SERVICE_ASWS, response,
5276 &code, &message, (xmlChar **)refnumber);
5277 if (err) {
5278 isds_log(ILF_ISDS, ILL_DEBUG,
5279 _("ISDS response on SendSMSCode request is missing "
5280 "status\n"));
5281 goto leave;
5284 /* Check for error */
5285 if (xmlStrcmp(code, BAD_CAST "0000")) {
5286 char *code_locale = _isds_utf82locale((char*)code);
5287 char *message_locale = _isds_utf82locale((char*)message);
5288 int i;
5289 isds_log(ILF_ISDS, ILL_DEBUG,
5290 _("Server refused to send new code on SendSMSCode "
5291 "request (code=%s, message=%s)\n"),
5292 code_locale, message_locale);
5294 /* Check for known error codes */
5295 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5296 if (!xmlStrcmp(code, codes[i])) break;
5298 if (i < sizeof(codes)/sizeof(*codes)) {
5299 isds_log_message(context, _(meanings[i]));
5300 /* Mimic otp->resolution according to the code, specification does
5301 * prescribe OTP header to be available. */
5302 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5303 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5304 otp->resolution = resolutions[i];
5305 } else
5306 isds_log_message(context, message_locale);
5308 free(code_locale);
5309 free(message_locale);
5311 err = IE_ISDS;
5312 goto leave;
5315 /* Otherwise new code sent successfully */
5316 /* Mimic otp->resolution according to the code, specification does
5317 * prescribe OTP header to be available. */
5318 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5319 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5321 leave:
5322 if (NULL != saved_url) {
5323 /* Revert URL to original one */
5324 zfree(context->url);
5325 context->url = saved_url;
5327 #if HAVE_CURL_REAUTHORIZATION_BUG
5328 if (NULL != saved_curl) {
5329 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5330 context->curl = saved_curl;
5332 #endif
5334 free(code);
5335 free(message);
5336 xmlFreeDoc(response);
5337 xmlFreeNode(request);
5339 if (!err)
5340 isds_log(ILF_ISDS, ILL_DEBUG,
5341 _("New OTP code has been sent successfully on SendSMSCode "
5342 "request.\n"));
5343 return err;
5347 /* Convert response status code to isds_error code and set long message
5348 * @context is context to save long message to
5349 * @map is mapping from codes to errors and messages. Pass NULL for generic
5350 * handling.
5351 * @code is status code to translate
5352 * @message is non-localized status message to put into long message in case
5353 * of uknown error. It can be NULL if server did not provide any.
5354 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5355 * invalid invocation. */
5356 static isds_error statuscode2isds_error(struct isds_ctx *context,
5357 const struct code_map_isds_error *map,
5358 const xmlChar *code, const xmlChar *message) {
5359 if (NULL == code) {
5360 isds_log_message(context,
5361 _("NULL status code passed to statuscode2isds_error()"));
5362 return IE_INVAL;
5365 if (NULL != map) {
5366 /* Check for known error codes */
5367 for (int i=0; map->codes[i] != NULL; i++) {
5368 if (!xmlStrcmp(code, map->codes[i])) {
5369 isds_log_message(context, _(map->meanings[i]));
5370 return map->errors[i];
5375 /* Other error */
5376 if (xmlStrcmp(code, BAD_CAST "0000")) {
5377 char *message_locale = _isds_utf82locale((char*)message);
5378 if (NULL == message_locale)
5379 isds_log_message(context, _("ISDS server returned unknown error"));
5380 else
5381 isds_log_message(context, message_locale);
5382 free(message_locale);
5383 return IE_ISDS;
5386 return IE_SUCCESS;
5388 #endif
5391 /* Change user password in ISDS.
5392 * User must supply old password, new password will takes effect after some
5393 * time, current session can continue. Password must fulfill some constraints.
5394 * @context is session context
5395 * @old_password is current password.
5396 * @new_password is requested new password
5397 * @otp auxiliary data required if one-time password authentication is in use,
5398 * defines OTP code (if known) and returns fine grade resolution of OTP
5399 * procedure. Pass NULL, if one-time password authentication is not needed.
5400 * Please note the @otp argument must match OTP method used at log-in time. See
5401 * isds_login() function for more details.
5402 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5403 * NULL, if you don't care.
5404 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5405 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5406 * awaiting OTP code that has been delivered by side channel to the user. */
5407 isds_error isds_change_password(struct isds_ctx *context,
5408 const char *old_password, const char *new_password,
5409 struct isds_otp *otp, char **refnumber) {
5410 isds_error err = IE_SUCCESS;
5411 #if HAVE_LIBCURL
5412 char *saved_url = NULL; /* No copy */
5413 #if HAVE_CURL_REAUTHORIZATION_BUG
5414 CURL *saved_curl = NULL; /* No copy */
5415 #endif
5416 xmlNsPtr isds_ns = NULL;
5417 xmlNodePtr request = NULL, node;
5418 xmlDocPtr response = NULL;
5419 xmlChar *code = NULL, *message = NULL;
5420 const xmlChar *codes[] = {
5421 BAD_CAST "1066",
5422 BAD_CAST "1067",
5423 BAD_CAST "1079",
5424 BAD_CAST "1080",
5425 BAD_CAST "1081",
5426 BAD_CAST "1082",
5427 BAD_CAST "1083",
5428 BAD_CAST "1090",
5429 BAD_CAST "1091",
5430 BAD_CAST "2300",
5431 BAD_CAST "9204"
5433 const char *meanings[] = {
5434 N_("Password length must be between 8 and 32 characters"),
5435 N_("Password cannot be reused"), /* Server does not distinguish 1067
5436 and 1091 on ChangePasswordOTP */
5437 N_("Password contains forbidden character"),
5438 N_("Password must contain at least one upper-case letter, "
5439 "one lower-case, and one digit"),
5440 N_("Password cannot contain sequence of three identical characters"),
5441 N_("Password cannot contain user identifier"),
5442 N_("Password is too simmple"),
5443 N_("Old password is not valid"),
5444 N_("Password cannot be reused"),
5445 N_("Unexpected error"),
5446 N_("LDAP update error")
5448 #endif
5450 if (!context) return IE_INVALID_CONTEXT;
5451 zfree(context->long_message);
5452 if (NULL != refnumber)
5453 zfree(*refnumber);
5454 if (NULL == old_password) {
5455 isds_log_message(context,
5456 _("Second argument (old password) of isds_change_password() "
5457 "is NULL"));
5458 return IE_INVAL;
5460 if (NULL == otp && NULL == new_password) {
5461 isds_log_message(context,
5462 _("Third argument (new password) of isds_change_password() "
5463 "is NULL"));
5464 return IE_INVAL;
5467 #if HAVE_LIBCURL
5468 /* Check if connection is established
5469 * TODO: This check should be done downstairs. */
5470 if (!context->curl) return IE_CONNECTION_CLOSED;
5472 if (context->otp && NULL == otp) {
5473 isds_log_message(context, _("If one-time password authentication "
5474 "method is in use, changing password requires one-time "
5475 "credentials either"));
5476 return IE_INVAL;
5479 /* Build ChangeISDSPassword request */
5480 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5481 BAD_CAST "ChangePasswordOTP");
5482 if (!request) {
5483 isds_log_message(context, (NULL == otp) ?
5484 _("Could not build ChangeISDSPassword request") :
5485 _("Could not build ChangePasswordOTP request"));
5486 return IE_ERROR;
5488 isds_ns = xmlNewNs(request,
5489 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5490 NULL);
5491 if(!isds_ns) {
5492 isds_log_message(context, _("Could not create ISDS name space"));
5493 xmlFreeNode(request);
5494 return IE_ERROR;
5496 xmlSetNs(request, isds_ns);
5498 INSERT_STRING(request, "dbOldPassword", old_password);
5499 INSERT_STRING(request, "dbNewPassword", new_password);
5501 if (NULL != otp) {
5502 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5503 switch (otp->method) {
5504 case OTP_HMAC:
5505 isds_log(ILF_SEC, ILL_INFO,
5506 _("Selected authentication method: "
5507 "HMAC-based one-time password\n"));
5508 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5509 break;
5510 case OTP_TIME:
5511 isds_log(ILF_SEC, ILL_INFO,
5512 _("Selected authentication method: "
5513 "Time-based one-time password\n"));
5514 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5515 if (otp->otp_code == NULL) {
5516 isds_log(ILF_SEC, ILL_INFO,
5517 _("OTP code has not been provided by "
5518 "application, requesting server for "
5519 "new one.\n"));
5520 err = _isds_request_totp_code(context, old_password, otp,
5521 refnumber);
5522 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5523 goto leave;
5525 } else {
5526 isds_log(ILF_SEC, ILL_INFO,
5527 _("OTP code has been provided by "
5528 "application, not requesting server "
5529 "for new one.\n"));
5531 break;
5532 default:
5533 isds_log_message(context,
5534 _("Unknown one-time password authentication "
5535 "method requested by application"));
5536 err = IE_ENUM;
5537 goto leave;
5540 /* Change URL temporarily for sending this request only */
5542 char *new_url = NULL;
5543 if ((err = _isds_build_url_from_context(context,
5544 "%1$.*2$sasws/changePassword", &new_url))) {
5545 goto leave;
5547 saved_url = context->url;
5548 context->url = new_url;
5551 /* Store credentials for sending this request only */
5552 context->otp_credentials = otp;
5553 _isds_discard_credentials(context, 0);
5554 if ((err = _isds_store_credentials(context, context->saved_username,
5555 old_password, NULL))) {
5556 _isds_discard_credentials(context, 0);
5557 goto leave;
5559 #if HAVE_CURL_REAUTHORIZATION_BUG
5560 saved_curl = context->curl;
5561 context->curl = curl_easy_init();
5562 if (NULL == context->curl) {
5563 err = IE_ERROR;
5564 goto leave;
5566 if (context->timeout) {
5567 err = isds_set_timeout(context, context->timeout);
5568 if (err) goto leave;
5570 #endif
5573 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5574 _("Sending ChangeISDSPassword request to ISDS\n") :
5575 _("Sending ChangePasswordOTP request to ISDS\n"));
5577 /* Sent request */
5578 err = isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5579 request, &response, NULL, NULL);
5581 if (otp) {
5582 /* Remove temporal credentials */
5583 _isds_discard_credentials(context, 0);
5584 /* Detach pointer to OTP credentials from context */
5585 context->otp_credentials = NULL;
5586 /* Keep context->otp true to keep signaling this is OTP session */
5589 /* Destroy request */
5590 xmlFreeNode(request); request = NULL;
5592 if (err) {
5593 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5594 _("Processing ISDS response on ChangeISDSPassword "
5595 "request failed\n") :
5596 _("Processing ISDS response on ChangePasswordOTP "
5597 "request failed\n"));
5598 goto leave;
5601 /* Check for response status */
5602 err = isds_response_status(context,
5603 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5604 &code, &message, (xmlChar **)refnumber);
5605 if (err) {
5606 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5607 _("ISDS response on ChangeISDSPassword request is missing "
5608 "status\n") :
5609 _("ISDS response on ChangePasswordOTP request is missing "
5610 "status\n"));
5611 goto leave;
5614 /* Check for known error codes */
5615 for (int i=0; i < sizeof(codes)/sizeof(*codes); i++) {
5616 if (!xmlStrcmp(code, codes[i])) {
5617 char *code_locale = _isds_utf82locale((char*)code);
5618 char *message_locale = _isds_utf82locale((char*)message);
5619 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5620 _("Server refused to change password on ChangeISDSPassword "
5621 "request (code=%s, message=%s)\n") :
5622 _("Server refused to change password on ChangePasswordOTP "
5623 "request (code=%s, message=%s)\n"),
5624 code_locale, message_locale);
5625 free(code_locale);
5626 free(message_locale);
5627 isds_log_message(context, _(meanings[i]));
5628 err = IE_INVAL;
5629 goto leave;
5633 /* Other error */
5634 if (xmlStrcmp(code, BAD_CAST "0000")) {
5635 char *code_locale = _isds_utf82locale((char*)code);
5636 char *message_locale = _isds_utf82locale((char*)message);
5637 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5638 _("Server refused to change password on ChangeISDSPassword "
5639 "request (code=%s, message=%s)\n") :
5640 _("Server refused to change password on ChangePasswordOTP "
5641 "request (code=%s, message=%s)\n"),
5642 code_locale, message_locale);
5643 isds_log_message(context, message_locale);
5644 free(code_locale);
5645 free(message_locale);
5646 err = IE_ISDS;
5647 goto leave;
5650 /* Otherwise password changed successfully */
5652 leave:
5653 if (NULL != saved_url) {
5654 /* Revert URL to original one */
5655 zfree(context->url);
5656 context->url = saved_url;
5658 #if HAVE_CURL_REAUTHORIZATION_BUG
5659 if (NULL != saved_curl) {
5660 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5661 context->curl = saved_curl;
5663 #endif
5665 free(code);
5666 free(message);
5667 xmlFreeDoc(response);
5668 xmlFreeNode(request);
5670 if (!err)
5671 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5672 _("Password changed successfully on ChangeISDSPassword "
5673 "request.\n") :
5674 _("Password changed successfully on ChangePasswordOTP "
5675 "request.\n"));
5676 #else /* not HAVE_LIBCURL */
5677 err = IE_NOTSUP;
5678 #endif
5680 return err;
5684 #if HAVE_LIBCURL
5685 /* Generic middle part with request sending and response check.
5686 * It sends prepared request and checks for error code.
5687 * @context is ISDS session context.
5688 * @service is ISDS service handler
5689 * @service_name is name in scope of given @service
5690 * @request is XML tree with request. Will be freed to save memory.
5691 * @response is XML document outputting ISDS response.
5692 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5693 * @map is mapping from status code to library error. Pass NULL if no special
5694 * handling is requested.
5695 * NULL, if you don't care. */
5696 static isds_error send_destroy_request_check_response(
5697 struct isds_ctx *context,
5698 const isds_service service, const xmlChar *service_name,
5699 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5700 const struct code_map_isds_error *map) {
5701 isds_error err = IE_SUCCESS;
5702 char *service_name_locale = NULL;
5703 xmlChar *code = NULL, *message = NULL;
5706 if (!context) return IE_INVALID_CONTEXT;
5707 if (!service_name || *service_name == '\0' || !request || !*request ||
5708 !response)
5709 return IE_INVAL;
5711 /* Check if connection is established
5712 * TODO: This check should be done downstairs. */
5713 if (!context->curl) return IE_CONNECTION_CLOSED;
5715 service_name_locale = _isds_utf82locale((char*) service_name);
5716 if (!service_name_locale) {
5717 err = IE_NOMEM;
5718 goto leave;
5721 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5722 service_name_locale);
5724 /* Send request */
5725 err = isds(context, service, *request, response, NULL, NULL);
5726 xmlFreeNode(*request); *request = NULL;
5728 if (err) {
5729 isds_log(ILF_ISDS, ILL_DEBUG,
5730 _("Processing ISDS response on %s request failed\n"),
5731 service_name_locale);
5732 goto leave;
5735 /* Check for response status */
5736 err = isds_response_status(context, service, *response,
5737 &code, &message, refnumber);
5738 if (err) {
5739 isds_log(ILF_ISDS, ILL_DEBUG,
5740 _("ISDS response on %s request is missing status\n"),
5741 service_name_locale);
5742 goto leave;
5745 err = statuscode2isds_error(context, map, code, message);
5747 /* Request processed, but server failed */
5748 if (xmlStrcmp(code, BAD_CAST "0000")) {
5749 char *code_locale = _isds_utf82locale((char*) code);
5750 char *message_locale = _isds_utf82locale((char*) message);
5751 isds_log(ILF_ISDS, ILL_DEBUG,
5752 _("Server refused %s request (code=%s, message=%s)\n"),
5753 service_name_locale, code_locale, message_locale);
5754 free(code_locale);
5755 free(message_locale);
5756 goto leave;
5760 leave:
5761 free(code);
5762 free(message);
5763 if (err && *response) {
5764 xmlFreeDoc(*response);
5765 *response = NULL;
5767 if (*request) {
5768 xmlFreeNode(*request);
5769 *request = NULL;
5771 free(service_name_locale);
5773 return err;
5777 /* Generic bottom half with request sending.
5778 * It sends prepared request, checks for error code, destroys response and
5779 * request and log success or failure.
5780 * @context is ISDS session context.
5781 * @service is ISDS service handler
5782 * @service_name is name in scope of given @service
5783 * @request is XML tree with request. Will be freed to save memory.
5784 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5785 * NULL, if you don't care. */
5786 static isds_error send_request_check_drop_response(
5787 struct isds_ctx *context,
5788 const isds_service service, const xmlChar *service_name,
5789 xmlNodePtr *request, xmlChar **refnumber) {
5790 isds_error err = IE_SUCCESS;
5791 xmlDocPtr response = NULL;
5794 if (!context) return IE_INVALID_CONTEXT;
5795 if (!service_name || *service_name == '\0' || !request || !*request)
5796 return IE_INVAL;
5798 /* Send request and check response*/
5799 err = send_destroy_request_check_response(context,
5800 service, service_name, request, &response, refnumber, NULL);
5802 xmlFreeDoc(response);
5804 if (*request) {
5805 xmlFreeNode(*request);
5806 *request = NULL;
5809 if (!err) {
5810 char *service_name_locale = _isds_utf82locale((char *) service_name);
5811 isds_log(ILF_ISDS, ILL_DEBUG,
5812 _("%s request processed by server successfully.\n"),
5813 service_name_locale);
5814 free(service_name_locale);
5817 return err;
5821 /* Insert isds_credentials_delivery structure into XML request if not NULL
5822 * @context is session context
5823 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5824 * credentials delivery. The email field is passed.
5825 * @parent is XML element where to insert */
5826 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5827 const struct isds_credentials_delivery *credentials_delivery,
5828 xmlNodePtr parent) {
5829 isds_error err = IE_SUCCESS;
5830 xmlNodePtr node;
5832 if (!context) return IE_INVALID_CONTEXT;
5833 if (!parent) return IE_INVAL;
5835 if (credentials_delivery) {
5836 /* Following elements are valid only for services:
5837 * NewAccessData, AddDataBoxUser, CreateDataBox */
5838 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5839 INSERT_STRING(parent, "email", credentials_delivery->email);
5842 leave:
5843 return err;
5847 /* Extract credentials delivery from ISDS response.
5848 * @context is session context
5849 * @credentials_delivery is pointer to valid structure to fill in returned
5850 * user's password (and new log-in name). If NULL, do not extract the data.
5851 * @response is pointer to XML document with ISDS response
5852 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5853 * @return IE_SUCCESS even if new user name has not been found because it's not
5854 * clear whether it's returned always. */
5855 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5856 struct isds_credentials_delivery *credentials_delivery,
5857 xmlDocPtr response, const char *request_name) {
5858 isds_error err = IE_SUCCESS;
5859 xmlXPathContextPtr xpath_ctx = NULL;
5860 xmlXPathObjectPtr result = NULL;
5861 char *xpath_query = NULL;
5863 if (!context) return IE_INVALID_CONTEXT;
5864 if (credentials_delivery) {
5865 zfree(credentials_delivery->token);
5866 zfree(credentials_delivery->new_user_name);
5868 if (!response || !request_name || !*request_name) return IE_INVAL;
5871 /* Extract optional token */
5872 if (credentials_delivery) {
5873 xpath_ctx = xmlXPathNewContext(response);
5874 if (!xpath_ctx) {
5875 err = IE_ERROR;
5876 goto leave;
5878 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5879 err = IE_ERROR;
5880 goto leave;
5883 /* Verify root element */
5884 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
5885 request_name)) {
5886 err = IE_NOMEM;
5887 goto leave;
5889 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
5890 if (!result) {
5891 err = IE_ERROR;
5892 goto leave;
5894 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5895 char *request_name_locale = _isds_utf82locale(request_name);
5896 isds_log(ILF_ISDS, ILL_WARNING,
5897 _("Wrong element in ISDS response for %s request "
5898 "while extracting credentials delivery details\n"),
5899 request_name_locale);
5900 free(request_name_locale);
5901 err = IE_ERROR;
5902 goto leave;
5904 xpath_ctx->node = result->nodesetval->nodeTab[0];
5907 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5908 * optional. */
5909 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
5911 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
5912 if (!credentials_delivery->token) {
5913 char *request_name_locale = _isds_utf82locale(request_name);
5914 isds_log(ILF_ISDS, ILL_ERR,
5915 _("ISDS did not return token on %s request "
5916 "even if requested\n"), request_name_locale);
5917 free(request_name_locale);
5918 err = IE_ERROR;
5922 leave:
5923 free(xpath_query);
5924 xmlXPathFreeObject(result);
5925 xmlXPathFreeContext(xpath_ctx);
5927 return err;
5931 /* Build XSD:tCreateDBInput request type for box creating.
5932 * @context is session context
5933 * @request outputs built XML tree
5934 * @service_name is request name of SERVICE_DB_MANIPULATION service
5935 * @box is box description to create including single primary user (in case of
5936 * FO box type)
5937 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5938 * box, or contact address of PFO box owner)
5939 * @former_names is optional former name of box owner. Pass NULL if otherwise.
5940 * @upper_box_id is optional ID of supper box if currently created box is
5941 * subordinated.
5942 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
5943 * don't care.
5944 * @credentials_delivery is valid pointer if ISDS should return token that box
5945 * owner can use to obtain his new credentials in on-line way. Then valid email
5946 * member value should be supplied.
5947 * @approval is optional external approval of box manipulation */
5948 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
5949 xmlNodePtr *request, const xmlChar *service_name,
5950 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
5951 const xmlChar *former_names, const xmlChar *upper_box_id,
5952 const xmlChar *ceo_label,
5953 const struct isds_credentials_delivery *credentials_delivery,
5954 const struct isds_approval *approval) {
5955 isds_error err = IE_SUCCESS;
5956 xmlNsPtr isds_ns = NULL;
5957 xmlNodePtr node, dbPrimaryUsers;
5958 xmlChar *string = NULL;
5959 const struct isds_list *item;
5962 if (!context) return IE_INVALID_CONTEXT;
5963 if (!request || !service_name || service_name[0] == '\0' || !box)
5964 return IE_INVAL;
5967 /* Build CreateDataBox-similar request */
5968 *request = xmlNewNode(NULL, service_name);
5969 if (!*request) {
5970 char *service_name_locale = _isds_utf82locale((char*) service_name);
5971 isds_printf_message(context, _("Could build %s request"),
5972 service_name_locale);
5973 free(service_name_locale);
5974 return IE_ERROR;
5976 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
5977 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
5978 if (!isds_ns) {
5979 isds_log_message(context, _("Could not create ISDS1 name space"));
5980 xmlFreeNode(*request);
5981 return IE_ERROR;
5983 } else {
5984 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
5985 if (!isds_ns) {
5986 isds_log_message(context, _("Could not create ISDS name space"));
5987 xmlFreeNode(*request);
5988 return IE_ERROR;
5991 xmlSetNs(*request, isds_ns);
5993 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
5994 err = insert_DbOwnerInfo(context, box, node);
5995 if (err) goto leave;
5997 /* Insert users */
5998 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
5999 * verbose documentation allows none dbUserInfo */
6000 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6001 for (item = users; item; item = item->next) {
6002 if (item->data) {
6003 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6004 err = insert_DbUserInfo(context,
6005 (struct isds_DbUserInfo *) item->data, node);
6006 if (err) goto leave;
6010 INSERT_STRING(*request, "dbFormerNames", former_names);
6011 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6012 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6014 err = insert_credentials_delivery(context, credentials_delivery, *request);
6015 if (err) goto leave;
6017 err = insert_GExtApproval(context, approval, *request);
6018 if (err) goto leave;
6020 leave:
6021 if (err) {
6022 xmlFreeNode(*request);
6023 *request = NULL;
6025 free(string);
6026 return err;
6028 #endif /* HAVE_LIBCURL */
6031 /* Create new box.
6032 * @context is session context
6033 * @box is box description to create including single primary user (in case of
6034 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6035 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6036 * box, or contact address of PFO box owner)
6037 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6038 * @upper_box_id is optional ID of supper box if currently created box is
6039 * subordinated.
6040 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6041 * @credentials_delivery is NULL if new password should be delivered off-line
6042 * to box owner. It is valid pointer if owner should obtain new password on-line
6043 * on dedicated web server. Then input @credentials_delivery.email value is
6044 * his e-mail address he must provide to dedicated web server together
6045 * with output reallocated @credentials_delivery.token member. Output
6046 * member @credentials_delivery.new_user_name is unused up on this call.
6047 * @approval is optional external approval of box manipulation
6048 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6049 * NULL, if you don't care.*/
6050 isds_error isds_add_box(struct isds_ctx *context,
6051 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6052 const char *former_names, const char *upper_box_id,
6053 const char *ceo_label,
6054 struct isds_credentials_delivery *credentials_delivery,
6055 const struct isds_approval *approval, char **refnumber) {
6056 isds_error err = IE_SUCCESS;
6057 #if HAVE_LIBCURL
6058 xmlNodePtr request = NULL;
6059 xmlDocPtr response = NULL;
6060 xmlXPathContextPtr xpath_ctx = NULL;
6061 xmlXPathObjectPtr result = NULL;
6062 #endif
6065 if (!context) return IE_INVALID_CONTEXT;
6066 zfree(context->long_message);
6067 if (credentials_delivery) {
6068 zfree(credentials_delivery->token);
6069 zfree(credentials_delivery->new_user_name);
6071 if (!box) return IE_INVAL;
6073 #if HAVE_LIBCURL
6074 /* Scratch box ID */
6075 zfree(box->dbID);
6077 /* Build CreateDataBox request */
6078 err = build_CreateDBInput_request(context,
6079 &request, BAD_CAST "CreateDataBox",
6080 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6081 (xmlChar *) ceo_label, credentials_delivery, approval);
6082 if (err) goto leave;
6084 /* Send it to server and process response */
6085 err = send_destroy_request_check_response(context,
6086 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6087 &response, (xmlChar **) refnumber, NULL);
6089 /* Extract box ID */
6090 xpath_ctx = xmlXPathNewContext(response);
6091 if (!xpath_ctx) {
6092 err = IE_ERROR;
6093 goto leave;
6095 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6096 err = IE_ERROR;
6097 goto leave;
6099 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6101 /* Extract optional token */
6102 err = extract_credentials_delivery(context, credentials_delivery, response,
6103 "CreateDataBox");
6105 leave:
6106 xmlXPathFreeObject(result);
6107 xmlXPathFreeContext(xpath_ctx);
6108 xmlFreeDoc(response);
6109 xmlFreeNode(request);
6111 if (!err) {
6112 isds_log(ILF_ISDS, ILL_DEBUG,
6113 _("CreateDataBox request processed by server successfully.\n"));
6115 #else /* not HAVE_LIBCURL */
6116 err = IE_NOTSUP;
6117 #endif
6119 return err;
6123 /* Notify ISDS about new PFO entity.
6124 * This function has no real effect.
6125 * @context is session context
6126 * @box is PFO description including single primary user.
6127 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6128 * @former_names is optional undocumented string. Pass NULL if you don't care.
6129 * @upper_box_id is optional ID of supper box if currently created box is
6130 * subordinated.
6131 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6132 * @approval is optional external approval of box manipulation
6133 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6134 * NULL, if you don't care.*/
6135 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6136 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6137 const char *former_names, const char *upper_box_id,
6138 const char *ceo_label, const struct isds_approval *approval,
6139 char **refnumber) {
6140 isds_error err = IE_SUCCESS;
6141 #if HAVE_LIBCURL
6142 xmlNodePtr request = NULL;
6143 #endif
6145 if (!context) return IE_INVALID_CONTEXT;
6146 zfree(context->long_message);
6147 if (!box) return IE_INVAL;
6149 #if HAVE_LIBCURL
6150 /* Build CreateDataBoxPFOInfo request */
6151 err = build_CreateDBInput_request(context,
6152 &request, BAD_CAST "CreateDataBoxPFOInfo",
6153 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6154 (xmlChar *) ceo_label, NULL, approval);
6155 if (err) goto leave;
6157 /* Send it to server and process response */
6158 err = send_request_check_drop_response(context,
6159 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6160 (xmlChar **) refnumber);
6161 /* XXX: XML Schema names output dbID element but textual documentation
6162 * states no box identifier is returned. */
6163 leave:
6164 xmlFreeNode(request);
6165 #else /* not HAVE_LIBCURL */
6166 err = IE_NOTSUP;
6167 #endif
6168 return err;
6172 /* Common implementation for removing given box.
6173 * @context is session context
6174 * @service_name is UTF-8 encoded name fo ISDS service
6175 * @box is box description to delete
6176 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6177 * carry sane value. If NULL, do not inject this information into request.
6178 * @approval is optional external approval of box manipulation
6179 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6180 * NULL, if you don't care.*/
6181 isds_error _isds_delete_box_common(struct isds_ctx *context,
6182 const xmlChar *service_name,
6183 const struct isds_DbOwnerInfo *box, const struct tm *since,
6184 const struct isds_approval *approval, char **refnumber) {
6185 isds_error err = IE_SUCCESS;
6186 #if HAVE_LIBCURL
6187 xmlNsPtr isds_ns = NULL;
6188 xmlNodePtr request = NULL;
6189 xmlNodePtr node;
6190 xmlChar *string = NULL;
6191 #endif
6194 if (!context) return IE_INVALID_CONTEXT;
6195 zfree(context->long_message);
6196 if (!service_name || !*service_name || !box) return IE_INVAL;
6199 #if HAVE_LIBCURL
6200 /* Build DeleteDataBox(Promptly) request */
6201 request = xmlNewNode(NULL, service_name);
6202 if (!request) {
6203 char *service_name_locale = _isds_utf82locale((char*)service_name);
6204 isds_printf_message(context,
6205 _("Could build %s request"), service_name_locale);
6206 free(service_name_locale);
6207 return IE_ERROR;
6209 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6210 if(!isds_ns) {
6211 isds_log_message(context, _("Could not create ISDS name space"));
6212 xmlFreeNode(request);
6213 return IE_ERROR;
6215 xmlSetNs(request, isds_ns);
6217 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6218 err = insert_DbOwnerInfo(context, box, node);
6219 if (err) goto leave;
6221 if (since) {
6222 err = tm2datestring(since, &string);
6223 if (err) {
6224 isds_log_message(context,
6225 _("Could not convert `since' argument to ISO date string"));
6226 goto leave;
6228 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6229 zfree(string);
6232 err = insert_GExtApproval(context, approval, request);
6233 if (err) goto leave;
6236 /* Send it to server and process response */
6237 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6238 service_name, &request, (xmlChar **) refnumber);
6240 leave:
6241 xmlFreeNode(request);
6242 free(string);
6243 #else /* not HAVE_LIBCURL */
6244 err = IE_NOTSUP;
6245 #endif
6246 return err;
6250 /* Remove given box permanently.
6251 * @context is session context
6252 * @box is box description to delete
6253 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6254 * carry sane value.
6255 * @approval is optional external approval of box manipulation
6256 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6257 * NULL, if you don't care.*/
6258 isds_error isds_delete_box(struct isds_ctx *context,
6259 const struct isds_DbOwnerInfo *box, const struct tm *since,
6260 const struct isds_approval *approval, char **refnumber) {
6261 if (!context) return IE_INVALID_CONTEXT;
6262 zfree(context->long_message);
6263 if (!box || !since) return IE_INVAL;
6265 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6266 box, since, approval, refnumber);
6270 /* Undocumented function.
6271 * @context is session context
6272 * @box is box description to delete
6273 * @approval is optional external approval of box manipulation
6274 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6275 * NULL, if you don't care.*/
6276 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6277 const struct isds_DbOwnerInfo *box,
6278 const struct isds_approval *approval, char **refnumber) {
6279 if (!context) return IE_INVALID_CONTEXT;
6280 zfree(context->long_message);
6281 if (!box) return IE_INVAL;
6283 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6284 box, NULL, approval, refnumber);
6288 /* Update data about given box.
6289 * @context is session context
6290 * @old_box current box description
6291 * @new_box are updated data about @old_box
6292 * @approval is optional external approval of box manipulation
6293 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6294 * NULL, if you don't care.*/
6295 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6296 const struct isds_DbOwnerInfo *old_box,
6297 const struct isds_DbOwnerInfo *new_box,
6298 const struct isds_approval *approval, char **refnumber) {
6299 isds_error err = IE_SUCCESS;
6300 #if HAVE_LIBCURL
6301 xmlNsPtr isds_ns = NULL;
6302 xmlNodePtr request = NULL;
6303 xmlNodePtr node;
6304 #endif
6307 if (!context) return IE_INVALID_CONTEXT;
6308 zfree(context->long_message);
6309 if (!old_box || !new_box) return IE_INVAL;
6312 #if HAVE_LIBCURL
6313 /* Build UpdateDataBoxDescr request */
6314 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6315 if (!request) {
6316 isds_log_message(context,
6317 _("Could build UpdateDataBoxDescr request"));
6318 return IE_ERROR;
6320 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6321 if(!isds_ns) {
6322 isds_log_message(context, _("Could not create ISDS name space"));
6323 xmlFreeNode(request);
6324 return IE_ERROR;
6326 xmlSetNs(request, isds_ns);
6328 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6329 err = insert_DbOwnerInfo(context, old_box, node);
6330 if (err) goto leave;
6332 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6333 err = insert_DbOwnerInfo(context, new_box, node);
6334 if (err) goto leave;
6336 err = insert_GExtApproval(context, approval, request);
6337 if (err) goto leave;
6340 /* Send it to server and process response */
6341 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6342 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6344 leave:
6345 xmlFreeNode(request);
6346 #else /* not HAVE_LIBCURL */
6347 err = IE_NOTSUP;
6348 #endif
6350 return err;
6354 #if HAVE_LIBCURL
6355 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6356 * code
6357 * @context is session context
6358 * @service is SOAP service
6359 * @service_name is name of request in @service
6360 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6361 * @box_id is box ID of interest
6362 * @approval is optional external approval of box manipulation
6363 * @response is server SOAP body response as XML document
6364 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6365 * NULL, if you don't care.
6366 * @return error coded from lower layer, context message will be set up
6367 * appropriately. */
6368 static isds_error build_send_dbid_request_check_response(
6369 struct isds_ctx *context, const isds_service service,
6370 const xmlChar *service_name, const xmlChar *box_id_element,
6371 const xmlChar *box_id, const struct isds_approval *approval,
6372 xmlDocPtr *response, xmlChar **refnumber) {
6374 isds_error err = IE_SUCCESS;
6375 char *service_name_locale = NULL, *box_id_locale = NULL;
6376 xmlNodePtr request = NULL, node;
6377 xmlNsPtr isds_ns = NULL;
6379 if (!context) return IE_INVALID_CONTEXT;
6380 if (!service_name || !box_id) return IE_INVAL;
6381 if (!response) return IE_INVAL;
6383 /* Free output argument */
6384 xmlFreeDoc(*response); *response = NULL;
6386 /* Prepare strings */
6387 service_name_locale = _isds_utf82locale((char*)service_name);
6388 if (!service_name_locale) {
6389 err = IE_NOMEM;
6390 goto leave;
6392 box_id_locale = _isds_utf82locale((char*)box_id);
6393 if (!box_id_locale) {
6394 err = IE_NOMEM;
6395 goto leave;
6398 /* Build request */
6399 request = xmlNewNode(NULL, service_name);
6400 if (!request) {
6401 isds_printf_message(context,
6402 _("Could not build %s request for %s box"), service_name_locale,
6403 box_id_locale);
6404 err = IE_ERROR;
6405 goto leave;
6407 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6408 if(!isds_ns) {
6409 isds_log_message(context, _("Could not create ISDS name space"));
6410 err = IE_ERROR;
6411 goto leave;
6413 xmlSetNs(request, isds_ns);
6415 /* Add XSD:tIdDbInput children */
6416 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6417 INSERT_STRING(request, box_id_element, box_id);
6418 err = insert_GExtApproval(context, approval, request);
6419 if (err) goto leave;
6421 /* Send request and check response*/
6422 err = send_destroy_request_check_response(context,
6423 service, service_name, &request, response, refnumber, NULL);
6425 leave:
6426 free(service_name_locale);
6427 free(box_id_locale);
6428 xmlFreeNode(request);
6429 return err;
6431 #endif /* HAVE_LIBCURL */
6434 /* Get data about all users assigned to given box.
6435 * @context is session context
6436 * @box_id is box ID
6437 * @users is automatically reallocated list of struct isds_DbUserInfo */
6438 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6439 struct isds_list **users) {
6440 isds_error err = IE_SUCCESS;
6441 #if HAVE_LIBCURL
6442 xmlDocPtr response = NULL;
6443 xmlXPathContextPtr xpath_ctx = NULL;
6444 xmlXPathObjectPtr result = NULL;
6445 int i;
6446 struct isds_list *item, *prev_item = NULL;
6447 #endif
6449 if (!context) return IE_INVALID_CONTEXT;
6450 zfree(context->long_message);
6451 if (!users || !box_id) return IE_INVAL;
6452 isds_list_free(users);
6455 #if HAVE_LIBCURL
6456 /* Do request and check for success */
6457 err = build_send_dbid_request_check_response(context,
6458 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6459 BAD_CAST box_id, NULL, &response, NULL);
6460 if (err) goto leave;
6463 /* Extract data */
6464 /* Prepare structure */
6465 xpath_ctx = xmlXPathNewContext(response);
6466 if (!xpath_ctx) {
6467 err = IE_ERROR;
6468 goto leave;
6470 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6471 err = IE_ERROR;
6472 goto leave;
6475 /* Set context node */
6476 result = xmlXPathEvalExpression(BAD_CAST
6477 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6478 xpath_ctx);
6479 if (!result) {
6480 err = IE_ERROR;
6481 goto leave;
6483 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6484 /* Iterate over all users */
6485 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6487 /* Prepare structure */
6488 item = calloc(1, sizeof(*item));
6489 if (!item) {
6490 err = IE_NOMEM;
6491 goto leave;
6493 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6494 if (i == 0) *users = item;
6495 else prev_item->next = item;
6496 prev_item = item;
6498 /* Extract it */
6499 xpath_ctx->node = result->nodesetval->nodeTab[i];
6500 err = extract_DbUserInfo(context,
6501 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6502 if (err) goto leave;
6506 leave:
6507 if (err) {
6508 isds_list_free(users);
6511 xmlXPathFreeObject(result);
6512 xmlXPathFreeContext(xpath_ctx);
6513 xmlFreeDoc(response);
6515 if (!err)
6516 isds_log(ILF_ISDS, ILL_DEBUG,
6517 _("GetDataBoxUsers request processed by server "
6518 "successfully.\n"));
6519 #else /* not HAVE_LIBCURL */
6520 err = IE_NOTSUP;
6521 #endif
6523 return err;
6527 /* Update data about user assigned to given box.
6528 * @context is session context
6529 * @box is box identification
6530 * @old_user identifies user to update
6531 * @new_user are updated data about @old_user
6532 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6533 * NULL, if you don't care.*/
6534 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6535 const struct isds_DbOwnerInfo *box,
6536 const struct isds_DbUserInfo *old_user,
6537 const struct isds_DbUserInfo *new_user,
6538 char **refnumber) {
6539 isds_error err = IE_SUCCESS;
6540 #if HAVE_LIBCURL
6541 xmlNsPtr isds_ns = NULL;
6542 xmlNodePtr request = NULL;
6543 xmlNodePtr node;
6544 #endif
6547 if (!context) return IE_INVALID_CONTEXT;
6548 zfree(context->long_message);
6549 if (!box || !old_user || !new_user) return IE_INVAL;
6552 #if HAVE_LIBCURL
6553 /* Build UpdateDataBoxUser request */
6554 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6555 if (!request) {
6556 isds_log_message(context,
6557 _("Could build UpdateDataBoxUser request"));
6558 return IE_ERROR;
6560 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6561 if(!isds_ns) {
6562 isds_log_message(context, _("Could not create ISDS name space"));
6563 xmlFreeNode(request);
6564 return IE_ERROR;
6566 xmlSetNs(request, isds_ns);
6568 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6569 err = insert_DbOwnerInfo(context, box, node);
6570 if (err) goto leave;
6572 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6573 err = insert_DbUserInfo(context, old_user, node);
6574 if (err) goto leave;
6576 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6577 err = insert_DbUserInfo(context, new_user, node);
6578 if (err) goto leave;
6580 /* Send it to server and process response */
6581 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6582 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6584 leave:
6585 xmlFreeNode(request);
6586 #else /* not HAVE_LIBCURL */
6587 err = IE_NOTSUP;
6588 #endif
6590 return err;
6594 /* Undocumented function.
6595 * @context is session context
6596 * @box_id is UTF-8 encoded box identifier
6597 * @token is UTF-8 encoded temporary password
6598 * @user_id outputs UTF-8 encoded reallocated user identifier
6599 * @password outpus UTF-8 encoded reallocated user password
6600 * Output arguments will be nulled in case of error */
6601 isds_error isds_activate(struct isds_ctx *context,
6602 const char *box_id, const char *token,
6603 char **user_id, char **password) {
6604 isds_error err = IE_SUCCESS;
6605 #if HAVE_LIBCURL
6606 xmlNsPtr isds_ns = NULL;
6607 xmlNodePtr request = NULL, node;
6608 xmlDocPtr response = NULL;
6609 xmlXPathContextPtr xpath_ctx = NULL;
6610 xmlXPathObjectPtr result = NULL;
6611 #endif
6614 if (!context) return IE_INVALID_CONTEXT;
6615 zfree(context->long_message);
6617 if (user_id) zfree(*user_id);
6618 if (password) zfree(*password);
6620 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6623 #if HAVE_LIBCURL
6624 /* Build Activate request */
6625 request = xmlNewNode(NULL, BAD_CAST "Activate");
6626 if (!request) {
6627 isds_log_message(context, _("Could build Activate request"));
6628 return IE_ERROR;
6630 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6631 if(!isds_ns) {
6632 isds_log_message(context, _("Could not create ISDS name space"));
6633 xmlFreeNode(request);
6634 return IE_ERROR;
6636 xmlSetNs(request, isds_ns);
6638 INSERT_STRING(request, "dbAccessDataId", token);
6639 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6640 INSERT_STRING(request, "dbID", box_id);
6643 /* Send request and check response*/
6644 err = send_destroy_request_check_response(context,
6645 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6646 &response, NULL, NULL);
6647 if (err) goto leave;
6650 /* Extract data */
6651 xpath_ctx = xmlXPathNewContext(response);
6652 if (!xpath_ctx) {
6653 err = IE_ERROR;
6654 goto leave;
6656 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6657 err = IE_ERROR;
6658 goto leave;
6660 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6661 xpath_ctx);
6662 if (!result) {
6663 err = IE_ERROR;
6664 goto leave;
6666 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6667 isds_log_message(context, _("Missing ActivateResponse element"));
6668 err = IE_ISDS;
6669 goto leave;
6671 if (result->nodesetval->nodeNr > 1) {
6672 isds_log_message(context, _("Multiple ActivateResponse element"));
6673 err = IE_ISDS;
6674 goto leave;
6676 xpath_ctx->node = result->nodesetval->nodeTab[0];
6677 xmlXPathFreeObject(result); result = NULL;
6679 EXTRACT_STRING("isds:userId", *user_id);
6680 if (!*user_id)
6681 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6682 "but did not return `userId' element.\n"));
6684 EXTRACT_STRING("isds:password", *password);
6685 if (!*password)
6686 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6687 "but did not return `password' element.\n"));
6689 leave:
6690 xmlXPathFreeObject(result);
6691 xmlXPathFreeContext(xpath_ctx);
6692 xmlFreeDoc(response);
6693 xmlFreeNode(request);
6695 if (!err)
6696 isds_log(ILF_ISDS, ILL_DEBUG,
6697 _("Activate request processed by server successfully.\n"));
6698 #else /* not HAVE_LIBCURL */
6699 err = IE_NOTSUP;
6700 #endif
6702 return err;
6706 /* Reset credentials of user assigned to given box.
6707 * @context is session context
6708 * @box is box identification
6709 * @user identifies user to reset password
6710 * @fee_paid is true if fee has been paid, false otherwise
6711 * @approval is optional external approval of box manipulation
6712 * @credentials_delivery is NULL if new password should be delivered off-line
6713 * to the user. It is valid pointer if user should obtain new password on-line
6714 * on dedicated web server. Then input @credentials_delivery.email value is
6715 * user's e-mail address user must provide to dedicated web server together
6716 * with @credentials_delivery.token. The output reallocated token user needs
6717 * to use to authorize on the web server to view his new password. Output
6718 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6719 * ISDS changed up on this call. (No reason why server could change the name
6720 * is known now.)
6721 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6722 * NULL, if you don't care.*/
6723 isds_error isds_reset_password(struct isds_ctx *context,
6724 const struct isds_DbOwnerInfo *box,
6725 const struct isds_DbUserInfo *user,
6726 const _Bool fee_paid, const struct isds_approval *approval,
6727 struct isds_credentials_delivery *credentials_delivery,
6728 char **refnumber) {
6729 isds_error err = IE_SUCCESS;
6730 #if HAVE_LIBCURL
6731 xmlNsPtr isds_ns = NULL;
6732 xmlNodePtr request = NULL, node;
6733 xmlDocPtr response = NULL;
6734 #endif
6737 if (!context) return IE_INVALID_CONTEXT;
6738 zfree(context->long_message);
6740 if (credentials_delivery) {
6741 zfree(credentials_delivery->token);
6742 zfree(credentials_delivery->new_user_name);
6744 if (!box || !user) return IE_INVAL;
6747 #if HAVE_LIBCURL
6748 /* Build NewAccessData request */
6749 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6750 if (!request) {
6751 isds_log_message(context,
6752 _("Could build NewAccessData request"));
6753 return IE_ERROR;
6755 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6756 if(!isds_ns) {
6757 isds_log_message(context, _("Could not create ISDS name space"));
6758 xmlFreeNode(request);
6759 return IE_ERROR;
6761 xmlSetNs(request, isds_ns);
6763 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6764 err = insert_DbOwnerInfo(context, box, node);
6765 if (err) goto leave;
6767 INSERT_ELEMENT(node, request, "dbUserInfo");
6768 err = insert_DbUserInfo(context, user, node);
6769 if (err) goto leave;
6771 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6773 err = insert_credentials_delivery(context, credentials_delivery, request);
6774 if (err) goto leave;
6776 err = insert_GExtApproval(context, approval, request);
6777 if (err) goto leave;
6779 /* Send request and check response*/
6780 err = send_destroy_request_check_response(context,
6781 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6782 &response, (xmlChar **) refnumber, NULL);
6783 if (err) goto leave;
6786 /* Extract optional token */
6787 err = extract_credentials_delivery(context, credentials_delivery,
6788 response, "NewAccessData");
6790 leave:
6791 xmlFreeDoc(response);
6792 xmlFreeNode(request);
6794 if (!err)
6795 isds_log(ILF_ISDS, ILL_DEBUG,
6796 _("NewAccessData request processed by server "
6797 "successfully.\n"));
6798 #else /* not HAVE_LIBCURL */
6799 err = IE_NOTSUP;
6800 #endif
6802 return err;
6806 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6807 * code, destroy response and log success.
6808 * @context is ISDS session context.
6809 * @service_name is name of SERVICE_DB_MANIPULATION service
6810 * @box is box identification
6811 * @user identifies user to remove
6812 * @credentials_delivery is NULL if new user's password should be delivered
6813 * off-line to the user. It is valid pointer if user should obtain new
6814 * password on-line on dedicated web server. Then input
6815 * @credentials_delivery.email value is user's e-mail address user must
6816 * provide to dedicated web server together with @credentials_delivery.token.
6817 * The output reallocated token user needs to use to authorize on the web
6818 * server to view his new password. Output reallocated
6819 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6820 * assingned or changed up on this call.
6821 * @approval is optional external approval of box manipulation
6822 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6823 * NULL, if you don't care. */
6824 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6825 struct isds_ctx *context, const xmlChar *service_name,
6826 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6827 struct isds_credentials_delivery *credentials_delivery,
6828 const struct isds_approval *approval, xmlChar **refnumber) {
6829 isds_error err = IE_SUCCESS;
6830 #if HAVE_LIBCURL
6831 xmlNsPtr isds_ns = NULL;
6832 xmlNodePtr request = NULL, node;
6833 xmlDocPtr response = NULL;
6834 #endif
6837 if (!context) return IE_INVALID_CONTEXT;
6838 zfree(context->long_message);
6839 if (credentials_delivery) {
6840 zfree(credentials_delivery->token);
6841 zfree(credentials_delivery->new_user_name);
6843 if (!service_name || service_name[0] == '\0' || !box || !user)
6844 return IE_INVAL;
6847 #if HAVE_LIBCURL
6848 /* Build NewAccessData or similar request */
6849 request = xmlNewNode(NULL, service_name);
6850 if (!request) {
6851 char *service_name_locale = _isds_utf82locale((char *) service_name);
6852 isds_printf_message(context, _("Could not build %s request"),
6853 service_name_locale);
6854 free(service_name_locale);
6855 return IE_ERROR;
6857 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6858 if(!isds_ns) {
6859 isds_log_message(context, _("Could not create ISDS name space"));
6860 xmlFreeNode(request);
6861 return IE_ERROR;
6863 xmlSetNs(request, isds_ns);
6865 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6866 err = insert_DbOwnerInfo(context, box, node);
6867 if (err) goto leave;
6869 INSERT_ELEMENT(node, request, "dbUserInfo");
6870 err = insert_DbUserInfo(context, user, node);
6871 if (err) goto leave;
6873 err = insert_credentials_delivery(context, credentials_delivery, request);
6874 if (err) goto leave;
6876 err = insert_GExtApproval(context, approval, request);
6877 if (err) goto leave;
6880 /* Send request and check response*/
6881 err = send_destroy_request_check_response(context,
6882 SERVICE_DB_MANIPULATION, service_name, &request, &response,
6883 refnumber, NULL);
6885 xmlFreeNode(request);
6886 request = NULL;
6888 /* Pick up credentials_delivery if requested */
6889 err = extract_credentials_delivery(context, credentials_delivery, response,
6890 (char *)service_name);
6892 leave:
6893 xmlFreeDoc(response);
6894 if (request) xmlFreeNode(request);
6896 if (!err) {
6897 char *service_name_locale = _isds_utf82locale((char *) service_name);
6898 isds_log(ILF_ISDS, ILL_DEBUG,
6899 _("%s request processed by server successfully.\n"),
6900 service_name_locale);
6901 free(service_name_locale);
6903 #else /* not HAVE_LIBCURL */
6904 err = IE_NOTSUP;
6905 #endif
6907 return err;
6911 /* Assign new user to given box.
6912 * @context is session context
6913 * @box is box identification
6914 * @user defines new user to add
6915 * @credentials_delivery is NULL if new user's password should be delivered
6916 * off-line to the user. It is valid pointer if user should obtain new
6917 * password on-line on dedicated web server. Then input
6918 * @credentials_delivery.email value is user's e-mail address user must
6919 * provide to dedicated web server together with @credentials_delivery.token.
6920 * The output reallocated token user needs to use to authorize on the web
6921 * server to view his new password. Output reallocated
6922 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6923 * assingned up on this call.
6924 * @approval is optional external approval of box manipulation
6925 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6926 * NULL, if you don't care.*/
6927 isds_error isds_add_user(struct isds_ctx *context,
6928 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6929 struct isds_credentials_delivery *credentials_delivery,
6930 const struct isds_approval *approval, char **refnumber) {
6931 return build_send_manipulationboxuser_request_check_drop_response(context,
6932 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
6933 approval, (xmlChar **) refnumber);
6937 /* Remove user assigned to given box.
6938 * @context is session context
6939 * @box is box identification
6940 * @user identifies user to remove
6941 * @approval is optional external approval of box manipulation
6942 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6943 * NULL, if you don't care.*/
6944 isds_error isds_delete_user(struct isds_ctx *context,
6945 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6946 const struct isds_approval *approval, char **refnumber) {
6947 return build_send_manipulationboxuser_request_check_drop_response(context,
6948 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
6949 (xmlChar **) refnumber);
6953 /* Get list of boxes in ZIP archive.
6954 * @context is session context
6955 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
6956 * System recognizes following values currently: ALL (all boxes), UPG
6957 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
6958 * receiving commercial messages). This argument is a string because
6959 * specification states new values can appear in the future. Not all list
6960 * types are available to all users.
6961 * @buffer is automatically reallocated memory to store the list of boxes. The
6962 * list is zipped CSV file.
6963 * @buffer_length is size of @buffer data in bytes.
6964 * In case of error @buffer will be freed and @buffer_length will be
6965 * undefined.*/
6966 isds_error isds_get_box_list_archive(struct isds_ctx *context,
6967 const char *list_identifier, void **buffer, size_t *buffer_length) {
6968 isds_error err = IE_SUCCESS;
6969 #if HAVE_LIBCURL
6970 xmlNsPtr isds_ns = NULL;
6971 xmlNodePtr request = NULL, node;
6972 xmlDocPtr response = NULL;
6973 xmlXPathContextPtr xpath_ctx = NULL;
6974 xmlXPathObjectPtr result = NULL;
6975 char *string = NULL;
6976 #endif
6979 if (!context) return IE_INVALID_CONTEXT;
6980 zfree(context->long_message);
6981 if (buffer) zfree(*buffer);
6982 if (!buffer || !buffer_length) return IE_INVAL;
6985 #if HAVE_LIBCURL
6986 /* Check if connection is established
6987 * TODO: This check should be done downstairs. */
6988 if (!context->curl) return IE_CONNECTION_CLOSED;
6991 /* Build AuthenticateMessage request */
6992 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
6993 if (!request) {
6994 isds_log_message(context,
6995 _("Could not build GetDataBoxList request"));
6996 return IE_ERROR;
6998 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6999 if(!isds_ns) {
7000 isds_log_message(context, _("Could not create ISDS name space"));
7001 xmlFreeNode(request);
7002 return IE_ERROR;
7004 xmlSetNs(request, isds_ns);
7005 INSERT_STRING(request, "dblType", list_identifier);
7007 /* Send request to server and process response */
7008 err = send_destroy_request_check_response(context,
7009 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7010 &response, NULL, NULL);
7011 if (err) goto leave;
7014 /* Extract Base-64 encoded ZIP file */
7015 xpath_ctx = xmlXPathNewContext(response);
7016 if (!xpath_ctx) {
7017 err = IE_ERROR;
7018 goto leave;
7020 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7021 err = IE_ERROR;
7022 goto leave;
7024 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7026 /* Decode non-empty archive */
7027 if (string && string[0] != '\0') {
7028 *buffer_length = _isds_b64decode(string, buffer);
7029 if (*buffer_length == (size_t) -1) {
7030 isds_printf_message(context,
7031 _("Error while Base64-decoding box list archive"));
7032 err = IE_ERROR;
7033 goto leave;
7038 leave:
7039 free(string);
7040 xmlXPathFreeObject(result);
7041 xmlXPathFreeContext(xpath_ctx);
7042 xmlFreeDoc(response);
7043 xmlFreeNode(request);
7045 if (!err) {
7046 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7047 "processed by server successfully.\n"));
7049 #else /* not HAVE_LIBCURL */
7050 err = IE_NOTSUP;
7051 #endif
7053 return err;
7057 /* Find boxes suiting given criteria.
7058 * @criteria is filter. You should fill in at least some members.
7059 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7060 * possibly empty. Input NULL or valid old structure.
7061 * @return:
7062 * IE_SUCCESS if search succeeded, @boxes contains useful data
7063 * IE_NOEXIST if no such box exists, @boxes will be NULL
7064 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7065 * contains still valid data
7066 * other code if something bad happens. @boxes will be NULL. */
7067 isds_error isds_FindDataBox(struct isds_ctx *context,
7068 const struct isds_DbOwnerInfo *criteria,
7069 struct isds_list **boxes) {
7070 isds_error err = IE_SUCCESS;
7071 #if HAVE_LIBCURL
7072 _Bool truncated = 0;
7073 xmlNsPtr isds_ns = NULL;
7074 xmlNodePtr request = NULL;
7075 xmlDocPtr response = NULL;
7076 xmlChar *code = NULL, *message = NULL;
7077 xmlNodePtr db_owner_info;
7078 xmlXPathContextPtr xpath_ctx = NULL;
7079 xmlXPathObjectPtr result = NULL;
7080 xmlChar *string = NULL;
7081 #endif
7084 if (!context) return IE_INVALID_CONTEXT;
7085 zfree(context->long_message);
7086 if (!boxes) return IE_INVAL;
7087 isds_list_free(boxes);
7089 if (!criteria) {
7090 return IE_INVAL;
7093 #if HAVE_LIBCURL
7094 /* Check if connection is established
7095 * TODO: This check should be done downstairs. */
7096 if (!context->curl) return IE_CONNECTION_CLOSED;
7099 /* Build FindDataBox request */
7100 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
7101 if (!request) {
7102 isds_log_message(context,
7103 _("Could build FindDataBox request"));
7104 return IE_ERROR;
7106 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7107 if(!isds_ns) {
7108 isds_log_message(context, _("Could not create ISDS name space"));
7109 xmlFreeNode(request);
7110 return IE_ERROR;
7112 xmlSetNs(request, isds_ns);
7113 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7114 if (!db_owner_info) {
7115 isds_log_message(context, _("Could not add dbOwnerInfo child to "
7116 "FindDataBox element"));
7117 xmlFreeNode(request);
7118 return IE_ERROR;
7121 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
7122 if (err) goto leave;
7125 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
7127 /* Sent request */
7128 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7130 /* Destroy request */
7131 xmlFreeNode(request); request = NULL;
7133 if (err) {
7134 isds_log(ILF_ISDS, ILL_DEBUG,
7135 _("Processing ISDS response on FindDataBox "
7136 "request failed\n"));
7137 goto leave;
7140 /* Check for response status */
7141 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7142 &code, &message, NULL);
7143 if (err) {
7144 isds_log(ILF_ISDS, ILL_DEBUG,
7145 _("ISDS response on FindDataBox request is missing status\n"));
7146 goto leave;
7149 /* Request processed, but nothing found */
7150 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7151 !xmlStrcmp(code, BAD_CAST "5001")) {
7152 char *code_locale = _isds_utf82locale((char*)code);
7153 char *message_locale = _isds_utf82locale((char*)message);
7154 isds_log(ILF_ISDS, ILL_DEBUG,
7155 _("Server did not found any box on FindDataBox request "
7156 "(code=%s, message=%s)\n"), code_locale, message_locale);
7157 isds_log_message(context, message_locale);
7158 free(code_locale);
7159 free(message_locale);
7160 err = IE_NOEXIST;
7161 goto leave;
7164 /* Warning, not a error */
7165 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7166 char *code_locale = _isds_utf82locale((char*)code);
7167 char *message_locale = _isds_utf82locale((char*)message);
7168 isds_log(ILF_ISDS, ILL_DEBUG,
7169 _("Server truncated response on FindDataBox request "
7170 "(code=%s, message=%s)\n"), code_locale, message_locale);
7171 isds_log_message(context, message_locale);
7172 free(code_locale);
7173 free(message_locale);
7174 truncated = 1;
7177 /* Other error */
7178 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7179 char *code_locale = _isds_utf82locale((char*)code);
7180 char *message_locale = _isds_utf82locale((char*)message);
7181 isds_log(ILF_ISDS, ILL_DEBUG,
7182 _("Server refused FindDataBox request "
7183 "(code=%s, message=%s)\n"), code_locale, message_locale);
7184 isds_log_message(context, message_locale);
7185 free(code_locale);
7186 free(message_locale);
7187 err = IE_ISDS;
7188 goto leave;
7191 xpath_ctx = xmlXPathNewContext(response);
7192 if (!xpath_ctx) {
7193 err = IE_ERROR;
7194 goto leave;
7196 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7197 err = IE_ERROR;
7198 goto leave;
7201 /* Extract boxes if they present */
7202 result = xmlXPathEvalExpression(BAD_CAST
7203 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7204 xpath_ctx);
7205 if (!result) {
7206 err = IE_ERROR;
7207 goto leave;
7209 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7210 struct isds_list *item, *prev_item = NULL;
7211 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7212 item = calloc(1, sizeof(*item));
7213 if (!item) {
7214 err = IE_NOMEM;
7215 goto leave;
7218 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7219 if (i == 0) *boxes = item;
7220 else prev_item->next = item;
7221 prev_item = item;
7223 xpath_ctx->node = result->nodesetval->nodeTab[i];
7224 err = extract_DbOwnerInfo(context,
7225 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7226 if (err) goto leave;
7230 leave:
7231 if (err) {
7232 isds_list_free(boxes);
7233 } else {
7234 if (truncated) err = IE_2BIG;
7237 free(string);
7238 xmlFreeNode(request);
7239 xmlXPathFreeObject(result);
7240 xmlXPathFreeContext(xpath_ctx);
7242 free(code);
7243 free(message);
7244 xmlFreeDoc(response);
7246 if (!err)
7247 isds_log(ILF_ISDS, ILL_DEBUG,
7248 _("FindDataBox request processed by server successfully.\n"));
7249 #else /* not HAVE_LIBCURL */
7250 err = IE_NOTSUP;
7251 #endif
7253 return err;
7257 /* Get status of a box.
7258 * @context is ISDS session context.
7259 * @box_id is UTF-8 encoded box identifier as zero terminated string
7260 * @box_status is return value of box status.
7261 * @return:
7262 * IE_SUCCESS if box has been found and its status retrieved
7263 * IE_NOEXIST if box is not known to ISDS server
7264 * or other appropriate error.
7265 * You can use isds_DbState to enumerate box status. However out of enum
7266 * range value can be returned too. This is feature because ISDS
7267 * specification leaves the set of values open.
7268 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7269 * the box has been deleted, but ISDS still lists its former existence. */
7270 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7271 long int *box_status) {
7272 isds_error err = IE_SUCCESS;
7273 #if HAVE_LIBCURL
7274 xmlNsPtr isds_ns = NULL;
7275 xmlNodePtr request = NULL, db_id;
7276 xmlDocPtr response = NULL;
7277 xmlXPathContextPtr xpath_ctx = NULL;
7278 xmlXPathObjectPtr result = NULL;
7279 xmlChar *string = NULL;
7281 const xmlChar *codes[] = {
7282 BAD_CAST "5001",
7283 BAD_CAST "1007",
7284 BAD_CAST "2011",
7285 NULL
7287 const char *meanings[] = {
7288 "The box does not exist",
7289 "Box ID is malformed",
7290 "Box ID malformed",
7292 const isds_error errors[] = {
7293 IE_NOEXIST,
7294 IE_INVAL,
7295 IE_INVAL,
7297 struct code_map_isds_error map = {
7298 .codes = codes,
7299 .meanings = meanings,
7300 .errors = errors
7302 #endif
7304 if (!context) return IE_INVALID_CONTEXT;
7305 zfree(context->long_message);
7306 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7308 #if HAVE_LIBCURL
7309 /* Check if connection is established
7310 * TODO: This check should be done downstairs. */
7311 if (!context->curl) return IE_CONNECTION_CLOSED;
7314 /* Build CheckDataBox request */
7315 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7316 if (!request) {
7317 isds_log_message(context,
7318 _("Could build CheckDataBox request"));
7319 return IE_ERROR;
7321 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7322 if(!isds_ns) {
7323 isds_log_message(context, _("Could not create ISDS name space"));
7324 xmlFreeNode(request);
7325 return IE_ERROR;
7327 xmlSetNs(request, isds_ns);
7328 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7329 if (!db_id) {
7330 isds_log_message(context, _("Could not add dbID child to "
7331 "CheckDataBox element"));
7332 xmlFreeNode(request);
7333 return IE_ERROR;
7337 /* Send request and check response*/
7338 err = send_destroy_request_check_response(context,
7339 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7340 &request, &response, NULL, &map);
7341 if (err) goto leave;
7344 /* Extract data */
7345 xpath_ctx = xmlXPathNewContext(response);
7346 if (!xpath_ctx) {
7347 err = IE_ERROR;
7348 goto leave;
7350 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7351 err = IE_ERROR;
7352 goto leave;
7354 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7355 xpath_ctx);
7356 if (!result) {
7357 err = IE_ERROR;
7358 goto leave;
7360 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7361 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7362 err = IE_ISDS;
7363 goto leave;
7365 if (result->nodesetval->nodeNr > 1) {
7366 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7367 err = IE_ISDS;
7368 goto leave;
7370 xpath_ctx->node = result->nodesetval->nodeTab[0];
7371 xmlXPathFreeObject(result); result = NULL;
7373 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7376 leave:
7377 free(string);
7378 xmlXPathFreeObject(result);
7379 xmlXPathFreeContext(xpath_ctx);
7381 xmlFreeDoc(response);
7383 if (!err)
7384 isds_log(ILF_ISDS, ILL_DEBUG,
7385 _("CheckDataBox request processed by server successfully.\n"));
7386 #else /* not HAVE_LIBCURL */
7387 err = IE_NOTSUP;
7388 #endif
7390 return err;
7394 /* Get list of permissions to send commercial messages.
7395 * @context is ISDS session context.
7396 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7397 * @permissions is a reallocated list of permissions (struct
7398 * isds_commercial_permission*) to send commercial messages from @box_id. The
7399 * order of permissions is significant as the server applies the permissions
7400 * and associated pre-paid credits in the order. Empty list means no
7401 * permission.
7402 * @return:
7403 * IE_SUCCESS if the list has been obtained correctly,
7404 * or other appropriate error. */
7405 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7406 const char *box_id, struct isds_list **permissions) {
7407 isds_error err = IE_SUCCESS;
7408 #if HAVE_LIBCURL
7409 xmlDocPtr response = NULL;
7410 xmlXPathContextPtr xpath_ctx = NULL;
7411 xmlXPathObjectPtr result = NULL;
7412 #endif
7414 if (!context) return IE_INVALID_CONTEXT;
7415 zfree(context->long_message);
7416 if (NULL == permissions) return IE_INVAL;
7417 isds_list_free(permissions);
7418 if (NULL == box_id) return IE_INVAL;
7420 #if HAVE_LIBCURL
7421 /* Check if connection is established */
7422 if (!context->curl) return IE_CONNECTION_CLOSED;
7424 /* Do request and check for success */
7425 err = build_send_dbid_request_check_response(context,
7426 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7427 BAD_CAST box_id, NULL, &response, NULL);
7428 if (!err) {
7429 isds_log(ILF_ISDS, ILL_DEBUG,
7430 _("PDZInfo request processed by server successfully.\n"));
7433 /* Extract data */
7434 /* Prepare structure */
7435 xpath_ctx = xmlXPathNewContext(response);
7436 if (!xpath_ctx) {
7437 err = IE_ERROR;
7438 goto leave;
7440 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7441 err = IE_ERROR;
7442 goto leave;
7445 /* Set context node */
7446 result = xmlXPathEvalExpression(BAD_CAST
7447 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7448 xpath_ctx);
7449 if (!result) {
7450 err = IE_ERROR;
7451 goto leave;
7453 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7454 struct isds_list *prev_item = NULL;
7456 /* Iterate over all permission records */
7457 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7458 struct isds_list *item;
7460 /* Prepare structure */
7461 item = calloc(1, sizeof(*item));
7462 if (!item) {
7463 err = IE_NOMEM;
7464 goto leave;
7466 item->destructor = (void(*)(void**))isds_commercial_permission_free;
7467 if (i == 0) *permissions = item;
7468 else prev_item->next = item;
7469 prev_item = item;
7471 /* Extract it */
7472 xpath_ctx->node = result->nodesetval->nodeTab[i];
7473 err = extract_DbPDZRecord(context,
7474 (struct isds_commercial_permission **) (&item->data),
7475 xpath_ctx);
7476 if (err) goto leave;
7480 leave:
7481 if (err) {
7482 isds_list_free(permissions);
7485 xmlXPathFreeObject(result);
7486 xmlXPathFreeContext(xpath_ctx);
7487 xmlFreeDoc(response);
7489 #else /* not HAVE_LIBCURL */
7490 err = IE_NOTSUP;
7491 #endif
7493 return err;
7497 /* Get details about credit for sending pre-paid commercial messages.
7498 * @context is ISDS session context.
7499 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
7500 * @from_date is first day of credit history to return in @history. Only
7501 * tm_year, tm_mon and tm_mday carry sane value.
7502 * @to_date is last day of credit history to return in @history. Only
7503 * tm_year, tm_mon and tm_mday carry sane value.
7504 * @credit outputs current credit value into pre-allocated memory. Pass NULL
7505 * if you don't care. This and all other credit values are integers in
7506 * hundredths of Czech Crowns.
7507 * @email outputs notification e-mail address where notifications about credit
7508 * are sent. This is automatically reallocated string. Pass NULL if you don't
7509 * care. It can return NULL if no address is defined.
7510 * @history outputs auto-reallocated list of pointers to struct
7511 * isds_credit_event. Events in closed interval @from_time to @to_time are
7512 * returned. Pass NULL @to_time and @from_time if you don't care. The events
7513 * are sorted by time.
7514 * @return:
7515 * IE_SUCCESS if the credit details have been obtained correctly,
7516 * or other appropriate error. Please note that server allows to retrieve
7517 * only limited history of events. */
7518 isds_error isds_get_commercial_credit(struct isds_ctx *context,
7519 const char *box_id,
7520 const struct tm *from_date, const struct tm *to_date,
7521 long int *credit, char **email, struct isds_list **history) {
7522 isds_error err = IE_SUCCESS;
7523 #if HAVE_LIBCURL
7524 char *box_id_locale = NULL;
7525 xmlNodePtr request = NULL, node;
7526 xmlNsPtr isds_ns = NULL;
7527 xmlChar *string = NULL;
7529 xmlDocPtr response = NULL;
7530 xmlXPathContextPtr xpath_ctx = NULL;
7531 xmlXPathObjectPtr result = NULL;
7533 const xmlChar *codes[] = {
7534 BAD_CAST "1004",
7535 BAD_CAST "2011",
7536 BAD_CAST "1093",
7537 BAD_CAST "1137",
7538 BAD_CAST "1058",
7539 NULL
7541 const char *meanings[] = {
7542 "Insufficient priviledges for the box",
7543 "The box does not exist",
7544 "Date is too long (history is not available after 15 months)",
7545 "Interval is too long (limit is 3 months)",
7546 "Invalid date"
7548 const isds_error errors[] = {
7549 IE_ISDS,
7550 IE_NOEXIST,
7551 IE_DATE,
7552 IE_DATE,
7553 IE_DATE,
7555 struct code_map_isds_error map = {
7556 .codes = codes,
7557 .meanings = meanings,
7558 .errors = errors
7560 #endif
7562 if (!context) return IE_INVALID_CONTEXT;
7563 zfree(context->long_message);
7565 /* Free output argument */
7566 if (NULL != credit) *credit = 0;
7567 if (NULL != email) zfree(*email);
7568 isds_list_free(history);
7570 if (NULL == box_id) return IE_INVAL;
7572 #if HAVE_LIBCURL
7573 /* Check if connection is established */
7574 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7576 box_id_locale = _isds_utf82locale((char*)box_id);
7577 if (NULL == box_id_locale) {
7578 err = IE_NOMEM;
7579 goto leave;
7582 /* Build request */
7583 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
7584 if (NULL == request) {
7585 isds_printf_message(context,
7586 _("Could not build DataBoxCreditInfo request for %s box"),
7587 box_id_locale);
7588 err = IE_ERROR;
7589 goto leave;
7591 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7592 if(!isds_ns) {
7593 isds_log_message(context, _("Could not create ISDS name space"));
7594 err = IE_ERROR;
7595 goto leave;
7597 xmlSetNs(request, isds_ns);
7599 /* Add mandatory XSD:tIdDbInput child */
7600 INSERT_STRING(request, BAD_CAST "dbID", box_id);
7601 /* Add mandatory dates elements with optional values */
7602 if (from_date) {
7603 err = tm2datestring(from_date, &string);
7604 if (err) {
7605 isds_log_message(context,
7606 _("Could not convert `from_date' argument to ISO date "
7607 "string"));
7608 goto leave;
7610 INSERT_STRING(request, "ciFromDate", string);
7611 zfree(string);
7612 } else {
7613 INSERT_STRING(request, "ciFromDate", NULL);
7615 if (to_date) {
7616 err = tm2datestring(to_date, &string);
7617 if (err) {
7618 isds_log_message(context,
7619 _("Could not convert `to_date' argument to ISO date "
7620 "string"));
7621 goto leave;
7623 INSERT_STRING(request, "ciTodate", string);
7624 zfree(string);
7625 } else {
7626 INSERT_STRING(request, "ciTodate", NULL);
7629 /* Send request and check response*/
7630 err = send_destroy_request_check_response(context,
7631 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
7632 &request, &response, NULL, &map);
7633 if (err) goto leave;
7636 /* Extract data */
7637 /* Set context to the root */
7638 xpath_ctx = xmlXPathNewContext(response);
7639 if (!xpath_ctx) {
7640 err = IE_ERROR;
7641 goto leave;
7643 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7644 err = IE_ERROR;
7645 goto leave;
7647 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
7648 xpath_ctx);
7649 if (!result) {
7650 err = IE_ERROR;
7651 goto leave;
7653 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7654 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
7655 err = IE_ISDS;
7656 goto leave;
7658 if (result->nodesetval->nodeNr > 1) {
7659 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
7660 err = IE_ISDS;
7661 goto leave;
7663 xpath_ctx->node = result->nodesetval->nodeTab[0];
7664 xmlXPathFreeObject(result); result = NULL;
7666 /* Extract common data */
7667 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
7668 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
7670 /* Extract records */
7671 if (NULL == history) goto leave;
7672 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
7673 xpath_ctx);
7674 if (!result) {
7675 err = IE_ERROR;
7676 goto leave;
7678 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7679 struct isds_list *prev_item = NULL;
7681 /* Iterate over all records */
7682 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7683 struct isds_list *item;
7685 /* Prepare structure */
7686 item = calloc(1, sizeof(*item));
7687 if (!item) {
7688 err = IE_NOMEM;
7689 goto leave;
7691 item->destructor = (void(*)(void**))isds_credit_event_free;
7692 if (i == 0) *history = item;
7693 else prev_item->next = item;
7694 prev_item = item;
7696 /* Extract it */
7697 xpath_ctx->node = result->nodesetval->nodeTab[i];
7698 err = extract_CiRecord(context,
7699 (struct isds_credit_event **) (&item->data),
7700 xpath_ctx);
7701 if (err) goto leave;
7705 leave:
7706 if (!err) {
7707 isds_log(ILF_ISDS, ILL_DEBUG,
7708 _("DataBoxCreditInfo request processed by server successfully.\n"));
7710 if (err) {
7711 isds_list_free(history);
7712 if (NULL != email) zfree(*email)
7715 free(box_id_locale);
7716 xmlXPathFreeObject(result);
7717 xmlXPathFreeContext(xpath_ctx);
7718 xmlFreeDoc(response);
7720 #else /* not HAVE_LIBCURL */
7721 err = IE_NOTSUP;
7722 #endif
7724 return err;
7728 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
7729 * code, destroy response and log success.
7730 * @context is ISDS session context.
7731 * @service_name is name of SERVICE_DB_MANIPULATION service
7732 * @box_id is UTF-8 encoded box identifier as zero terminated string
7733 * @approval is optional external approval of box manipulation
7734 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7735 * NULL, if you don't care. */
7736 static isds_error build_send_manipulationdbid_request_check_drop_response(
7737 struct isds_ctx *context, const xmlChar *service_name,
7738 const xmlChar *box_id, const struct isds_approval *approval,
7739 xmlChar **refnumber) {
7740 isds_error err = IE_SUCCESS;
7741 #if HAVE_LIBCURL
7742 xmlDocPtr response = NULL;
7743 #endif
7745 if (!context) return IE_INVALID_CONTEXT;
7746 zfree(context->long_message);
7747 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
7749 #if HAVE_LIBCURL
7750 /* Check if connection is established */
7751 if (!context->curl) return IE_CONNECTION_CLOSED;
7753 /* Do request and check for success */
7754 err = build_send_dbid_request_check_response(context,
7755 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
7756 &response, refnumber);
7757 xmlFreeDoc(response);
7759 if (!err) {
7760 char *service_name_locale = _isds_utf82locale((char *) service_name);
7761 isds_log(ILF_ISDS, ILL_DEBUG,
7762 _("%s request processed by server successfully.\n"),
7763 service_name_locale);
7764 free(service_name_locale);
7766 #else /* not HAVE_LIBCURL */
7767 err = IE_NOTSUP;
7768 #endif
7770 return err;
7774 /* Switch box into state where box can receive commercial messages (off by
7775 * default)
7776 * @context is ISDS session context.
7777 * @box_id is UTF-8 encoded box identifier as zero terminated string
7778 * @allow is true for enable, false for disable commercial messages income
7779 * @approval is optional external approval of box manipulation
7780 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7781 * NULL, if you don't care. */
7782 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
7783 const char *box_id, const _Bool allow,
7784 const struct isds_approval *approval, char **refnumber) {
7785 return build_send_manipulationdbid_request_check_drop_response(context,
7786 (allow) ? BAD_CAST "SetOpenAddressing" :
7787 BAD_CAST "ClearOpenAddressing",
7788 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7792 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
7793 * message acceptance). This is just a box permission. Sender must apply
7794 * such role by sending each message.
7795 * @context is ISDS session context.
7796 * @box_id is UTF-8 encoded box identifier as zero terminated string
7797 * @allow is true for enable, false for disable OVM role permission
7798 * @approval is optional external approval of box manipulation
7799 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7800 * NULL, if you don't care. */
7801 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
7802 const char *box_id, const _Bool allow,
7803 const struct isds_approval *approval, char **refnumber) {
7804 return build_send_manipulationdbid_request_check_drop_response(context,
7805 (allow) ? BAD_CAST "SetEffectiveOVM" :
7806 BAD_CAST "ClearEffectiveOVM",
7807 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7811 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
7812 * code, destroy response and log success.
7813 * @context is ISDS session context.
7814 * @service_name is name of SERVICE_DB_MANIPULATION service
7815 * @owner is structure describing box
7816 * @approval is optional external approval of box manipulation
7817 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7818 * NULL, if you don't care. */
7819 static isds_error build_send_manipulationdbowner_request_check_drop_response(
7820 struct isds_ctx *context, const xmlChar *service_name,
7821 const struct isds_DbOwnerInfo *owner,
7822 const struct isds_approval *approval, xmlChar **refnumber) {
7823 isds_error err = IE_SUCCESS;
7824 #if HAVE_LIBCURL
7825 char *service_name_locale = NULL;
7826 xmlNodePtr request = NULL, db_owner_info;
7827 xmlNsPtr isds_ns = NULL;
7828 #endif
7831 if (!context) return IE_INVALID_CONTEXT;
7832 zfree(context->long_message);
7833 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
7835 #if HAVE_LIBCURL
7836 service_name_locale = _isds_utf82locale((char*)service_name);
7837 if (!service_name_locale) {
7838 err = IE_NOMEM;
7839 goto leave;
7842 /* Build request */
7843 request = xmlNewNode(NULL, service_name);
7844 if (!request) {
7845 isds_printf_message(context,
7846 _("Could not build %s request"), service_name_locale);
7847 err = IE_ERROR;
7848 goto leave;
7850 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7851 if(!isds_ns) {
7852 isds_log_message(context, _("Could not create ISDS name space"));
7853 err = IE_ERROR;
7854 goto leave;
7856 xmlSetNs(request, isds_ns);
7859 /* Add XSD:tOwnerInfoInput child*/
7860 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
7861 err = insert_DbOwnerInfo(context, owner, db_owner_info);
7862 if (err) goto leave;
7864 /* Add XSD:gExtApproval*/
7865 err = insert_GExtApproval(context, approval, request);
7866 if (err) goto leave;
7868 /* Send it to server and process response */
7869 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7870 service_name, &request, refnumber);
7872 leave:
7873 xmlFreeNode(request);
7874 free(service_name_locale);
7875 #else /* not HAVE_LIBCURL */
7876 err = IE_NOTSUP;
7877 #endif
7879 return err;
7883 /* Switch box accessibility state on request of box owner.
7884 * Despite the name, owner must do the request off-line. This function is
7885 * designed for such off-line meeting points (e.g. Czech POINT).
7886 * @context is ISDS session context.
7887 * @box identifies box to switch accessibility state.
7888 * @allow is true for making accessible, false to disallow access.
7889 * @approval is optional external approval of box manipulation
7890 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7891 * NULL, if you don't care. */
7892 isds_error isds_switch_box_accessibility_on_owner_request(
7893 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7894 const _Bool allow, const struct isds_approval *approval,
7895 char **refnumber) {
7896 return build_send_manipulationdbowner_request_check_drop_response(context,
7897 (allow) ? BAD_CAST "EnableOwnDataBox" :
7898 BAD_CAST "DisableOwnDataBox",
7899 box, approval, (xmlChar **) refnumber);
7903 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
7904 * date.
7905 * @context is ISDS session context.
7906 * @box identifies box to switch accessibility state.
7907 * @since is date since accessibility has been denied. This can be past too.
7908 * Only tm_year, tm_mon and tm_mday carry sane value.
7909 * @approval is optional external approval of box manipulation
7910 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7911 * NULL, if you don't care. */
7912 isds_error isds_disable_box_accessibility_externaly(
7913 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7914 const struct tm *since, const struct isds_approval *approval,
7915 char **refnumber) {
7916 isds_error err = IE_SUCCESS;
7917 #if HAVE_LIBCURL
7918 char *service_name_locale = NULL;
7919 xmlNodePtr request = NULL, node;
7920 xmlNsPtr isds_ns = NULL;
7921 xmlChar *string = NULL;
7922 #endif
7925 if (!context) return IE_INVALID_CONTEXT;
7926 zfree(context->long_message);
7927 if (!box || !since) return IE_INVAL;
7929 #if HAVE_LIBCURL
7930 /* Build request */
7931 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
7932 if (!request) {
7933 isds_printf_message(context,
7934 _("Could not build %s request"), "DisableDataBoxExternally");
7935 err = IE_ERROR;
7936 goto leave;
7938 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7939 if(!isds_ns) {
7940 isds_log_message(context, _("Could not create ISDS name space"));
7941 err = IE_ERROR;
7942 goto leave;
7944 xmlSetNs(request, isds_ns);
7947 /* Add @box identification */
7948 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7949 err = insert_DbOwnerInfo(context, box, node);
7950 if (err) goto leave;
7952 /* Add @since date */
7953 err = tm2datestring(since, &string);
7954 if(err) {
7955 isds_log_message(context,
7956 _("Could not convert `since' argument to ISO date string"));
7957 goto leave;
7959 INSERT_STRING(request, "dbOwnerDisableDate", string);
7960 zfree(string);
7962 /* Add @approval */
7963 err = insert_GExtApproval(context, approval, request);
7964 if (err) goto leave;
7966 /* Send it to server and process response */
7967 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7968 BAD_CAST "DisableDataBoxExternally", &request,
7969 (xmlChar **) refnumber);
7971 leave:
7972 free(string);
7973 xmlFreeNode(request);
7974 free(service_name_locale);
7975 #else /* not HAVE_LIBCURL */
7976 err = IE_NOTSUP;
7977 #endif
7979 return err;
7983 #if HAVE_LIBCURL
7984 /* Insert struct isds_message data (envelope (recipient data optional) and
7985 * documents into XML tree
7986 * @context is session context
7987 * @outgoing_message is libisds structure with message data
7988 * @create_message is XML CreateMessage or CreateMultipleMessage element
7989 * @process_recipient true for recipient data serialization, false for no
7990 * serialization */
7991 static isds_error insert_envelope_files(struct isds_ctx *context,
7992 const struct isds_message *outgoing_message, xmlNodePtr create_message,
7993 const _Bool process_recipient) {
7995 isds_error err = IE_SUCCESS;
7996 xmlNodePtr envelope, dm_files, node;
7997 xmlChar *string = NULL;
7999 if (!context) return IE_INVALID_CONTEXT;
8000 if (!outgoing_message || !create_message) return IE_INVAL;
8003 /* Build envelope */
8004 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8005 if (!envelope) {
8006 isds_printf_message(context, _("Could not add dmEnvelope child to "
8007 "%s element"), create_message->name);
8008 return IE_ERROR;
8011 if (!outgoing_message->envelope) {
8012 isds_log_message(context, _("Outgoing message is missing envelope"));
8013 err = IE_INVAL;
8014 goto leave;
8017 /* Insert optional message type */
8018 err = insert_message_type(context, outgoing_message->envelope->dmType,
8019 envelope);
8020 if (err) goto leave;
8022 INSERT_STRING(envelope, "dmSenderOrgUnit",
8023 outgoing_message->envelope->dmSenderOrgUnit);
8024 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8025 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8027 if (process_recipient) {
8028 if (!outgoing_message->envelope->dbIDRecipient) {
8029 isds_log_message(context,
8030 _("Outgoing message is missing recipient box identifier"));
8031 err = IE_INVAL;
8032 goto leave;
8034 INSERT_STRING(envelope, "dbIDRecipient",
8035 outgoing_message->envelope->dbIDRecipient);
8037 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8038 outgoing_message->envelope->dmRecipientOrgUnit);
8039 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8040 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8041 INSERT_STRING(envelope, "dmToHands",
8042 outgoing_message->envelope->dmToHands);
8045 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8046 "dmAnnotation");
8047 INSERT_STRING(envelope, "dmAnnotation",
8048 outgoing_message->envelope->dmAnnotation);
8050 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8051 0, 50, "dmRecipientRefNumber");
8052 INSERT_STRING(envelope, "dmRecipientRefNumber",
8053 outgoing_message->envelope->dmRecipientRefNumber);
8055 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8056 0, 50, "dmSenderRefNumber");
8057 INSERT_STRING(envelope, "dmSenderRefNumber",
8058 outgoing_message->envelope->dmSenderRefNumber);
8060 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8061 0, 50, "dmRecipientIdent");
8062 INSERT_STRING(envelope, "dmRecipientIdent",
8063 outgoing_message->envelope->dmRecipientIdent);
8065 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8066 0, 50, "dmSenderIdent");
8067 INSERT_STRING(envelope, "dmSenderIdent",
8068 outgoing_message->envelope->dmSenderIdent);
8070 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8071 outgoing_message->envelope->dmLegalTitleLaw, string);
8072 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8073 outgoing_message->envelope->dmLegalTitleYear, string);
8074 INSERT_STRING(envelope, "dmLegalTitleSect",
8075 outgoing_message->envelope->dmLegalTitleSect);
8076 INSERT_STRING(envelope, "dmLegalTitlePar",
8077 outgoing_message->envelope->dmLegalTitlePar);
8078 INSERT_STRING(envelope, "dmLegalTitlePoint",
8079 outgoing_message->envelope->dmLegalTitlePoint);
8081 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8082 outgoing_message->envelope->dmPersonalDelivery);
8083 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8084 outgoing_message->envelope->dmAllowSubstDelivery);
8086 /* ???: Should we require value for dbEffectiveOVM sender?
8087 * ISDS has default as true */
8088 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8089 INSERT_BOOLEAN(envelope, "dmOVM",
8090 outgoing_message->envelope->dmPublishOwnID);
8093 /* Append dmFiles */
8094 if (!outgoing_message->documents) {
8095 isds_log_message(context,
8096 _("Outgoing message is missing list of documents"));
8097 err = IE_INVAL;
8098 goto leave;
8100 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8101 if (!dm_files) {
8102 isds_printf_message(context, _("Could not add dmFiles child to "
8103 "%s element"), create_message->name);
8104 err = IE_ERROR;
8105 goto leave;
8108 /* Check for document hierarchy */
8109 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8110 if (err) goto leave;
8112 /* Process each document */
8113 for (struct isds_list *item =
8114 (struct isds_list *) outgoing_message->documents;
8115 item; item = item->next) {
8116 if (!item->data) {
8117 isds_log_message(context,
8118 _("List of documents contains empty item"));
8119 err = IE_INVAL;
8120 goto leave;
8122 /* FIXME: Check for dmFileMetaType and for document references.
8123 * Only first document can be of MAIN type */
8124 err = insert_document(context, (struct isds_document*) item->data,
8125 dm_files);
8127 if (err) goto leave;
8130 leave:
8131 free(string);
8132 return err;
8134 #endif /* HAVE_LIBCURL */
8137 /* Send a message via ISDS to a recipient
8138 * @context is session context
8139 * @outgoing_message is message to send; Some members are mandatory (like
8140 * dbIDRecipient), some are optional and some are irrelevant (especially data
8141 * about sender). Included pointer to isds_list documents must contain at
8142 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8143 * members will be filled with valid data from ISDS. Exact list of write
8144 * members is subject to change. Currently dmID is changed.
8145 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8146 isds_error isds_send_message(struct isds_ctx *context,
8147 struct isds_message *outgoing_message) {
8149 isds_error err = IE_SUCCESS;
8150 #if HAVE_LIBCURL
8151 xmlNsPtr isds_ns = NULL;
8152 xmlNodePtr request = NULL;
8153 xmlDocPtr response = NULL;
8154 xmlChar *code = NULL, *message = NULL;
8155 xmlXPathContextPtr xpath_ctx = NULL;
8156 xmlXPathObjectPtr result = NULL;
8157 /*_Bool message_is_complete = 0;*/
8158 #endif
8160 if (!context) return IE_INVALID_CONTEXT;
8161 zfree(context->long_message);
8162 if (!outgoing_message) return IE_INVAL;
8164 #if HAVE_LIBCURL
8165 /* Check if connection is established
8166 * TODO: This check should be done downstairs. */
8167 if (!context->curl) return IE_CONNECTION_CLOSED;
8170 /* Build CreateMessage request */
8171 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8172 if (!request) {
8173 isds_log_message(context,
8174 _("Could not build CreateMessage request"));
8175 return IE_ERROR;
8177 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8178 if(!isds_ns) {
8179 isds_log_message(context, _("Could not create ISDS name space"));
8180 xmlFreeNode(request);
8181 return IE_ERROR;
8183 xmlSetNs(request, isds_ns);
8185 /* Append envelope and files */
8186 err = insert_envelope_files(context, outgoing_message, request, 1);
8187 if (err) goto leave;
8190 /* Signal we can serialize message since now */
8191 /*message_is_complete = 1;*/
8194 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8196 /* Sent request */
8197 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8199 /* Don't' destroy request, we want to provide it to application later */
8201 if (err) {
8202 isds_log(ILF_ISDS, ILL_DEBUG,
8203 _("Processing ISDS response on CreateMessage "
8204 "request failed\n"));
8205 goto leave;
8208 /* Check for response status */
8209 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8210 &code, &message, NULL);
8211 if (err) {
8212 isds_log(ILF_ISDS, ILL_DEBUG,
8213 _("ISDS response on CreateMessage request "
8214 "is missing status\n"));
8215 goto leave;
8218 /* Request processed, but refused by server or server failed */
8219 if (xmlStrcmp(code, BAD_CAST "0000")) {
8220 char *box_id_locale =
8221 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8222 char *code_locale = _isds_utf82locale((char*)code);
8223 char *message_locale = _isds_utf82locale((char*)message);
8224 isds_log(ILF_ISDS, ILL_DEBUG,
8225 _("Server did not accept message for %s on CreateMessage "
8226 "request (code=%s, message=%s)\n"),
8227 box_id_locale, code_locale, message_locale);
8228 isds_log_message(context, message_locale);
8229 free(box_id_locale);
8230 free(code_locale);
8231 free(message_locale);
8232 err = IE_ISDS;
8233 goto leave;
8237 /* Extract data */
8238 xpath_ctx = xmlXPathNewContext(response);
8239 if (!xpath_ctx) {
8240 err = IE_ERROR;
8241 goto leave;
8243 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8244 err = IE_ERROR;
8245 goto leave;
8247 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8248 xpath_ctx);
8249 if (!result) {
8250 err = IE_ERROR;
8251 goto leave;
8253 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8254 isds_log_message(context, _("Missing CreateMessageResponse element"));
8255 err = IE_ISDS;
8256 goto leave;
8258 if (result->nodesetval->nodeNr > 1) {
8259 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8260 err = IE_ISDS;
8261 goto leave;
8263 xpath_ctx->node = result->nodesetval->nodeTab[0];
8264 xmlXPathFreeObject(result); result = NULL;
8266 if (outgoing_message->envelope->dmID) {
8267 free(outgoing_message->envelope->dmID);
8268 outgoing_message->envelope->dmID = NULL;
8270 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8271 if (!outgoing_message->envelope->dmID) {
8272 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8273 "but did not return assigned message ID\n"));
8276 leave:
8277 /* TODO: Serialize message into structure member raw */
8278 /* XXX: Each web service transport message in different format.
8279 * Therefore it's not possible to save them directly.
8280 * To save them, one must figure out common format.
8281 * We can leave it on application, or we can implement the ESS format. */
8282 /*if (message_is_complete) {
8283 if (outgoing_message->envelope->dmID) {
8285 /* Add assigned message ID as first child*/
8286 /*xmlNodePtr dmid_text = xmlNewText(
8287 (xmlChar *) outgoing_message->envelope->dmID);
8288 if (!dmid_text) goto serialization_failed;
8290 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8291 BAD_CAST "dmID");
8292 if (!dmid_element) {
8293 xmlFreeNode(dmid_text);
8294 goto serialization_failed;
8297 xmlNodePtr dmid_element_with_text =
8298 xmlAddChild(dmid_element, dmid_text);
8299 if (!dmid_element_with_text) {
8300 xmlFreeNode(dmid_element);
8301 xmlFreeNode(dmid_text);
8302 goto serialization_failed;
8305 node = xmlAddPrevSibling(envelope->childern,
8306 dmid_element_with_text);
8307 if (!node) {
8308 xmlFreeNodeList(dmid_element_with_text);
8309 goto serialization_failed;
8313 /* Serialize message with ID into raw */
8314 /*buffer = serialize_element(envelope)*/
8315 /* }
8317 serialization_failed:
8321 /* Clean up */
8322 xmlXPathFreeObject(result);
8323 xmlXPathFreeContext(xpath_ctx);
8325 free(code);
8326 free(message);
8327 xmlFreeDoc(response);
8328 xmlFreeNode(request);
8330 if (!err)
8331 isds_log(ILF_ISDS, ILL_DEBUG,
8332 _("CreateMessage request processed by server "
8333 "successfully.\n"));
8334 #else /* not HAVE_LIBCURL */
8335 err = IE_NOTSUP;
8336 #endif
8338 return err;
8342 /* Send a message via ISDS to a multiple recipients
8343 * @context is session context
8344 * @outgoing_message is message to send; Some members are mandatory,
8345 * some are optional and some are irrelevant (especially data
8346 * about sender). Data about recipient will be substituted by ISDS from
8347 * @copies. Included pointer to isds_list documents must
8348 * contain at least one document of FILEMETATYPE_MAIN.
8349 * @copies is list of isds_message_copy structures addressing all desired
8350 * recipients. This is read-write structure, some members will be filled with
8351 * valid data from ISDS (message IDs, error codes, error descriptions).
8352 * @return
8353 * ISDS_SUCCESS if all messages have been sent
8354 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8355 * succeeded messages can be identified by copies->data->error),
8356 * or other error code if something other goes wrong. */
8357 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8358 const struct isds_message *outgoing_message,
8359 struct isds_list *copies) {
8361 isds_error err = IE_SUCCESS;
8362 #if HAVE_LIBCURL
8363 isds_error append_err;
8364 xmlNsPtr isds_ns = NULL;
8365 xmlNodePtr request = NULL, recipients, recipient, node;
8366 struct isds_list *item;
8367 struct isds_message_copy *copy;
8368 xmlDocPtr response = NULL;
8369 xmlChar *code = NULL, *message = NULL;
8370 xmlXPathContextPtr xpath_ctx = NULL;
8371 xmlXPathObjectPtr result = NULL;
8372 xmlChar *string = NULL;
8373 int i;
8374 #endif
8376 if (!context) return IE_INVALID_CONTEXT;
8377 zfree(context->long_message);
8378 if (!outgoing_message || !copies) return IE_INVAL;
8380 #if HAVE_LIBCURL
8381 /* Check if connection is established
8382 * TODO: This check should be done downstairs. */
8383 if (!context->curl) return IE_CONNECTION_CLOSED;
8386 /* Build CreateMultipleMessage request */
8387 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
8388 if (!request) {
8389 isds_log_message(context,
8390 _("Could not build CreateMultipleMessage request"));
8391 return IE_ERROR;
8393 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8394 if(!isds_ns) {
8395 isds_log_message(context, _("Could not create ISDS name space"));
8396 xmlFreeNode(request);
8397 return IE_ERROR;
8399 xmlSetNs(request, isds_ns);
8402 /* Build recipients */
8403 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8404 if (!recipients) {
8405 isds_log_message(context, _("Could not add dmRecipients child to "
8406 "CreateMultipleMessage element"));
8407 xmlFreeNode(request);
8408 return IE_ERROR;
8411 /* Insert each recipient */
8412 for (item = copies; item; item = item->next) {
8413 copy = (struct isds_message_copy *) item->data;
8414 if (!copy) {
8415 isds_log_message(context,
8416 _("`copies' list item contains empty data"));
8417 err = IE_INVAL;
8418 goto leave;
8421 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
8422 if (!recipient) {
8423 isds_log_message(context, _("Could not add dmRecipient child to "
8424 "dmRecipients element"));
8425 err = IE_ERROR;
8426 goto leave;
8429 if (!copy->dbIDRecipient) {
8430 isds_log_message(context,
8431 _("Message copy is missing recipient box identifier"));
8432 err = IE_INVAL;
8433 goto leave;
8435 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
8436 INSERT_STRING(recipient, "dmRecipientOrgUnit",
8437 copy->dmRecipientOrgUnit);
8438 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
8439 copy->dmRecipientOrgUnitNum, string);
8440 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
8443 /* Append envelope and files */
8444 err = insert_envelope_files(context, outgoing_message, request, 0);
8445 if (err) goto leave;
8448 isds_log(ILF_ISDS, ILL_DEBUG,
8449 _("Sending CreateMultipleMessage request to ISDS\n"));
8451 /* Sent request */
8452 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8453 if (err) {
8454 isds_log(ILF_ISDS, ILL_DEBUG,
8455 _("Processing ISDS response on CreateMultipleMessage "
8456 "request failed\n"));
8457 goto leave;
8460 /* Check for response status */
8461 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8462 &code, &message, NULL);
8463 if (err) {
8464 isds_log(ILF_ISDS, ILL_DEBUG,
8465 _("ISDS response on CreateMultipleMessage request "
8466 "is missing status\n"));
8467 goto leave;
8470 /* Request processed, but some copies failed */
8471 if (!xmlStrcmp(code, BAD_CAST "0004")) {
8472 char *box_id_locale =
8473 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8474 char *code_locale = _isds_utf82locale((char*)code);
8475 char *message_locale = _isds_utf82locale((char*)message);
8476 isds_log(ILF_ISDS, ILL_DEBUG,
8477 _("Server did accept message for multiple recipients "
8478 "on CreateMultipleMessage request but delivery to "
8479 "some of them failed (code=%s, message=%s)\n"),
8480 box_id_locale, code_locale, message_locale);
8481 isds_log_message(context, message_locale);
8482 free(box_id_locale);
8483 free(code_locale);
8484 free(message_locale);
8485 err = IE_PARTIAL_SUCCESS;
8488 /* Request refused by server as whole */
8489 else if (xmlStrcmp(code, BAD_CAST "0000")) {
8490 char *box_id_locale =
8491 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8492 char *code_locale = _isds_utf82locale((char*)code);
8493 char *message_locale = _isds_utf82locale((char*)message);
8494 isds_log(ILF_ISDS, ILL_DEBUG,
8495 _("Server did not accept message for multiple recipients "
8496 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
8497 box_id_locale, code_locale, message_locale);
8498 isds_log_message(context, message_locale);
8499 free(box_id_locale);
8500 free(code_locale);
8501 free(message_locale);
8502 err = IE_ISDS;
8503 goto leave;
8507 /* Extract data */
8508 xpath_ctx = xmlXPathNewContext(response);
8509 if (!xpath_ctx) {
8510 err = IE_ERROR;
8511 goto leave;
8513 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8514 err = IE_ERROR;
8515 goto leave;
8517 result = xmlXPathEvalExpression(
8518 BAD_CAST "/isds:CreateMultipleMessageResponse"
8519 "/isds:dmMultipleStatus/isds:dmSingleStatus",
8520 xpath_ctx);
8521 if (!result) {
8522 err = IE_ERROR;
8523 goto leave;
8525 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8526 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
8527 err = IE_ISDS;
8528 goto leave;
8531 /* Extract message ID and delivery status for each copy */
8532 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
8533 item = item->next, i++) {
8534 copy = (struct isds_message_copy *) item->data;
8535 xpath_ctx->node = result->nodesetval->nodeTab[i];
8537 append_err = append_TMStatus(context, copy, xpath_ctx);
8538 if (append_err) {
8539 err = append_err;
8540 goto leave;
8543 if (item || i < result->nodesetval->nodeNr) {
8544 isds_printf_message(context, _("ISDS returned unexpected number of "
8545 "message copy delivery states: %d"),
8546 result->nodesetval->nodeNr);
8547 err = IE_ISDS;
8548 goto leave;
8552 leave:
8553 /* Clean up */
8554 free(string);
8555 xmlXPathFreeObject(result);
8556 xmlXPathFreeContext(xpath_ctx);
8558 free(code);
8559 free(message);
8560 xmlFreeDoc(response);
8561 xmlFreeNode(request);
8563 if (!err)
8564 isds_log(ILF_ISDS, ILL_DEBUG,
8565 _("CreateMultipleMessageResponse request processed by server "
8566 "successfully.\n"));
8567 #else /* not HAVE_LIBCURL */
8568 err = IE_NOTSUP;
8569 #endif
8571 return err;
8575 /* Get list of messages. This is common core for getting sent or received
8576 * messages.
8577 * Any criterion argument can be NULL, if you don't care about it.
8578 * @context is session context. Must not be NULL.
8579 * @outgoing_direction is true if you want list of outgoing messages,
8580 * it's false if you want incoming messages.
8581 * @from_time is minimal time and date of message sending inclusive.
8582 * @to_time is maximal time and date of message sending inclusive
8583 * @organization_unit_number is number of sender/recipient respectively.
8584 * @status_filter is bit field of isds_message_status values. Use special
8585 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8586 * all values, you can use bit-wise arithmetic if you want.)
8587 * @offset is index of first message we are interested in. First message is 1.
8588 * Set to 0 (or 1) if you don't care.
8589 * @number is maximal length of list you want to get as input value, outputs
8590 * number of messages matching these criteria. Can be NULL if you don't care
8591 * (applies to output value either).
8592 * @messages is automatically reallocated list of isds_message's. Be ware that
8593 * it returns only brief overview (envelope and some other fields) about each
8594 * message, not the complete message. FIXME: Specify exact fields.
8595 * The list is sorted by delivery time in ascending order.
8596 * Use NULL if you don't care about don't need the data (useful if you want to
8597 * know only the @number). If you provide &NULL, list will be allocated on
8598 * heap, if you provide pointer to non-NULL, list will be freed automatically
8599 * at first. Also in case of error the list will be NULLed.
8600 * @return IE_SUCCESS or appropriate error code. */
8601 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
8602 _Bool outgoing_direction,
8603 const struct timeval *from_time, const struct timeval *to_time,
8604 const long int *organization_unit_number,
8605 const unsigned int status_filter,
8606 const unsigned long int offset, unsigned long int *number,
8607 struct isds_list **messages) {
8609 isds_error err = IE_SUCCESS;
8610 #if HAVE_LIBCURL
8611 xmlNsPtr isds_ns = NULL;
8612 xmlNodePtr request = NULL, node;
8613 xmlDocPtr response = NULL;
8614 xmlChar *code = NULL, *message = NULL;
8615 xmlXPathContextPtr xpath_ctx = NULL;
8616 xmlXPathObjectPtr result = NULL;
8617 xmlChar *string = NULL;
8618 long unsigned int count = 0;
8619 #endif
8621 if (!context) return IE_INVALID_CONTEXT;
8622 zfree(context->long_message);
8624 /* Free former message list if any */
8625 if (messages) isds_list_free(messages);
8627 #if HAVE_LIBCURL
8628 /* Check if connection is established
8629 * TODO: This check should be done downstairs. */
8630 if (!context->curl) return IE_CONNECTION_CLOSED;
8632 /* Build GetListOf*Messages request */
8633 request = xmlNewNode(NULL,
8634 (outgoing_direction) ?
8635 BAD_CAST "GetListOfSentMessages" :
8636 BAD_CAST "GetListOfReceivedMessages"
8638 if (!request) {
8639 isds_log_message(context,
8640 (outgoing_direction) ?
8641 _("Could not build GetListOfSentMessages request") :
8642 _("Could not build GetListOfReceivedMessages request")
8644 return IE_ERROR;
8646 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8647 if(!isds_ns) {
8648 isds_log_message(context, _("Could not create ISDS name space"));
8649 xmlFreeNode(request);
8650 return IE_ERROR;
8652 xmlSetNs(request, isds_ns);
8655 if (from_time) {
8656 err = timeval2timestring(from_time, &string);
8657 if (err) goto leave;
8659 INSERT_STRING(request, "dmFromTime", string);
8660 free(string); string = NULL;
8662 if (to_time) {
8663 err = timeval2timestring(to_time, &string);
8664 if (err) goto leave;
8666 INSERT_STRING(request, "dmToTime", string);
8667 free(string); string = NULL;
8669 if (outgoing_direction) {
8670 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
8671 organization_unit_number, string);
8672 } else {
8673 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
8674 organization_unit_number, string);
8677 if (status_filter > MESSAGESTATE_ANY) {
8678 isds_printf_message(context,
8679 _("Invalid message state filter value: %ld"), status_filter);
8680 err = IE_INVAL;
8681 goto leave;
8683 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
8685 if (offset > 0 ) {
8686 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
8687 } else {
8688 INSERT_STRING(request, "dmOffset", "1");
8691 /* number 0 means no limit */
8692 if (number && *number == 0) {
8693 INSERT_STRING(request, "dmLimit", NULL);
8694 } else {
8695 INSERT_ULONGINT(request, "dmLimit", number, string);
8699 isds_log(ILF_ISDS, ILL_DEBUG,
8700 (outgoing_direction) ?
8701 _("Sending GetListOfSentMessages request to ISDS\n") :
8702 _("Sending GetListOfReceivedMessages request to ISDS\n")
8705 /* Sent request */
8706 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
8707 xmlFreeNode(request); request = NULL;
8709 if (err) {
8710 isds_log(ILF_ISDS, ILL_DEBUG,
8711 (outgoing_direction) ?
8712 _("Processing ISDS response on GetListOfSentMessages "
8713 "request failed\n") :
8714 _("Processing ISDS response on GetListOfReceivedMessages "
8715 "request failed\n")
8717 goto leave;
8720 /* Check for response status */
8721 err = isds_response_status(context, SERVICE_DM_INFO, response,
8722 &code, &message, NULL);
8723 if (err) {
8724 isds_log(ILF_ISDS, ILL_DEBUG,
8725 (outgoing_direction) ?
8726 _("ISDS response on GetListOfSentMessages request "
8727 "is missing status\n") :
8728 _("ISDS response on GetListOfReceivedMessages request "
8729 "is missing status\n")
8731 goto leave;
8734 /* Request processed, but nothing found */
8735 if (xmlStrcmp(code, BAD_CAST "0000")) {
8736 char *code_locale = _isds_utf82locale((char*)code);
8737 char *message_locale = _isds_utf82locale((char*)message);
8738 isds_log(ILF_ISDS, ILL_DEBUG,
8739 (outgoing_direction) ?
8740 _("Server refused GetListOfSentMessages request "
8741 "(code=%s, message=%s)\n") :
8742 _("Server refused GetListOfReceivedMessages request "
8743 "(code=%s, message=%s)\n"),
8744 code_locale, message_locale);
8745 isds_log_message(context, message_locale);
8746 free(code_locale);
8747 free(message_locale);
8748 err = IE_ISDS;
8749 goto leave;
8753 /* Extract data */
8754 xpath_ctx = xmlXPathNewContext(response);
8755 if (!xpath_ctx) {
8756 err = IE_ERROR;
8757 goto leave;
8759 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8760 err = IE_ERROR;
8761 goto leave;
8763 result = xmlXPathEvalExpression(
8764 (outgoing_direction) ?
8765 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
8766 "isds:dmRecords/isds:dmRecord" :
8767 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
8768 "isds:dmRecords/isds:dmRecord",
8769 xpath_ctx);
8770 if (!result) {
8771 err = IE_ERROR;
8772 goto leave;
8775 /* Fill output arguments in */
8776 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8777 struct isds_envelope *envelope;
8778 struct isds_list *item = NULL, *last_item = NULL;
8780 for (count = 0; count < result->nodesetval->nodeNr; count++) {
8781 /* Create new message */
8782 item = calloc(1, sizeof(*item));
8783 if (!item) {
8784 err = IE_NOMEM;
8785 goto leave;
8787 item->destructor = (void(*)(void**)) &isds_message_free;
8788 item->data = calloc(1, sizeof(struct isds_message));
8789 if (!item->data) {
8790 isds_list_free(&item);
8791 err = IE_NOMEM;
8792 goto leave;
8795 /* Extract envelope data */
8796 xpath_ctx->node = result->nodesetval->nodeTab[count];
8797 envelope = NULL;
8798 err = extract_DmRecord(context, &envelope, xpath_ctx);
8799 if (err) {
8800 isds_list_free(&item);
8801 goto leave;
8804 /* Attach extracted envelope */
8805 ((struct isds_message *) item->data)->envelope = envelope;
8807 /* Append new message into the list */
8808 if (!*messages) {
8809 *messages = last_item = item;
8810 } else {
8811 last_item->next = item;
8812 last_item = item;
8816 if (number) *number = count;
8818 leave:
8819 if (err) {
8820 isds_list_free(messages);
8823 free(string);
8824 xmlXPathFreeObject(result);
8825 xmlXPathFreeContext(xpath_ctx);
8827 free(code);
8828 free(message);
8829 xmlFreeDoc(response);
8830 xmlFreeNode(request);
8832 if (!err)
8833 isds_log(ILF_ISDS, ILL_DEBUG,
8834 (outgoing_direction) ?
8835 _("GetListOfSentMessages request processed by server "
8836 "successfully.\n") :
8837 _("GetListOfReceivedMessages request processed by server "
8838 "successfully.\n")
8840 #else /* not HAVE_LIBCURL */
8841 err = IE_NOTSUP;
8842 #endif
8843 return err;
8847 /* Get list of outgoing (already sent) messages.
8848 * Any criterion argument can be NULL, if you don't care about it.
8849 * @context is session context. Must not be NULL.
8850 * @from_time is minimal time and date of message sending inclusive.
8851 * @to_time is maximal time and date of message sending inclusive
8852 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
8853 * @status_filter is bit field of isds_message_status values. Use special
8854 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8855 * all values, you can use bit-wise arithmetic if you want.)
8856 * @offset is index of first message we are interested in. First message is 1.
8857 * Set to 0 (or 1) if you don't care.
8858 * @number is maximal length of list you want to get as input value, outputs
8859 * number of messages matching these criteria. Can be NULL if you don't care
8860 * (applies to output value either).
8861 * @messages is automatically reallocated list of isds_message's. Be ware that
8862 * it returns only brief overview (envelope and some other fields) about each
8863 * message, not the complete message. FIXME: Specify exact fields.
8864 * The list is sorted by delivery time in ascending order.
8865 * Use NULL if you don't care about the meta data (useful if you want to know
8866 * only the @number). If you provide &NULL, list will be allocated on heap,
8867 * if you provide pointer to non-NULL, list will be freed automatically at
8868 * first. Also in case of error the list will be NULLed.
8869 * @return IE_SUCCESS or appropriate error code. */
8870 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
8871 const struct timeval *from_time, const struct timeval *to_time,
8872 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
8873 const unsigned long int offset, unsigned long int *number,
8874 struct isds_list **messages) {
8876 return isds_get_list_of_messages(
8877 context, 1,
8878 from_time, to_time, dmSenderOrgUnitNum, status_filter,
8879 offset, number,
8880 messages);
8884 /* Get list of incoming (addressed to you) messages.
8885 * Any criterion argument can be NULL, if you don't care about it.
8886 * @context is session context. Must not be NULL.
8887 * @from_time is minimal time and date of message sending inclusive.
8888 * @to_time is maximal time and date of message sending inclusive
8889 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
8890 * @status_filter is bit field of isds_message_status values. Use special
8891 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8892 * all values, you can use bit-wise arithmetic if you want.)
8893 * @offset is index of first message we are interested in. First message is 1.
8894 * Set to 0 (or 1) if you don't care.
8895 * @number is maximal length of list you want to get as input value, outputs
8896 * number of messages matching these criteria. Can be NULL if you don't care
8897 * (applies to output value either).
8898 * @messages is automatically reallocated list of isds_message's. Be ware that
8899 * it returns only brief overview (envelope and some other fields) about each
8900 * message, not the complete message. FIXME: Specify exact fields.
8901 * Use NULL if you don't care about the meta data (useful if you want to know
8902 * only the @number). If you provide &NULL, list will be allocated on heap,
8903 * if you provide pointer to non-NULL, list will be freed automatically at
8904 * first. Also in case of error the list will be NULLed.
8905 * @return IE_SUCCESS or appropriate error code. */
8906 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
8907 const struct timeval *from_time, const struct timeval *to_time,
8908 const long int *dmRecipientOrgUnitNum,
8909 const unsigned int status_filter,
8910 const unsigned long int offset, unsigned long int *number,
8911 struct isds_list **messages) {
8913 return isds_get_list_of_messages(
8914 context, 0,
8915 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
8916 offset, number,
8917 messages);
8921 /* Get list of sent message state changes.
8922 * Any criterion argument can be NULL, if you don't care about it.
8923 * @context is session context. Must not be NULL.
8924 * @from_time is minimal time and date of status changes inclusive
8925 * @to_time is maximal time and date of status changes inclusive
8926 * @changed_states is automatically reallocated list of
8927 * isds_message_status_change's. If you provide &NULL, list will be allocated
8928 * on heap, if you provide pointer to non-NULL, list will be freed
8929 * automatically at first. Also in case of error the list will be NULLed.
8930 * XXX: The list item ordering is not specified.
8931 * XXX: Server provides only `recent' changes.
8932 * @return IE_SUCCESS or appropriate error code. */
8933 isds_error isds_get_list_of_sent_message_state_changes(
8934 struct isds_ctx *context,
8935 const struct timeval *from_time, const struct timeval *to_time,
8936 struct isds_list **changed_states) {
8938 isds_error err = IE_SUCCESS;
8939 #if HAVE_LIBCURL
8940 xmlNsPtr isds_ns = NULL;
8941 xmlNodePtr request = NULL, node;
8942 xmlDocPtr response = NULL;
8943 xmlXPathContextPtr xpath_ctx = NULL;
8944 xmlXPathObjectPtr result = NULL;
8945 xmlChar *string = NULL;
8946 long unsigned int count = 0;
8947 #endif
8949 if (!context) return IE_INVALID_CONTEXT;
8950 zfree(context->long_message);
8952 /* Free former message list if any */
8953 isds_list_free(changed_states);
8955 #if HAVE_LIBCURL
8956 /* Check if connection is established
8957 * TODO: This check should be done downstairs. */
8958 if (!context->curl) return IE_CONNECTION_CLOSED;
8960 /* Build GetMessageStateChanges request */
8961 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
8962 if (!request) {
8963 isds_log_message(context,
8964 _("Could not build GetMessageStateChanges request"));
8965 return IE_ERROR;
8967 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8968 if(!isds_ns) {
8969 isds_log_message(context, _("Could not create ISDS name space"));
8970 xmlFreeNode(request);
8971 return IE_ERROR;
8973 xmlSetNs(request, isds_ns);
8976 if (from_time) {
8977 err = timeval2timestring(from_time, &string);
8978 if (err) goto leave;
8980 INSERT_STRING(request, "dmFromTime", string);
8981 zfree(string);
8983 if (to_time) {
8984 err = timeval2timestring(to_time, &string);
8985 if (err) goto leave;
8987 INSERT_STRING(request, "dmToTime", string);
8988 zfree(string);
8991 /* Sent request */
8992 err = send_destroy_request_check_response(context,
8993 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
8994 &response, NULL, NULL);
8995 if (err) goto leave;
8998 /* Extract data */
8999 xpath_ctx = xmlXPathNewContext(response);
9000 if (!xpath_ctx) {
9001 err = IE_ERROR;
9002 goto leave;
9004 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9005 err = IE_ERROR;
9006 goto leave;
9008 result = xmlXPathEvalExpression(
9009 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9010 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9011 if (!result) {
9012 err = IE_ERROR;
9013 goto leave;
9016 /* Fill output arguments in */
9017 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9018 struct isds_list *item = NULL, *last_item = NULL;
9020 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9021 /* Create new status change */
9022 item = calloc(1, sizeof(*item));
9023 if (!item) {
9024 err = IE_NOMEM;
9025 goto leave;
9027 item->destructor =
9028 (void(*)(void**)) &isds_message_status_change_free;
9030 /* Extract message status change */
9031 xpath_ctx->node = result->nodesetval->nodeTab[count];
9032 err = extract_StateChangesRecord(context,
9033 (struct isds_message_status_change **) &item->data,
9034 xpath_ctx);
9035 if (err) {
9036 isds_list_free(&item);
9037 goto leave;
9040 /* Append new message status change into the list */
9041 if (!*changed_states) {
9042 *changed_states = last_item = item;
9043 } else {
9044 last_item->next = item;
9045 last_item = item;
9050 leave:
9051 if (err) {
9052 isds_list_free(changed_states);
9055 free(string);
9056 xmlXPathFreeObject(result);
9057 xmlXPathFreeContext(xpath_ctx);
9058 xmlFreeDoc(response);
9059 xmlFreeNode(request);
9061 if (!err)
9062 isds_log(ILF_ISDS, ILL_DEBUG,
9063 _("GetMessageStateChanges request processed by server "
9064 "successfully.\n"));
9065 #else /* not HAVE_LIBCURL */
9066 err = IE_NOTSUP;
9067 #endif
9068 return err;
9072 #if HAVE_LIBCURL
9073 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9074 * code
9075 * @context is session context
9076 * @service is ISDS WS service handler
9077 * @service_name is name of SERVICE_DM_OPERATIONS
9078 * @message_id is message ID to send as service argument to ISDS
9079 * @response is reallocated server SOAP body response as XML document
9080 * @raw_response is reallocated bit stream with response body. Use
9081 * NULL if you don't care
9082 * @raw_response_length is size of @raw_response in bytes
9083 * @code is reallocated ISDS status code
9084 * @status_message is reallocated ISDS status message
9085 * @return error coded from lower layer, context message will be set up
9086 * appropriately. */
9087 static isds_error build_send_check_message_request(struct isds_ctx *context,
9088 const isds_service service, const xmlChar *service_name,
9089 const char *message_id,
9090 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9091 xmlChar **code, xmlChar **status_message) {
9093 isds_error err = IE_SUCCESS;
9094 char *service_name_locale = NULL, *message_id_locale = NULL;
9095 xmlNodePtr request = NULL, node;
9096 xmlNsPtr isds_ns = NULL;
9098 if (!context) return IE_INVALID_CONTEXT;
9099 if (!service_name || !message_id) return IE_INVAL;
9100 if (!response || !code || !status_message) return IE_INVAL;
9101 if (!raw_response_length && raw_response) return IE_INVAL;
9103 /* Free output argument */
9104 xmlFreeDoc(*response); *response = NULL;
9105 if (raw_response) zfree(*raw_response);
9106 zfree(*code);
9107 zfree(*status_message);
9110 /* Check if connection is established
9111 * TODO: This check should be done downstairs. */
9112 if (!context->curl) return IE_CONNECTION_CLOSED;
9114 service_name_locale = _isds_utf82locale((char*)service_name);
9115 message_id_locale = _isds_utf82locale(message_id);
9116 if (!service_name_locale || !message_id_locale) {
9117 err = IE_NOMEM;
9118 goto leave;
9121 /* Build request */
9122 request = xmlNewNode(NULL, service_name);
9123 if (!request) {
9124 isds_printf_message(context,
9125 _("Could not build %s request for %s message ID"),
9126 service_name_locale, message_id_locale);
9127 err = IE_ERROR;
9128 goto leave;
9130 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9131 if(!isds_ns) {
9132 isds_log_message(context, _("Could not create ISDS name space"));
9133 err = IE_ERROR;
9134 goto leave;
9136 xmlSetNs(request, isds_ns);
9139 /* Add requested ID */
9140 err = validate_message_id_length(context, (xmlChar *) message_id);
9141 if (err) goto leave;
9142 INSERT_STRING(request, "dmID", message_id);
9145 isds_log(ILF_ISDS, ILL_DEBUG,
9146 _("Sending %s request for %s message ID to ISDS\n"),
9147 service_name_locale, message_id_locale);
9149 /* Send request */
9150 err = isds(context, service, request, response,
9151 raw_response, raw_response_length);
9152 xmlFreeNode(request); request = NULL;
9154 if (err) {
9155 isds_log(ILF_ISDS, ILL_DEBUG,
9156 _("Processing ISDS response on %s request failed\n"),
9157 service_name_locale);
9158 goto leave;
9161 /* Check for response status */
9162 err = isds_response_status(context, service, *response,
9163 code, status_message, NULL);
9164 if (err) {
9165 isds_log(ILF_ISDS, ILL_DEBUG,
9166 _("ISDS response on %s request is missing status\n"),
9167 service_name_locale);
9168 goto leave;
9171 /* Request processed, but nothing found */
9172 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9173 char *code_locale = _isds_utf82locale((char*) *code);
9174 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9175 isds_log(ILF_ISDS, ILL_DEBUG,
9176 _("Server refused %s request for %s message ID "
9177 "(code=%s, message=%s)\n"),
9178 service_name_locale, message_id_locale,
9179 code_locale, status_message_locale);
9180 isds_log_message(context, status_message_locale);
9181 free(code_locale);
9182 free(status_message_locale);
9183 err = IE_ISDS;
9184 goto leave;
9187 leave:
9188 free(message_id_locale);
9189 free(service_name_locale);
9190 xmlFreeNode(request);
9191 return err;
9195 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9196 * signed data and free ISDS response.
9197 * @context is session context
9198 * @message_id is UTF-8 encoded message ID for logging purpose
9199 * @response is parsed XML document. It will be freed and NULLed in the middle
9200 * of function run to save memory. This is not guaranteed in case of error.
9201 * @request_name is name of ISDS request used to construct response root
9202 * element name and for logging purpose.
9203 * @raw is reallocated output buffer with DER encoded CMS data
9204 * @raw_length is size of @raw buffer in bytes
9205 * @returns standard error codes, in case of error, @raw will be freed and
9206 * NULLed, @response sometimes. */
9207 static isds_error find_extract_signed_data_free_response(
9208 struct isds_ctx *context, const xmlChar *message_id,
9209 xmlDocPtr *response, const xmlChar *request_name,
9210 void **raw, size_t *raw_length) {
9212 isds_error err = IE_SUCCESS;
9213 char *xpath_expression = NULL;
9214 xmlXPathContextPtr xpath_ctx = NULL;
9215 xmlXPathObjectPtr result = NULL;
9216 char *encoded_structure = NULL;
9218 if (!context) return IE_INVALID_CONTEXT;
9219 if (!raw) return IE_INVAL;
9220 zfree(*raw);
9221 if (!message_id || !response || !*response || !request_name || !raw_length)
9222 return IE_INVAL;
9224 /* Build XPath expression */
9225 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9226 "Response/isds:dmSignature");
9227 if (!xpath_expression) return IE_NOMEM;
9229 /* Extract data */
9230 xpath_ctx = xmlXPathNewContext(*response);
9231 if (!xpath_ctx) {
9232 err = IE_ERROR;
9233 goto leave;
9235 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9236 err = IE_ERROR;
9237 goto leave;
9239 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9240 if (!result) {
9241 err = IE_ERROR;
9242 goto leave;
9244 /* Empty response */
9245 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9246 char *message_id_locale = _isds_utf82locale((char*) message_id);
9247 isds_printf_message(context,
9248 _("Server did not return any signed data for message ID `%s' "
9249 "on %s request"),
9250 message_id_locale, request_name);
9251 free(message_id_locale);
9252 err = IE_ISDS;
9253 goto leave;
9255 /* More responses */
9256 if (result->nodesetval->nodeNr > 1) {
9257 char *message_id_locale = _isds_utf82locale((char*) message_id);
9258 isds_printf_message(context,
9259 _("Server did return more 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 /* One response */
9267 xpath_ctx->node = result->nodesetval->nodeTab[0];
9269 /* Extract PKCS#7 structure */
9270 EXTRACT_STRING(".", encoded_structure);
9271 if (!encoded_structure) {
9272 isds_log_message(context, _("dmSignature element is empty"));
9275 /* Here we have delivery info as standalone CMS in encoded_structure.
9276 * We don't need any other data, free them: */
9277 xmlXPathFreeObject(result); result = NULL;
9278 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9279 xmlFreeDoc(*response); *response = NULL;
9282 /* Decode PKCS#7 to DER format */
9283 *raw_length = _isds_b64decode(encoded_structure, raw);
9284 if (*raw_length == (size_t) -1) {
9285 isds_log_message(context,
9286 _("Error while Base64-decoding PKCS#7 structure"));
9287 err = IE_ERROR;
9288 goto leave;
9291 leave:
9292 if (err) {
9293 zfree(*raw);
9294 raw_length = 0;
9297 free(encoded_structure);
9298 xmlXPathFreeObject(result);
9299 xmlXPathFreeContext(xpath_ctx);
9300 free(xpath_expression);
9302 return err;
9304 #endif /* HAVE_LIBCURL */
9307 /* Download incoming message envelope identified by ID.
9308 * @context is session context
9309 * @message_id is message identifier (you can get them from
9310 * isds_get_list_of_received_messages())
9311 * @message is automatically reallocated message retrieved from ISDS.
9312 * It will miss documents per se. Use isds_get_received_message(), if you are
9313 * interested in documents (content) too.
9314 * Returned hash and timestamp require documents to be verifiable. */
9315 isds_error isds_get_received_envelope(struct isds_ctx *context,
9316 const char *message_id, struct isds_message **message) {
9318 isds_error err = IE_SUCCESS;
9319 #if HAVE_LIBCURL
9320 xmlDocPtr response = NULL;
9321 xmlChar *code = NULL, *status_message = NULL;
9322 xmlXPathContextPtr xpath_ctx = NULL;
9323 xmlXPathObjectPtr result = NULL;
9324 #endif
9326 if (!context) return IE_INVALID_CONTEXT;
9327 zfree(context->long_message);
9329 /* Free former message if any */
9330 if (!message) return IE_INVAL;
9331 isds_message_free(message);
9333 #if HAVE_LIBCURL
9334 /* Do request and check for success */
9335 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9336 BAD_CAST "MessageEnvelopeDownload", message_id,
9337 &response, NULL, NULL, &code, &status_message);
9338 if (err) goto leave;
9340 /* Extract data */
9341 xpath_ctx = xmlXPathNewContext(response);
9342 if (!xpath_ctx) {
9343 err = IE_ERROR;
9344 goto leave;
9346 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9347 err = IE_ERROR;
9348 goto leave;
9350 result = xmlXPathEvalExpression(
9351 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9352 "isds:dmReturnedMessageEnvelope",
9353 xpath_ctx);
9354 if (!result) {
9355 err = IE_ERROR;
9356 goto leave;
9358 /* Empty response */
9359 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9360 char *message_id_locale = _isds_utf82locale((char*) message_id);
9361 isds_printf_message(context,
9362 _("Server did not return any envelope for ID `%s' "
9363 "on MessageEnvelopeDownload request"), message_id_locale);
9364 free(message_id_locale);
9365 err = IE_ISDS;
9366 goto leave;
9368 /* More envelops */
9369 if (result->nodesetval->nodeNr > 1) {
9370 char *message_id_locale = _isds_utf82locale((char*) message_id);
9371 isds_printf_message(context,
9372 _("Server did return more envelopes for ID `%s' "
9373 "on MessageEnvelopeDownload request"), message_id_locale);
9374 free(message_id_locale);
9375 err = IE_ISDS;
9376 goto leave;
9378 /* One message */
9379 xpath_ctx->node = result->nodesetval->nodeTab[0];
9381 /* Extract the envelope (= message without documents, hence 0) */
9382 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9383 if (err) goto leave;
9385 /* Save XML blob */
9386 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9387 &(*message)->raw_length);
9389 leave:
9390 if (err) {
9391 isds_message_free(message);
9394 xmlXPathFreeObject(result);
9395 xmlXPathFreeContext(xpath_ctx);
9397 free(code);
9398 free(status_message);
9399 if (!*message || !(*message)->xml) {
9400 xmlFreeDoc(response);
9403 if (!err)
9404 isds_log(ILF_ISDS, ILL_DEBUG,
9405 _("MessageEnvelopeDownload request processed by server "
9406 "successfully.\n")
9408 #else /* not HAVE_LIBCURL */
9409 err = IE_NOTSUP;
9410 #endif
9411 return err;
9415 /* Load delivery info of any format from buffer.
9416 * @context is session context
9417 * @raw_type advertises format of @buffer content. Only delivery info types
9418 * are accepted.
9419 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9420 * retrieve such data from message->raw after calling
9421 * isds_get_signed_delivery_info().
9422 * @length is length of buffer in bytes.
9423 * @message is automatically reallocated message parsed from @buffer.
9424 * @strategy selects how buffer will be attached into raw isds_message member.
9425 * */
9426 isds_error isds_load_delivery_info(struct isds_ctx *context,
9427 const isds_raw_type raw_type,
9428 const void *buffer, const size_t length,
9429 struct isds_message **message, const isds_buffer_strategy strategy) {
9431 isds_error err = IE_SUCCESS;
9432 message_ns_type message_ns;
9433 xmlDocPtr message_doc = NULL;
9434 xmlXPathContextPtr xpath_ctx = NULL;
9435 xmlXPathObjectPtr result = NULL;
9436 void *xml_stream = NULL;
9437 size_t xml_stream_length = 0;
9439 if (!context) return IE_INVALID_CONTEXT;
9440 zfree(context->long_message);
9441 if (!message) return IE_INVAL;
9442 isds_message_free(message);
9443 if (!buffer) return IE_INVAL;
9446 /* Select buffer format and extract XML from CMS*/
9447 switch (raw_type) {
9448 case RAWTYPE_DELIVERYINFO:
9449 message_ns = MESSAGE_NS_UNSIGNED;
9450 xml_stream = (void *) buffer;
9451 xml_stream_length = length;
9452 break;
9454 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
9455 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9456 xml_stream = (void *) buffer;
9457 xml_stream_length = length;
9458 break;
9460 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
9461 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9462 err = _isds_extract_cms_data(context, buffer, length,
9463 &xml_stream, &xml_stream_length);
9464 if (err) goto leave;
9465 break;
9467 default:
9468 isds_log_message(context, _("Bad raw delivery representation type"));
9469 return IE_INVAL;
9470 break;
9473 isds_log(ILF_ISDS, ILL_DEBUG,
9474 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
9475 xml_stream_length, xml_stream);
9477 /* Convert delivery info XML stream into XPath context */
9478 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9479 if (!message_doc) {
9480 err = IE_XML;
9481 goto leave;
9483 xpath_ctx = xmlXPathNewContext(message_doc);
9484 if (!xpath_ctx) {
9485 err = IE_ERROR;
9486 goto leave;
9488 /* XXX: Name spaces mangled for signed delivery info:
9489 * http://isds.czechpoint.cz/v20/delivery:
9491 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
9492 * <q:dmDelivery>
9493 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9494 * <p:dmID>170272</p:dmID>
9495 * ...
9496 * </p:dmDm>
9497 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9498 * ...
9499 * </q:dmEvents>...</q:dmEvents>
9500 * </q:dmDelivery>
9501 * </q:GetDeliveryInfoResponse>
9502 * */
9503 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
9504 err = IE_ERROR;
9505 goto leave;
9507 result = xmlXPathEvalExpression(
9508 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
9509 xpath_ctx);
9510 if (!result) {
9511 err = IE_ERROR;
9512 goto leave;
9514 /* Empty delivery info */
9515 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9516 isds_printf_message(context,
9517 _("XML document is not sisds:dmDelivery document"));
9518 err = IE_ISDS;
9519 goto leave;
9521 /* More delivery info's */
9522 if (result->nodesetval->nodeNr > 1) {
9523 isds_printf_message(context,
9524 _("XML document has more sisds:dmDelivery elements"));
9525 err = IE_ISDS;
9526 goto leave;
9528 /* One delivery info */
9529 xpath_ctx->node = result->nodesetval->nodeTab[0];
9531 /* Extract the envelope (= message without documents, hence 0).
9532 * XXX: extract_TReturnedMessage() can obtain attachments size,
9533 * but delivery info carries none. It's coded as option elements,
9534 * so it should work. */
9535 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9536 if (err) goto leave;
9538 /* Extract events */
9539 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
9540 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
9541 if (err) { err = IE_ERROR; goto leave; }
9542 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
9543 if (err) goto leave;
9545 /* Append raw CMS structure into message */
9546 (*message)->raw_type = raw_type;
9547 switch (strategy) {
9548 case BUFFER_DONT_STORE:
9549 break;
9550 case BUFFER_COPY:
9551 (*message)->raw = malloc(length);
9552 if (!(*message)->raw) {
9553 err = IE_NOMEM;
9554 goto leave;
9556 memcpy((*message)->raw, buffer, length);
9557 (*message)->raw_length = length;
9558 break;
9559 case BUFFER_MOVE:
9560 (*message)->raw = (void *) buffer;
9561 (*message)->raw_length = length;
9562 break;
9563 default:
9564 err = IE_ENUM;
9565 goto leave;
9568 leave:
9569 if (err) {
9570 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
9571 isds_message_free(message);
9574 xmlXPathFreeObject(result);
9575 xmlXPathFreeContext(xpath_ctx);
9576 if (!*message || !(*message)->xml) {
9577 xmlFreeDoc(message_doc);
9579 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9581 if (!err)
9582 isds_log(ILF_ISDS, ILL_DEBUG,
9583 _("Delivery info loaded successfully.\n"));
9584 return err;
9588 /* Download signed delivery info-sheet of given message identified by ID.
9589 * @context is session context
9590 * @message_id is message identifier (you can get them from
9591 * isds_get_list_of_{sent,received}_messages())
9592 * @message is automatically reallocated message retrieved from ISDS.
9593 * It will miss documents per se. Use isds_get_signed_received_message(),
9594 * if you are interested in documents (content). OTOH, only this function
9595 * can get list events message has gone through. */
9596 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
9597 const char *message_id, struct isds_message **message) {
9599 isds_error err = IE_SUCCESS;
9600 #if HAVE_LIBCURL
9601 xmlDocPtr response = NULL;
9602 xmlChar *code = NULL, *status_message = NULL;
9603 void *raw = NULL;
9604 size_t raw_length = 0;
9605 #endif
9607 if (!context) return IE_INVALID_CONTEXT;
9608 zfree(context->long_message);
9610 /* Free former message if any */
9611 if (!message) return IE_INVAL;
9612 isds_message_free(message);
9614 #if HAVE_LIBCURL
9615 /* Do request and check for success */
9616 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9617 BAD_CAST "GetSignedDeliveryInfo", message_id,
9618 &response, NULL, NULL, &code, &status_message);
9619 if (err) goto leave;
9621 /* Find signed delivery info, extract it into raw and maybe free
9622 * response */
9623 err = find_extract_signed_data_free_response(context,
9624 (xmlChar *)message_id, &response,
9625 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
9626 if (err) goto leave;
9628 /* Parse delivery info */
9629 err = isds_load_delivery_info(context,
9630 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
9631 message, BUFFER_MOVE);
9632 if (err) goto leave;
9634 raw = NULL;
9636 leave:
9637 if (err) {
9638 isds_message_free(message);
9641 free(raw);
9642 free(code);
9643 free(status_message);
9644 xmlFreeDoc(response);
9646 if (!err)
9647 isds_log(ILF_ISDS, ILL_DEBUG,
9648 _("GetSignedDeliveryInfo request processed by server "
9649 "successfully.\n")
9651 #else /* not HAVE_LIBCURL */
9652 err = IE_NOTSUP;
9653 #endif
9654 return err;
9658 /* Download delivery info-sheet of given message identified by ID.
9659 * @context is session context
9660 * @message_id is message identifier (you can get them from
9661 * isds_get_list_of_{sent,received}_messages())
9662 * @message is automatically reallocated message retrieved from ISDS.
9663 * It will miss documents per se. Use isds_get_received_message(), if you are
9664 * interested in documents (content). OTOH, only this function can get list
9665 * of events message has gone through. */
9666 isds_error isds_get_delivery_info(struct isds_ctx *context,
9667 const char *message_id, struct isds_message **message) {
9669 isds_error err = IE_SUCCESS;
9670 #if HAVE_LIBCURL
9671 xmlDocPtr response = NULL;
9672 xmlChar *code = NULL, *status_message = NULL;
9673 xmlNodePtr delivery_node = NULL;
9674 void *raw = NULL;
9675 size_t raw_length = 0;
9676 #endif
9678 if (!context) return IE_INVALID_CONTEXT;
9679 zfree(context->long_message);
9681 /* Free former message if any */
9682 if (!message) return IE_INVAL;
9683 isds_message_free(message);
9685 #if HAVE_LIBCURL
9686 /* Do request and check for success */
9687 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9688 BAD_CAST "GetDeliveryInfo", message_id,
9689 &response, NULL, NULL, &code, &status_message);
9690 if (err) goto leave;
9693 /* Serialize delivery info */
9694 delivery_node = xmlDocGetRootElement(response);
9695 if (!delivery_node) {
9696 char *message_id_locale = _isds_utf82locale((char*) message_id);
9697 isds_printf_message(context,
9698 _("Server did not return any delivery info for ID `%s' "
9699 "on GetDeliveryInfo request"), message_id_locale);
9700 free(message_id_locale);
9701 err = IE_ISDS;
9702 goto leave;
9704 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
9705 if (err) goto leave;
9707 /* Parse delivery info */
9708 /* TODO: Here we parse the response second time. We could single delivery
9709 * parser from isds_load_delivery_info() to make things faster. */
9710 err = isds_load_delivery_info(context,
9711 RAWTYPE_DELIVERYINFO, raw, raw_length,
9712 message, BUFFER_MOVE);
9713 if (err) goto leave;
9715 raw = NULL;
9718 leave:
9719 if (err) {
9720 isds_message_free(message);
9723 free(raw);
9724 free(code);
9725 free(status_message);
9726 xmlFreeDoc(response);
9728 if (!err)
9729 isds_log(ILF_ISDS, ILL_DEBUG,
9730 _("GetDeliveryInfo request processed by server "
9731 "successfully.\n")
9733 #else /* not HAVE_LIBCURL */
9734 err = IE_NOTSUP;
9735 #endif
9736 return err;
9740 /* Download incoming message identified by ID.
9741 * @context is session context
9742 * @message_id is message identifier (you can get them from
9743 * isds_get_list_of_received_messages())
9744 * @message is automatically reallocated message retrieved from ISDS */
9745 isds_error isds_get_received_message(struct isds_ctx *context,
9746 const char *message_id, struct isds_message **message) {
9748 isds_error err = IE_SUCCESS;
9749 #if HAVE_LIBCURL
9750 xmlDocPtr response = NULL;
9751 void *xml_stream = NULL;
9752 size_t xml_stream_length;
9753 xmlChar *code = NULL, *status_message = NULL;
9754 xmlXPathContextPtr xpath_ctx = NULL;
9755 xmlXPathObjectPtr result = NULL;
9756 char *phys_path = NULL;
9757 size_t phys_start, phys_end;
9758 #endif
9760 if (!context) return IE_INVALID_CONTEXT;
9761 zfree(context->long_message);
9763 /* Free former message if any */
9764 if (NULL == message) return IE_INVAL;
9765 if (message) isds_message_free(message);
9767 #if HAVE_LIBCURL
9768 /* Do request and check for success */
9769 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
9770 BAD_CAST "MessageDownload", message_id,
9771 &response, &xml_stream, &xml_stream_length,
9772 &code, &status_message);
9773 if (err) goto leave;
9775 /* Extract data */
9776 xpath_ctx = xmlXPathNewContext(response);
9777 if (!xpath_ctx) {
9778 err = IE_ERROR;
9779 goto leave;
9781 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9782 err = IE_ERROR;
9783 goto leave;
9785 result = xmlXPathEvalExpression(
9786 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
9787 xpath_ctx);
9788 if (!result) {
9789 err = IE_ERROR;
9790 goto leave;
9792 /* Empty response */
9793 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9794 char *message_id_locale = _isds_utf82locale((char*) message_id);
9795 isds_printf_message(context,
9796 _("Server did not return any message for ID `%s' "
9797 "on MessageDownload request"), message_id_locale);
9798 free(message_id_locale);
9799 err = IE_ISDS;
9800 goto leave;
9802 /* More messages */
9803 if (result->nodesetval->nodeNr > 1) {
9804 char *message_id_locale = _isds_utf82locale((char*) message_id);
9805 isds_printf_message(context,
9806 _("Server did return more messages for ID `%s' "
9807 "on MessageDownload request"), message_id_locale);
9808 free(message_id_locale);
9809 err = IE_ISDS;
9810 goto leave;
9812 /* One message */
9813 xpath_ctx->node = result->nodesetval->nodeTab[0];
9815 /* Extract the message */
9816 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
9817 if (err) goto leave;
9819 /* Locate raw XML blob */
9820 phys_path = strdup(
9821 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
9822 PHYSXML_ELEMENT_SEPARATOR
9823 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
9824 PHYSXML_ELEMENT_SEPARATOR
9825 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
9827 if (!phys_path) {
9828 err = IE_NOMEM;
9829 goto leave;
9831 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
9832 phys_path, &phys_start, &phys_end);
9833 zfree(phys_path);
9834 if (err) {
9835 isds_log_message(context,
9836 _("Substring with isds:MessageDownloadResponse element "
9837 "could not be located in raw SOAP message"));
9838 goto leave;
9840 /* Save XML blob */
9841 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9842 &(*message)->raw_length);*/
9843 /* TODO: Store name space declarations from ancestors */
9844 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
9845 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
9846 (*message)->raw_length = phys_end - phys_start + 1;
9847 (*message)->raw = malloc((*message)->raw_length);
9848 if (!(*message)->raw) {
9849 err = IE_NOMEM;
9850 goto leave;
9852 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
9855 leave:
9856 if (err) {
9857 isds_message_free(message);
9860 free(phys_path);
9862 xmlXPathFreeObject(result);
9863 xmlXPathFreeContext(xpath_ctx);
9865 free(code);
9866 free(status_message);
9867 free(xml_stream);
9868 if (!*message || !(*message)->xml) {
9869 xmlFreeDoc(response);
9872 if (!err)
9873 isds_log(ILF_ISDS, ILL_DEBUG,
9874 _("MessageDownload request processed by server "
9875 "successfully.\n")
9877 #else /* not HAVE_LIBCURL */
9878 err = IE_NOTSUP;
9879 #endif
9880 return err;
9884 /* Load message of any type from buffer.
9885 * @context is session context
9886 * @raw_type defines content type of @buffer. Only message types are allowed.
9887 * @buffer is message raw representation. Format (CMS, plain signed,
9888 * message direction) is defined in @raw_type. You can retrieve such data
9889 * from message->raw after calling isds_get_[signed]{received,sent}_message().
9890 * @length is length of buffer in bytes.
9891 * @message is automatically reallocated message parsed from @buffer.
9892 * @strategy selects how buffer will be attached into raw isds_message member.
9893 * */
9894 isds_error isds_load_message(struct isds_ctx *context,
9895 const isds_raw_type raw_type, const void *buffer, const size_t length,
9896 struct isds_message **message, const isds_buffer_strategy strategy) {
9898 isds_error err = IE_SUCCESS;
9899 void *xml_stream = NULL;
9900 size_t xml_stream_length = 0;
9901 message_ns_type message_ns;
9902 xmlDocPtr message_doc = NULL;
9903 xmlXPathContextPtr xpath_ctx = NULL;
9904 xmlXPathObjectPtr result = NULL;
9906 if (!context) return IE_INVALID_CONTEXT;
9907 zfree(context->long_message);
9908 if (!message) return IE_INVAL;
9909 isds_message_free(message);
9910 if (!buffer) return IE_INVAL;
9913 /* Select buffer format and extract XML from CMS*/
9914 switch (raw_type) {
9915 case RAWTYPE_INCOMING_MESSAGE:
9916 message_ns = MESSAGE_NS_UNSIGNED;
9917 xml_stream = (void *) buffer;
9918 xml_stream_length = length;
9919 break;
9921 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
9922 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9923 xml_stream = (void *) buffer;
9924 xml_stream_length = length;
9925 break;
9927 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
9928 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9929 err = _isds_extract_cms_data(context, buffer, length,
9930 &xml_stream, &xml_stream_length);
9931 if (err) goto leave;
9932 break;
9934 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
9935 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9936 xml_stream = (void *) buffer;
9937 xml_stream_length = length;
9938 break;
9940 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
9941 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9942 err = _isds_extract_cms_data(context, buffer, length,
9943 &xml_stream, &xml_stream_length);
9944 if (err) goto leave;
9945 break;
9947 default:
9948 isds_log_message(context, _("Bad raw message representation type"));
9949 return IE_INVAL;
9950 break;
9953 isds_log(ILF_ISDS, ILL_DEBUG,
9954 _("Loading message:\n%.*s\nEnd of message\n"),
9955 xml_stream_length, xml_stream);
9957 /* Convert messages XML stream into XPath context */
9958 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9959 if (!message_doc) {
9960 err = IE_XML;
9961 goto leave;
9963 xpath_ctx = xmlXPathNewContext(message_doc);
9964 if (!xpath_ctx) {
9965 err = IE_ERROR;
9966 goto leave;
9968 /* XXX: Standard name space for unsigned incoming direction:
9969 * http://isds.czechpoint.cz/v20/
9971 * XXX: Name spaces mangled for signed outgoing direction:
9972 * http://isds.czechpoint.cz/v20/SentMessage:
9974 * <q:MessageDownloadResponse
9975 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
9976 * <q:dmReturnedMessage>
9977 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9978 * <p:dmID>151916</p:dmID>
9979 * ...
9980 * </p:dmDm>
9981 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9982 * ...
9983 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9984 * </q:dmReturnedMessage>
9985 * </q:MessageDownloadResponse>
9987 * XXX: Name spaces mangled for signed incoming direction:
9988 * http://isds.czechpoint.cz/v20/message:
9990 * <q:MessageDownloadResponse
9991 * xmlns:q="http://isds.czechpoint.cz/v20/message">
9992 * <q:dmReturnedMessage>
9993 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9994 * <p:dmID>151916</p:dmID>
9995 * ...
9996 * </p:dmDm>
9997 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9998 * ...
9999 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10000 * </q:dmReturnedMessage>
10001 * </q:MessageDownloadResponse>
10003 * Stupidity of ISDS developers is unlimited */
10004 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10005 err = IE_ERROR;
10006 goto leave;
10008 result = xmlXPathEvalExpression(
10009 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10010 xpath_ctx);
10011 if (!result) {
10012 err = IE_ERROR;
10013 goto leave;
10015 /* Empty message */
10016 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10017 isds_printf_message(context,
10018 _("XML document does not contain "
10019 "sisds:dmReturnedMessage element"));
10020 err = IE_ISDS;
10021 goto leave;
10023 /* More messages */
10024 if (result->nodesetval->nodeNr > 1) {
10025 isds_printf_message(context,
10026 _("XML document has more sisds:dmReturnedMessage elements"));
10027 err = IE_ISDS;
10028 goto leave;
10030 /* One message */
10031 xpath_ctx->node = result->nodesetval->nodeTab[0];
10033 /* Extract the message */
10034 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10035 if (err) goto leave;
10037 /* Append raw buffer into message */
10038 (*message)->raw_type = raw_type;
10039 switch (strategy) {
10040 case BUFFER_DONT_STORE:
10041 break;
10042 case BUFFER_COPY:
10043 (*message)->raw = malloc(length);
10044 if (!(*message)->raw) {
10045 err = IE_NOMEM;
10046 goto leave;
10048 memcpy((*message)->raw, buffer, length);
10049 (*message)->raw_length = length;
10050 break;
10051 case BUFFER_MOVE:
10052 (*message)->raw = (void *) buffer;
10053 (*message)->raw_length = length;
10054 break;
10055 default:
10056 err = IE_ENUM;
10057 goto leave;
10061 leave:
10062 if (err) {
10063 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10064 isds_message_free(message);
10067 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10068 xmlXPathFreeObject(result);
10069 xmlXPathFreeContext(xpath_ctx);
10070 if (!*message || !(*message)->xml) {
10071 xmlFreeDoc(message_doc);
10074 if (!err)
10075 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10076 return err;
10080 /* Determine type of raw message or delivery info according some heuristics.
10081 * It does not validate the raw blob.
10082 * @context is session context
10083 * @raw_type returns content type of @buffer. Valid only if exit code of this
10084 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10085 * reallocated memory.
10086 * @buffer is message raw representation.
10087 * @length is length of buffer in bytes. */
10088 isds_error isds_guess_raw_type(struct isds_ctx *context,
10089 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10090 isds_error err;
10091 void *xml_stream = NULL;
10092 size_t xml_stream_length = 0;
10093 xmlDocPtr document = NULL;
10094 xmlNodePtr root = NULL;
10096 if (!context) return IE_INVALID_CONTEXT;
10097 zfree(context->long_message);
10098 if (length == 0 || !buffer) return IE_INVAL;
10099 if (!raw_type) return IE_INVAL;
10101 /* Try CMS */
10102 err = _isds_extract_cms_data(context, buffer, length,
10103 &xml_stream, &xml_stream_length);
10104 if (err) {
10105 xml_stream = (void *) buffer;
10106 xml_stream_length = (size_t) length;
10107 err = IE_SUCCESS;
10110 /* Try XML */
10111 document = xmlParseMemory(xml_stream, xml_stream_length);
10112 if (!document) {
10113 isds_printf_message(context,
10114 _("Could not parse data as XML document"));
10115 err = IE_NOTSUP;
10116 goto leave;
10119 /* Get root element */
10120 root = xmlDocGetRootElement(document);
10121 if (!root) {
10122 isds_printf_message(context,
10123 _("XML document is missing root element"));
10124 err = IE_XML;
10125 goto leave;
10128 if (!root->ns || !root->ns->href) {
10129 isds_printf_message(context,
10130 _("Root element does not belong to any name space"));
10131 err = IE_NOTSUP;
10132 goto leave;
10135 /* Test name space */
10136 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10137 if (xml_stream == buffer)
10138 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10139 else
10140 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10141 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10142 if (xml_stream == buffer)
10143 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10144 else
10145 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10146 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10147 if (xml_stream == buffer)
10148 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10149 else
10150 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10151 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10152 if (xml_stream != buffer) {
10153 isds_printf_message(context,
10154 _("Document in ISDS name space is encapsulated into CMS" ));
10155 err = IE_NOTSUP;
10156 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10157 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10158 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10159 *raw_type = RAWTYPE_DELIVERYINFO;
10160 else {
10161 isds_printf_message(context,
10162 _("Unknown root element in ISDS name space"));
10163 err = IE_NOTSUP;
10165 } else {
10166 isds_printf_message(context,
10167 _("Unknown name space"));
10168 err = IE_NOTSUP;
10171 leave:
10172 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10173 xmlFreeDoc(document);
10174 return err;
10178 /* Download signed incoming/outgoing message identified by ID.
10179 * @context is session context
10180 * @output is true for outgoing message, false for incoming message
10181 * @message_id is message identifier (you can get them from
10182 * isds_get_list_of_{sent,received}_messages())
10183 * @message is automatically reallocated message retrieved from ISDS. The raw
10184 * member will be filled with PKCS#7 structure in DER format. */
10185 static isds_error isds_get_signed_message(struct isds_ctx *context,
10186 const _Bool outgoing, const char *message_id,
10187 struct isds_message **message) {
10189 isds_error err = IE_SUCCESS;
10190 #if HAVE_LIBCURL
10191 xmlDocPtr response = NULL;
10192 xmlChar *code = NULL, *status_message = NULL;
10193 xmlXPathContextPtr xpath_ctx = NULL;
10194 xmlXPathObjectPtr result = NULL;
10195 char *encoded_structure = NULL;
10196 void *raw = NULL;
10197 size_t raw_length = 0;
10198 #endif
10200 if (!context) return IE_INVALID_CONTEXT;
10201 zfree(context->long_message);
10202 if (!message) return IE_INVAL;
10203 isds_message_free(message);
10205 #if HAVE_LIBCURL
10206 /* Do request and check for success */
10207 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10208 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10209 BAD_CAST "SignedMessageDownload",
10210 message_id, &response, NULL, NULL, &code, &status_message);
10211 if (err) goto leave;
10213 /* Find signed message, extract it into raw and maybe free
10214 * response */
10215 err = find_extract_signed_data_free_response(context,
10216 (xmlChar *)message_id, &response,
10217 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10218 BAD_CAST "SignedMessageDownload",
10219 &raw, &raw_length);
10220 if (err) goto leave;
10222 /* Parse message */
10223 err = isds_load_message(context,
10224 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10225 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10226 raw, raw_length, message, BUFFER_MOVE);
10227 if (err) goto leave;
10229 raw = NULL;
10231 leave:
10232 if (err) {
10233 isds_message_free(message);
10236 free(encoded_structure);
10237 xmlXPathFreeObject(result);
10238 xmlXPathFreeContext(xpath_ctx);
10239 free(raw);
10241 free(code);
10242 free(status_message);
10243 xmlFreeDoc(response);
10245 if (!err)
10246 isds_log(ILF_ISDS, ILL_DEBUG,
10247 (outgoing) ?
10248 _("SignedSentMessageDownload request processed by server "
10249 "successfully.\n") :
10250 _("SignedMessageDownload request processed by server "
10251 "successfully.\n")
10253 #else /* not HAVE_LIBCURL */
10254 err = IE_NOTSUP;
10255 #endif
10256 return err;
10260 /* Download signed incoming message identified by ID.
10261 * @context is session context
10262 * @message_id is message identifier (you can get them from
10263 * isds_get_list_of_received_messages())
10264 * @message is automatically reallocated message retrieved from ISDS. The raw
10265 * member will be filled with PKCS#7 structure in DER format. */
10266 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10267 const char *message_id, struct isds_message **message) {
10268 return isds_get_signed_message(context, 0, message_id, message);
10272 /* Download signed outgoing message identified by ID.
10273 * @context is session context
10274 * @message_id is message identifier (you can get them from
10275 * isds_get_list_of_sent_messages())
10276 * @message is automatically reallocated message retrieved from ISDS. The raw
10277 * member will be filled with PKCS#7 structure in DER format. */
10278 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10279 const char *message_id, struct isds_message **message) {
10280 return isds_get_signed_message(context, 1, message_id, message);
10284 /* Get type and name of user who sent a message identified by ID.
10285 * @context is session context
10286 * @message_id is message identifier
10287 * @sender_type is pointer to automatically allocated type of sender detected
10288 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10289 * library or to the server, NULL will be returned. Pass NULL if you don't
10290 * care about it.
10291 * @raw_sender_type is automatically reallocated UTF-8 string describing
10292 * sender type or NULL if not known to server. Pass NULL if you don't care.
10293 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10294 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10295 isds_error isds_get_message_sender(struct isds_ctx *context,
10296 const char *message_id, isds_sender_type **sender_type,
10297 char **raw_sender_type, char **sender_name) {
10298 isds_error err = IE_SUCCESS;
10299 #if HAVE_LIBCURL
10300 xmlDocPtr response = NULL;
10301 xmlChar *code = NULL, *status_message = NULL;
10302 xmlXPathContextPtr xpath_ctx = NULL;
10303 xmlXPathObjectPtr result = NULL;
10304 char *type_string = NULL;
10305 #endif
10307 if (!context) return IE_INVALID_CONTEXT;
10308 zfree(context->long_message);
10309 if (sender_type) zfree(*sender_type);
10310 if (raw_sender_type) zfree(*raw_sender_type);
10311 if (sender_name) zfree(*sender_name);
10312 if (!message_id) return IE_INVAL;
10314 #if HAVE_LIBCURL
10315 /* Do request and check for success */
10316 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10317 BAD_CAST "GetMessageAuthor",
10318 message_id, &response, NULL, NULL, &code, &status_message);
10319 if (err) goto leave;
10321 /* Extract data */
10322 xpath_ctx = xmlXPathNewContext(response);
10323 if (!xpath_ctx) {
10324 err = IE_ERROR;
10325 goto leave;
10327 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10328 err = IE_ERROR;
10329 goto leave;
10331 result = xmlXPathEvalExpression(
10332 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10333 if (!result) {
10334 err = IE_ERROR;
10335 goto leave;
10337 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10338 isds_log_message(context,
10339 _("Missing GetMessageAuthorResponse element"));
10340 err = IE_ISDS;
10341 goto leave;
10343 if (result->nodesetval->nodeNr > 1) {
10344 isds_log_message(context,
10345 _("Multiple GetMessageAuthorResponse element"));
10346 err = IE_ISDS;
10347 goto leave;
10349 xpath_ctx->node = result->nodesetval->nodeTab[0];
10350 xmlXPathFreeObject(result); result = NULL;
10352 /* Fill output arguments in */
10353 EXTRACT_STRING("isds:userType", type_string);
10354 if (NULL != type_string) {
10355 if (NULL != sender_type) {
10356 *sender_type = calloc(1, sizeof(**sender_type));
10357 if (NULL == *sender_type) {
10358 err = IE_NOMEM;
10359 goto leave;
10362 err = string2isds_sender_type((xmlChar *)type_string,
10363 *sender_type);
10364 if (err) {
10365 zfree(*sender_type);
10366 if (err == IE_ENUM) {
10367 err = IE_SUCCESS;
10368 char *type_string_locale = _isds_utf82locale(type_string);
10369 isds_log(ILF_ISDS, ILL_WARNING,
10370 _("Unknown isds:userType value: %s"),
10371 type_string_locale);
10372 free(type_string_locale);
10377 if (NULL != sender_name)
10378 EXTRACT_STRING("isds:authorName", *sender_name);
10380 leave:
10381 if (err) {
10382 if (NULL != sender_type) zfree(*sender_type);
10383 zfree(type_string);
10384 if (NULL != sender_name) zfree(*sender_name);
10386 if (NULL != raw_sender_type) *raw_sender_type = type_string;
10388 xmlXPathFreeObject(result);
10389 xmlXPathFreeContext(xpath_ctx);
10391 free(code);
10392 free(status_message);
10393 xmlFreeDoc(response);
10395 if (!err)
10396 isds_log(ILF_ISDS, ILL_DEBUG,
10397 _("GetMessageAuthor request processed by server "
10398 "successfully.\n"));
10399 #else /* not HAVE_LIBCURL */
10400 err = IE_NOTSUP;
10401 #endif
10402 return err;
10406 /* Retrieve hash of message identified by ID stored in ISDS.
10407 * @context is session context
10408 * @message_id is message identifier
10409 * @hash is automatically reallocated message hash downloaded from ISDS.
10410 * Message must exist in system and must not be deleted. */
10411 isds_error isds_download_message_hash(struct isds_ctx *context,
10412 const char *message_id, struct isds_hash **hash) {
10414 isds_error err = IE_SUCCESS;
10415 #if HAVE_LIBCURL
10416 xmlDocPtr response = NULL;
10417 xmlChar *code = NULL, *status_message = NULL;
10418 xmlXPathContextPtr xpath_ctx = NULL;
10419 xmlXPathObjectPtr result = NULL;
10420 #endif
10422 if (!context) return IE_INVALID_CONTEXT;
10423 zfree(context->long_message);
10425 isds_hash_free(hash);
10427 #if HAVE_LIBCURL
10428 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10429 BAD_CAST "VerifyMessage", message_id,
10430 &response, NULL, NULL, &code, &status_message);
10431 if (err) goto leave;
10434 /* Extract data */
10435 xpath_ctx = xmlXPathNewContext(response);
10436 if (!xpath_ctx) {
10437 err = IE_ERROR;
10438 goto leave;
10440 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10441 err = IE_ERROR;
10442 goto leave;
10444 result = xmlXPathEvalExpression(
10445 BAD_CAST "/isds:VerifyMessageResponse",
10446 xpath_ctx);
10447 if (!result) {
10448 err = IE_ERROR;
10449 goto leave;
10451 /* Empty response */
10452 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10453 char *message_id_locale = _isds_utf82locale((char*) message_id);
10454 isds_printf_message(context,
10455 _("Server did not return any response for ID `%s' "
10456 "on VerifyMessage request"), message_id_locale);
10457 free(message_id_locale);
10458 err = IE_ISDS;
10459 goto leave;
10461 /* More responses */
10462 if (result->nodesetval->nodeNr > 1) {
10463 char *message_id_locale = _isds_utf82locale((char*) message_id);
10464 isds_printf_message(context,
10465 _("Server did return more responses for ID `%s' "
10466 "on VerifyMessage request"), message_id_locale);
10467 free(message_id_locale);
10468 err = IE_ISDS;
10469 goto leave;
10471 /* One response */
10472 xpath_ctx->node = result->nodesetval->nodeTab[0];
10474 /* Extract the hash */
10475 err = find_and_extract_DmHash(context, hash, xpath_ctx);
10477 leave:
10478 if (err) {
10479 isds_hash_free(hash);
10482 xmlXPathFreeObject(result);
10483 xmlXPathFreeContext(xpath_ctx);
10485 free(code);
10486 free(status_message);
10487 xmlFreeDoc(response);
10489 if (!err)
10490 isds_log(ILF_ISDS, ILL_DEBUG,
10491 _("VerifyMessage request processed by server "
10492 "successfully.\n")
10494 #else /* not HAVE_LIBCURL */
10495 err = IE_NOTSUP;
10496 #endif
10497 return err;
10501 /* Erase message specified by @message_id from long term storage. Other
10502 * message cannot be erased on user request.
10503 * @context is session context
10504 * @message_id is message identifier.
10505 * @incoming is true for incoming message, false for outgoing message.
10506 * @return
10507 * IE_SUCCESS if message has ben removed
10508 * IE_INVAL if message does not exist in long term storage or message
10509 * belongs to different box
10510 * TODO: IE_NOEPRM if user has no permission to erase a message */
10511 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
10512 const char *message_id, _Bool incoming) {
10513 isds_error err = IE_SUCCESS;
10514 #if HAVE_LIBCURL
10515 xmlNodePtr request = NULL, node;
10516 xmlNsPtr isds_ns = NULL;
10517 xmlDocPtr response = NULL;
10518 xmlChar *code = NULL, *status_message = NULL;
10519 #endif
10521 if (!context) return IE_INVALID_CONTEXT;
10522 zfree(context->long_message);
10523 if (NULL == message_id) return IE_INVAL;
10525 /* Check if connection is established
10526 * TODO: This check should be done downstairs. */
10527 if (!context->curl) return IE_CONNECTION_CLOSED;
10529 #if HAVE_LIBCURL
10530 /* Build request */
10531 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
10532 if (!request) {
10533 isds_log_message(context,
10534 _("Could build EraseMessage request"));
10535 return IE_ERROR;
10537 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10538 if(!isds_ns) {
10539 isds_log_message(context, _("Could not create ISDS name space"));
10540 xmlFreeNode(request);
10541 return IE_ERROR;
10543 xmlSetNs(request, isds_ns);
10545 err = validate_message_id_length(context, (xmlChar *) message_id);
10546 if (err) goto leave;
10547 INSERT_STRING(request, "dmID", message_id);
10549 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
10552 /* Send request */
10553 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
10554 "message ID %s to ISDS\n"), message_id);
10555 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
10556 xmlFreeNode(request); request = NULL;
10558 if (err) {
10559 isds_log(ILF_ISDS, ILL_DEBUG,
10560 _("Processing ISDS response on EraseMessage request "
10561 "failed\n"));
10562 goto leave;
10565 /* Check for response status */
10566 err = isds_response_status(context, SERVICE_DM_INFO, response,
10567 &code, &status_message, NULL);
10568 if (err) {
10569 isds_log(ILF_ISDS, ILL_DEBUG,
10570 _("ISDS response on EraseMessage request is missing "
10571 "status\n"));
10572 goto leave;
10575 /* Check server status code */
10576 if (!xmlStrcmp(code, BAD_CAST "1211")) {
10577 isds_log_message(context, _("Message to erase belongs to other box"));
10578 err = IE_INVAL;
10579 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
10580 isds_log_message(context, _("Message to erase is not saved in "
10581 "long term storage or the direction does not match"));
10582 err = IE_INVAL;
10583 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
10584 char *code_locale = _isds_utf82locale((char*) code);
10585 char *message_locale = _isds_utf82locale((char*) status_message);
10586 isds_log(ILF_ISDS, ILL_DEBUG,
10587 _("Server refused EraseMessage request "
10588 "(code=%s, message=%s)\n"),
10589 code_locale, message_locale);
10590 isds_log_message(context, message_locale);
10591 free(code_locale);
10592 free(message_locale);
10593 err = IE_ISDS;
10594 goto leave;
10597 leave:
10598 free(code);
10599 free(status_message);
10600 xmlFreeDoc(response);
10601 xmlFreeNode(request);
10603 if (!err)
10604 isds_log(ILF_ISDS, ILL_DEBUG,
10605 _("EraseMessage request processed by server "
10606 "successfully.\n")
10608 #else /* not HAVE_LIBCURL */
10609 err = IE_NOTSUP;
10610 #endif
10611 return err;
10615 /* Mark message as read. This is a transactional commit function to acknowledge
10616 * to ISDS the message has been downloaded and processed by client properly.
10617 * @context is session context
10618 * @message_id is message identifier. */
10619 isds_error isds_mark_message_read(struct isds_ctx *context,
10620 const char *message_id) {
10622 isds_error err = IE_SUCCESS;
10623 #if HAVE_LIBCURL
10624 xmlDocPtr response = NULL;
10625 xmlChar *code = NULL, *status_message = NULL;
10626 #endif
10628 if (!context) return IE_INVALID_CONTEXT;
10629 zfree(context->long_message);
10631 #if HAVE_LIBCURL
10632 /* Do request and check for success */
10633 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10634 BAD_CAST "MarkMessageAsDownloaded", message_id,
10635 &response, NULL, NULL, &code, &status_message);
10637 free(code);
10638 free(status_message);
10639 xmlFreeDoc(response);
10641 if (!err)
10642 isds_log(ILF_ISDS, ILL_DEBUG,
10643 _("MarkMessageAsDownloaded request processed by server "
10644 "successfully.\n")
10646 #else /* not HAVE_LIBCURL */
10647 err = IE_NOTSUP;
10648 #endif
10649 return err;
10653 /* Mark message as received by recipient. This is applicable only to
10654 * commercial message. Use envelope->dmType message member to distinguish
10655 * commercial message from government message. Government message is
10656 * received automatically (by law), commercial message on recipient request.
10657 * @context is session context
10658 * @message_id is message identifier. */
10659 isds_error isds_mark_message_received(struct isds_ctx *context,
10660 const char *message_id) {
10662 isds_error err = IE_SUCCESS;
10663 #if HAVE_LIBCURL
10664 xmlDocPtr response = NULL;
10665 xmlChar *code = NULL, *status_message = NULL;
10666 #endif
10668 if (!context) return IE_INVALID_CONTEXT;
10669 zfree(context->long_message);
10671 #if HAVE_LIBCURL
10672 /* Do request and check for success */
10673 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10674 BAD_CAST "ConfirmDelivery", message_id,
10675 &response, NULL, NULL, &code, &status_message);
10677 free(code);
10678 free(status_message);
10679 xmlFreeDoc(response);
10681 if (!err)
10682 isds_log(ILF_ISDS, ILL_DEBUG,
10683 _("ConfirmDelivery request processed by server "
10684 "successfully.\n")
10686 #else /* not HAVE_LIBCURL */
10687 err = IE_NOTSUP;
10688 #endif
10689 return err;
10693 /* Send document for authorized conversion into Czech POINT system.
10694 * This is public anonymous service, no log-in necessary. Special context is
10695 * used to reuse keep-a-live HTTPS connection.
10696 * @context is Czech POINT session context. DO NOT use context connected to
10697 * ISDS server. Use new context or context used by this function previously.
10698 * @document is document to convert. Only data, data_length, dmFileDescr and
10699 * is_xml members are significant. Be ware that not all document formats can be
10700 * converted (signed PDF 1.3 and higher only (2010-02 state)).
10701 * @id is reallocated identifier assigned by Czech POINT system to
10702 * your document on submit. Use is to tell it to Czech POINT officer.
10703 * @date is reallocated document submit date (submitted documents
10704 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
10705 * value. */
10706 isds_error czp_convert_document(struct isds_ctx *context,
10707 const struct isds_document *document,
10708 char **id, struct tm **date) {
10709 isds_error err = IE_SUCCESS;
10710 #if HAVE_LIBCURL
10711 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
10712 xmlNodePtr request = NULL, node;
10713 xmlDocPtr response = NULL;
10715 xmlXPathContextPtr xpath_ctx = NULL;
10716 xmlXPathObjectPtr result = NULL;
10717 long int status = -1;
10718 long int *status_ptr = &status;
10719 char *string = NULL;
10720 #endif
10723 if (!context) return IE_INVALID_CONTEXT;
10724 zfree(context->long_message);
10725 if (!document || !id || !date) return IE_INVAL;
10727 if (document->is_xml) {
10728 isds_log_message(context,
10729 _("XML documents cannot be submitted to conversion"));
10730 return IE_NOTSUP;
10733 /* Free output arguments */
10734 zfree(*id);
10735 zfree(*date);
10737 #if HAVE_LIBCURL
10738 /* Store configuration */
10739 context->type = CTX_TYPE_CZP;
10740 free(context->url);
10741 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
10742 if (!(context->url))
10743 return IE_NOMEM;
10745 /* Prepare CURL handle if not yet connected */
10746 if (!context->curl) {
10747 context->curl = curl_easy_init();
10748 if (!(context->curl))
10749 return IE_ERROR;
10752 /* Build conversion request */
10753 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
10754 if (!request) {
10755 isds_log_message(context,
10756 _("Could not build Czech POINT conversion request"));
10757 return IE_ERROR;
10759 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
10760 if(!deposit_ns) {
10761 isds_log_message(context,
10762 _("Could not create Czech POINT deposit name space"));
10763 xmlFreeNode(request);
10764 return IE_ERROR;
10766 xmlSetNs(request, deposit_ns);
10768 /* Insert children. They are in empty namespace! */
10769 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
10770 if(!empty_ns) {
10771 isds_log_message(context, _("Could not create empty name space"));
10772 err = IE_ERROR;
10773 goto leave;
10775 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
10776 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
10777 document->dmFileDescr);
10779 /* Document encoded in Base64 */
10780 err = insert_base64_encoded_string(context, request, empty_ns, "document",
10781 document->data, document->data_length);
10782 if (err) goto leave;
10784 isds_log(ILF_ISDS, ILL_DEBUG,
10785 _("Submitting document for conversion into Czech POINT deposit"));
10787 /* Send conversion request */
10788 err = _czp_czpdeposit(context, request, &response);
10789 xmlFreeNode(request); request = NULL;
10791 if (err) {
10792 czp_do_close_connection(context);
10793 goto leave;
10797 /* Extract response */
10798 xpath_ctx = xmlXPathNewContext(response);
10799 if (!xpath_ctx) {
10800 err = IE_ERROR;
10801 goto leave;
10803 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10804 err = IE_ERROR;
10805 goto leave;
10807 result = xmlXPathEvalExpression(
10808 BAD_CAST "/deposit:saveDocumentResponse/return",
10809 xpath_ctx);
10810 if (!result) {
10811 err = IE_ERROR;
10812 goto leave;
10814 /* Empty response */
10815 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10816 isds_printf_message(context,
10817 _("Missing `return' element in Czech POINT deposit response"));
10818 err = IE_ISDS;
10819 goto leave;
10821 /* More responses */
10822 if (result->nodesetval->nodeNr > 1) {
10823 isds_printf_message(context,
10824 _("Multiple `return' element in Czech POINT deposit response"));
10825 err = IE_ISDS;
10826 goto leave;
10828 /* One response */
10829 xpath_ctx->node = result->nodesetval->nodeTab[0];
10831 /* Get status */
10832 EXTRACT_LONGINT("status", status_ptr, 1);
10833 if (status) {
10834 EXTRACT_STRING("statusMsg", string);
10835 char *string_locale = _isds_utf82locale(string);
10836 isds_printf_message(context,
10837 _("Czech POINT deposit refused document for conversion "
10838 "(code=%ld, message=%s)"),
10839 status, string_locale);
10840 free(string_locale);
10841 err = IE_ISDS;
10842 goto leave;
10845 /* Get document ID */
10846 EXTRACT_STRING("documentID", *id);
10848 /* Get submit date */
10849 EXTRACT_STRING("dateInserted", string);
10850 if (string) {
10851 *date = calloc(1, sizeof(**date));
10852 if (!*date) {
10853 err = IE_NOMEM;
10854 goto leave;
10856 err = _isds_datestring2tm((xmlChar *)string, *date);
10857 if (err) {
10858 if (err == IE_NOTSUP) {
10859 err = IE_ISDS;
10860 char *string_locale = _isds_utf82locale(string);
10861 isds_printf_message(context,
10862 _("Invalid dateInserted value: %s"), string_locale);
10863 free(string_locale);
10865 goto leave;
10869 leave:
10870 free(string);
10871 xmlXPathFreeObject(result);
10872 xmlXPathFreeContext(xpath_ctx);
10874 xmlFreeDoc(response);
10875 xmlFreeNode(request);
10877 if (!err) {
10878 char *id_locale = _isds_utf82locale((char *) *id);
10879 isds_log(ILF_ISDS, ILL_DEBUG,
10880 _("Document %s has been submitted for conversion "
10881 "to server successfully\n"), id_locale);
10882 free(id_locale);
10884 #else /* not HAVE_LIBCURL */
10885 err = IE_NOTSUP;
10886 #endif
10887 return err;
10891 /* Close possibly opened connection to Czech POINT document deposit.
10892 * @context is Czech POINT session context. */
10893 isds_error czp_close_connection(struct isds_ctx *context) {
10894 if (!context) return IE_INVALID_CONTEXT;
10895 zfree(context->long_message);
10896 #if HAVE_LIBCURL
10897 return czp_do_close_connection(context);
10898 #else
10899 return IE_NOTSUP;
10900 #endif
10904 /* Send request for new box creation in testing ISDS instance.
10905 * It's not possible to request for a production box currently, as it
10906 * communicates via e-mail.
10907 * XXX: This function does not work either. Server complains about invalid
10908 * e-mail address.
10909 * XXX: Remove context->type hacks in isds.c and validator.c when removing
10910 * this function
10911 * @context is special session context for box creation request. DO NOT use
10912 * standard context as it could reveal your password. Use fresh new context or
10913 * context previously used by this function.
10914 * @box is box description to create including single primary user (in case of
10915 * FO box type). It outputs box ID assigned by ISDS in dbID element.
10916 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
10917 * box, or contact address of PFO box owner). The email member is mandatory as
10918 * it will be used to deliver credentials.
10919 * @former_names is former name of box owner. Pass NULL if you don't care.
10920 * @approval is optional external approval of box manipulation
10921 * @refnumber is reallocated serial number of request assigned by ISDS. Use
10922 * NULL, if you don't care.*/
10923 isds_error isds_request_new_testing_box(struct isds_ctx *context,
10924 struct isds_DbOwnerInfo *box, const struct isds_list *users,
10925 const char *former_names, const struct isds_approval *approval,
10926 char **refnumber) {
10927 isds_error err = IE_SUCCESS;
10928 #if HAVE_LIBCURL
10929 xmlNodePtr request = NULL;
10930 xmlDocPtr response = NULL;
10931 xmlXPathContextPtr xpath_ctx = NULL;
10932 xmlXPathObjectPtr result = NULL;
10933 #endif
10936 if (!context) return IE_INVALID_CONTEXT;
10937 zfree(context->long_message);
10938 if (!box) return IE_INVAL;
10940 #if HAVE_LIBCURL
10941 if (!box->email || box->email[0] == '\0') {
10942 isds_log_message(context, _("E-mail field is mandatory"));
10943 return IE_INVAL;
10946 /* Scratch box ID */
10947 zfree(box->dbID);
10949 /* Store configuration */
10950 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
10951 free(context->url);
10952 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
10953 if (!(context->url))
10954 return IE_NOMEM;
10956 /* Prepare CURL handle if not yet connected */
10957 if (!context->curl) {
10958 context->curl = curl_easy_init();
10959 if (!(context->curl))
10960 return IE_ERROR;
10963 /* Build CreateDataBox request */
10964 err = build_CreateDBInput_request(context,
10965 &request, BAD_CAST "CreateDataBox",
10966 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
10967 if (err) goto leave;
10969 /* Send it to server and process response */
10970 err = send_destroy_request_check_response(context,
10971 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
10972 &response, (xmlChar **) refnumber, NULL);
10973 if (err) goto leave;
10975 /* Extract box ID */
10976 xpath_ctx = xmlXPathNewContext(response);
10977 if (!xpath_ctx) {
10978 err = IE_ERROR;
10979 goto leave;
10981 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10982 err = IE_ERROR;
10983 goto leave;
10985 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
10987 leave:
10988 xmlXPathFreeObject(result);
10989 xmlXPathFreeContext(xpath_ctx);
10990 xmlFreeDoc(response);
10991 xmlFreeNode(request);
10993 if (!err) {
10994 isds_log(ILF_ISDS, ILL_DEBUG,
10995 _("CreateDataBox request processed by server successfully.\n"));
10997 #else /* not HAVE_LIBCURL */
10998 err = IE_NOTSUP;
10999 #endif
11001 return err;
11005 /* Submit CMS signed message to ISDS to verify its originality. This is
11006 * stronger form of isds_verify_message_hash() because ISDS does more checks
11007 * than simple one (potentialy old weak) hash comparison.
11008 * @context is session context
11009 * @message is memory with raw CMS signed message bit stream
11010 * @length is @message size in bytes
11011 * @return
11012 * IE_SUCCESS if message originates in ISDS
11013 * IE_NOTEQUAL if message is unknown to ISDS
11014 * other code for other errors */
11015 isds_error isds_authenticate_message(struct isds_ctx *context,
11016 const void *message, size_t length) {
11017 isds_error err = IE_SUCCESS;
11018 #if HAVE_LIBCURL
11019 xmlNsPtr isds_ns = NULL;
11020 xmlNodePtr request = NULL;
11021 xmlDocPtr response = NULL;
11022 xmlXPathContextPtr xpath_ctx = NULL;
11023 xmlXPathObjectPtr result = NULL;
11024 _Bool *authentic = NULL;
11025 #endif
11027 if (!context) return IE_INVALID_CONTEXT;
11028 zfree(context->long_message);
11029 if (!message || length == 0) return IE_INVAL;
11031 #if HAVE_LIBCURL
11032 /* Check if connection is established
11033 * TODO: This check should be done downstairs. */
11034 if (!context->curl) return IE_CONNECTION_CLOSED;
11037 /* Build AuthenticateMessage request */
11038 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11039 if (!request) {
11040 isds_log_message(context,
11041 _("Could not build AuthenticateMessage request"));
11042 return IE_ERROR;
11044 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11045 if(!isds_ns) {
11046 isds_log_message(context, _("Could not create ISDS name space"));
11047 xmlFreeNode(request);
11048 return IE_ERROR;
11050 xmlSetNs(request, isds_ns);
11052 /* Insert Base64 encoded message */
11053 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11054 message, length);
11055 if (err) goto leave;
11057 /* Send request to server and process response */
11058 err = send_destroy_request_check_response(context,
11059 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11060 &response, NULL, NULL);
11061 if (err) goto leave;
11064 /* ISDS has decided */
11065 xpath_ctx = xmlXPathNewContext(response);
11066 if (!xpath_ctx) {
11067 err = IE_ERROR;
11068 goto leave;
11070 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11071 err = IE_ERROR;
11072 goto leave;
11075 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11077 if (!authentic) {
11078 isds_log_message(context,
11079 _("Server did not return any response on "
11080 "AuthenticateMessage request"));
11081 err = IE_ISDS;
11082 goto leave;
11084 if (*authentic) {
11085 isds_log(ILF_ISDS, ILL_DEBUG,
11086 _("ISDS authenticated the message successfully\n"));
11087 } else {
11088 isds_log_message(context, _("ISDS does not know the message"));
11089 err = IE_NOTEQUAL;
11093 leave:
11094 free(authentic);
11095 xmlXPathFreeObject(result);
11096 xmlXPathFreeContext(xpath_ctx);
11098 xmlFreeDoc(response);
11099 xmlFreeNode(request);
11100 #else /* not HAVE_LIBCURL */
11101 err = IE_NOTSUP;
11102 #endif
11104 return err;
11108 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11109 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11110 * be re-signed.
11111 * @context is session context
11112 * @input_data is memory with raw CMS signed message or delivery info bit
11113 * stream to re-sign
11114 * @input_length is @input_data size in bytes
11115 * @output_data is pointer to auto-allocated memory where to store re-signed
11116 * input data blob. Caller must free it.
11117 * @output_data is pointer where to store @output_data size in bytes
11118 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11119 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11120 * @return
11121 * IE_SUCCESS if CMS blob has been re-signed successfully
11122 * other code for other errors */
11123 isds_error isds_resign_message(struct isds_ctx *context,
11124 const void *input_data, size_t input_length,
11125 void **output_data, size_t *output_length, struct tm **valid_to) {
11126 isds_error err = IE_SUCCESS;
11127 #if HAVE_LIBCURL
11128 xmlNsPtr isds_ns = NULL;
11129 xmlNodePtr request = NULL;
11130 xmlDocPtr response = NULL;
11131 xmlXPathContextPtr xpath_ctx = NULL;
11132 xmlXPathObjectPtr result = NULL;
11133 char *string = NULL;
11134 const xmlChar *codes[] = {
11135 BAD_CAST "2200",
11136 BAD_CAST "2201",
11137 BAD_CAST "2204",
11138 BAD_CAST "2207",
11139 NULL
11141 const char *meanings[] = {
11142 "Message is bad",
11143 "Message is not original",
11144 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11145 "Time stamp could not been generated in time"
11147 const isds_error errors[] = {
11148 IE_INVAL,
11149 IE_NOTUNIQ,
11150 IE_INVAL,
11151 IE_ISDS,
11153 struct code_map_isds_error map = {
11154 .codes = codes,
11155 .meanings = meanings,
11156 .errors = errors
11158 #endif
11160 if (NULL != output_data) *output_data = NULL;
11161 if (NULL != output_length) *output_length = 0;
11162 if (NULL != valid_to) *valid_to = NULL;
11164 if (NULL == context) return IE_INVALID_CONTEXT;
11165 zfree(context->long_message);
11166 if (NULL == input_data || 0 == input_length) {
11167 isds_log_message(context, _("Empty CMS blob on input"));
11168 return IE_INVAL;
11170 if (NULL == output_data || NULL == output_length) {
11171 isds_log_message(context,
11172 _("NULL pointer provided for output CMS blob"));
11173 return IE_INVAL;
11176 #if HAVE_LIBCURL
11177 /* Check if connection is established
11178 * TODO: This check should be done downstairs. */
11179 if (!context->curl) return IE_CONNECTION_CLOSED;
11182 /* Build Re-signISDSDocument request */
11183 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11184 if (!request) {
11185 isds_log_message(context,
11186 _("Could not build Re-signISDSDocument request"));
11187 return IE_ERROR;
11189 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11190 if(!isds_ns) {
11191 isds_log_message(context, _("Could not create ISDS name space"));
11192 xmlFreeNode(request);
11193 return IE_ERROR;
11195 xmlSetNs(request, isds_ns);
11197 /* Insert Base64 encoded CMS blob */
11198 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11199 input_data, input_length);
11200 if (err) goto leave;
11202 /* Send request to server and process response */
11203 err = send_destroy_request_check_response(context,
11204 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11205 &response, NULL, &map);
11206 if (err) goto leave;
11209 /* Extract re-signed data */
11210 xpath_ctx = xmlXPathNewContext(response);
11211 if (!xpath_ctx) {
11212 err = IE_ERROR;
11213 goto leave;
11215 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11216 err = IE_ERROR;
11217 goto leave;
11219 result = xmlXPathEvalExpression(
11220 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11221 if (!result) {
11222 err = IE_ERROR;
11223 goto leave;
11225 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11226 isds_log_message(context,
11227 _("Missing Re-signISDSDocumentResponse element"));
11228 err = IE_ISDS;
11229 goto leave;
11231 if (result->nodesetval->nodeNr > 1) {
11232 isds_log_message(context,
11233 _("Multiple Re-signISDSDocumentResponse element"));
11234 err = IE_ISDS;
11235 goto leave;
11237 xpath_ctx->node = result->nodesetval->nodeTab[0];
11238 xmlXPathFreeObject(result); result = NULL;
11240 EXTRACT_STRING("isds:dmResultDoc", string);
11241 /* Decode non-empty data */
11242 if (NULL != string && string[0] != '\0') {
11243 *output_length = _isds_b64decode(string, output_data);
11244 if (*output_length == (size_t) -1) {
11245 isds_log_message(context,
11246 _("Error while Base64-decoding re-signed data"));
11247 err = IE_ERROR;
11248 goto leave;
11250 } else {
11251 isds_log_message(context, _("Server did not send re-signed data"));
11252 err = IE_ISDS;
11253 goto leave;
11255 zfree(string);
11257 if (NULL != valid_to) {
11258 /* Get time stamp expiration date */
11259 EXTRACT_STRING("isds:dmValidTo", string);
11260 if (NULL != string) {
11261 *valid_to = calloc(1, sizeof(**valid_to));
11262 if (!*valid_to) {
11263 err = IE_NOMEM;
11264 goto leave;
11266 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11267 if (err) {
11268 if (err == IE_NOTSUP) {
11269 err = IE_ISDS;
11270 char *string_locale = _isds_utf82locale(string);
11271 isds_printf_message(context,
11272 _("Invalid dmValidTo value: %s"), string_locale);
11273 free(string_locale);
11275 goto leave;
11280 leave:
11281 free(string);
11283 xmlXPathFreeObject(result);
11284 xmlXPathFreeContext(xpath_ctx);
11286 xmlFreeDoc(response);
11287 xmlFreeNode(request);
11288 #else /* not HAVE_LIBCURL */
11289 err = IE_NOTSUP;
11290 #endif
11292 return err;
11295 #undef INSERT_ELEMENT
11296 #undef CHECK_FOR_STRING_LENGTH
11297 #undef INSERT_STRING_ATTRIBUTE
11298 #undef INSERT_ULONGINTNOPTR
11299 #undef INSERT_ULONGINT
11300 #undef INSERT_LONGINT
11301 #undef INSERT_BOOLEAN
11302 #undef INSERT_SCALAR_BOOLEAN
11303 #undef INSERT_STRING
11304 #undef INSERT_STRING_WITH_NS
11305 #undef EXTRACT_STRING_ATTRIBUTE
11306 #undef EXTRACT_ULONGINT
11307 #undef EXTRACT_LONGINT
11308 #undef EXTRACT_BOOLEAN
11309 #undef EXTRACT_STRING
11312 /* Compute hash of message from raw representation and store it into envelope.
11313 * Original hash structure will be destroyed in envelope.
11314 * @context is session context
11315 * @message is message carrying raw XML message blob
11316 * @algorithm is desired hash algorithm to use */
11317 isds_error isds_compute_message_hash(struct isds_ctx *context,
11318 struct isds_message *message, const isds_hash_algorithm algorithm) {
11319 isds_error err = IE_SUCCESS;
11320 const char *nsuri;
11321 void *xml_stream = NULL;
11322 size_t xml_stream_length;
11323 size_t phys_start, phys_end;
11324 char *phys_path = NULL;
11325 struct isds_hash *new_hash = NULL;
11328 if (!context) return IE_INVALID_CONTEXT;
11329 zfree(context->long_message);
11330 if (!message) return IE_INVAL;
11332 if (!message->raw) {
11333 isds_log_message(context,
11334 _("Message does not carry raw representation"));
11335 return IE_INVAL;
11338 switch (message->raw_type) {
11339 case RAWTYPE_INCOMING_MESSAGE:
11340 nsuri = ISDS_NS;
11341 xml_stream = message->raw;
11342 xml_stream_length = message->raw_length;
11343 break;
11345 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11346 nsuri = SISDS_INCOMING_NS;
11347 xml_stream = message->raw;
11348 xml_stream_length = message->raw_length;
11349 break;
11351 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11352 nsuri = SISDS_INCOMING_NS;
11353 err = _isds_extract_cms_data(context,
11354 message->raw, message->raw_length,
11355 &xml_stream, &xml_stream_length);
11356 if (err) goto leave;
11357 break;
11359 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11360 nsuri = SISDS_OUTGOING_NS;
11361 xml_stream = message->raw;
11362 xml_stream_length = message->raw_length;
11363 break;
11365 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11366 nsuri = SISDS_OUTGOING_NS;
11367 err = _isds_extract_cms_data(context,
11368 message->raw, message->raw_length,
11369 &xml_stream, &xml_stream_length);
11370 if (err) goto leave;
11371 break;
11373 default:
11374 isds_log_message(context, _("Bad raw representation type"));
11375 return IE_INVAL;
11376 break;
11380 /* XXX: Hash is computed from original string representing isds:dmDm
11381 * subtree. That means no encoding, white space, xmlns attributes changes.
11382 * In other words, input for hash can be invalid XML stream. */
11383 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
11384 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11385 PHYSXML_ELEMENT_SEPARATOR,
11386 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
11387 PHYSXML_ELEMENT_SEPARATOR
11388 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
11389 err = IE_NOMEM;
11390 goto leave;
11392 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11393 phys_path, &phys_start, &phys_end);
11394 zfree(phys_path);
11395 if (err) {
11396 isds_log_message(context,
11397 _("Substring with isds:dmDM element could not be located "
11398 "in raw message"));
11399 goto leave;
11403 /* Compute hash */
11404 new_hash = calloc(1, sizeof(*new_hash));
11405 if (!new_hash) {
11406 err = IE_NOMEM;
11407 goto leave;
11409 new_hash->algorithm = algorithm;
11410 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
11411 new_hash);
11412 if (err) {
11413 isds_log_message(context, _("Could not compute message hash"));
11414 goto leave;
11417 /* Save computed hash */
11418 if (!message->envelope) {
11419 message->envelope = calloc(1, sizeof(*message->envelope));
11420 if (!message->envelope) {
11421 err = IE_NOMEM;
11422 goto leave;
11425 isds_hash_free(&message->envelope->hash);
11426 message->envelope->hash = new_hash;
11428 leave:
11429 if (err) {
11430 isds_hash_free(&new_hash);
11433 free(phys_path);
11434 if (xml_stream != message->raw) free(xml_stream);
11435 return err;
11439 /* Compare two hashes.
11440 * @h1 is first hash
11441 * @h2 is another hash
11442 * @return
11443 * IE_SUCCESS if hashes equal
11444 * IE_NOTUNIQ if hashes are comparable, but they don't equal
11445 * IE_ENUM if not comparable, but both structures defined
11446 * IE_INVAL if some of the structures are undefined (NULL)
11447 * IE_ERROR if internal error occurs */
11448 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
11449 if (h1 == NULL || h2 == NULL) return IE_INVAL;
11450 if (h1->algorithm != h2->algorithm) return IE_ENUM;
11451 if (h1->length != h2->length) return IE_ERROR;
11452 if (h1->length > 0 && !h1->value) return IE_ERROR;
11453 if (h2->length > 0 && !h2->value) return IE_ERROR;
11455 for (int i = 0; i < h1->length; i++) {
11456 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
11457 return IE_NOTEQUAL;
11459 return IE_SUCCESS;
11463 /* Check message has gone through ISDS by comparing message hash stored in
11464 * ISDS and locally computed hash. You must provide message with valid raw
11465 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
11466 * This is convenient wrapper for isds_download_message_hash(),
11467 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
11468 * @context is session context
11469 * @message is message with valid raw and envelope member; envelope->hash
11470 * member will be changed during function run. Use envelope on heap only.
11471 * @return
11472 * IE_SUCCESS if message originates in ISDS
11473 * IE_NOTEQUAL if message is unknown to ISDS
11474 * other code for other errors */
11475 isds_error isds_verify_message_hash(struct isds_ctx *context,
11476 struct isds_message *message) {
11477 isds_error err = IE_SUCCESS;
11478 struct isds_hash *downloaded_hash = NULL;
11480 if (!context) return IE_INVALID_CONTEXT;
11481 zfree(context->long_message);
11482 if (!message) return IE_INVAL;
11484 if (!message->envelope) {
11485 isds_log_message(context,
11486 _("Given message structure is missing envelope"));
11487 return IE_INVAL;
11489 if (!message->raw) {
11490 isds_log_message(context,
11491 _("Given message structure is missing raw representation"));
11492 return IE_INVAL;
11495 err = isds_download_message_hash(context, message->envelope->dmID,
11496 &downloaded_hash);
11497 if (err) goto leave;
11499 err = isds_compute_message_hash(context, message,
11500 downloaded_hash->algorithm);
11501 if (err) goto leave;
11503 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
11505 leave:
11506 isds_hash_free(&downloaded_hash);
11507 return err;
11511 /* Search for document by document ID in list of documents. IDs are compared
11512 * as UTF-8 string.
11513 * @documents is list of isds_documents
11514 * @id is document identifier
11515 * @return first matching document or NULL. */
11516 const struct isds_document *isds_find_document_by_id(
11517 const struct isds_list *documents, const char *id) {
11518 const struct isds_list *item;
11519 const struct isds_document *document;
11521 for (item = documents; item; item = item->next) {
11522 document = (struct isds_document *) item->data;
11523 if (!document) continue;
11525 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
11526 return document;
11529 return NULL;
11533 /* Normalize @mime_type to be proper MIME type.
11534 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
11535 * guess regular MIME type (e.g. "application/pdf").
11536 * @mime_type is UTF-8 encoded MIME type to fix
11537 * @return original @mime_type if no better interpretation exists, or
11538 * constant static UTF-8 encoded string with proper MIME type. */
11539 const char *isds_normalize_mime_type(const char *mime_type) {
11540 if (!mime_type) return NULL;
11542 for (int offset = 0;
11543 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
11544 offset += 2) {
11545 if (!xmlStrcasecmp((const xmlChar*) mime_type,
11546 extension_map_mime[offset]))
11547 return (const char *) extension_map_mime[offset + 1];
11550 return mime_type;
11554 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
11555 struct isds_message **message);
11556 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
11557 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
11558 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
11559 struct isds_address **address);
11561 int isds_message_free(struct isds_message **message);
11562 int isds_address_free(struct isds_address **address);
11566 /* Makes known all relevant namespaces to given XPath context
11567 * @xpath_ctx is XPath context
11568 * @message_ns selects proper message name space. Unsigned and signed
11569 * messages and delivery info's differ in prefix and URI. */
11570 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
11571 const message_ns_type message_ns) {
11572 const xmlChar *message_namespace = NULL;
11574 if (!xpath_ctx) return IE_ERROR;
11576 switch(message_ns) {
11577 case MESSAGE_NS_1:
11578 message_namespace = BAD_CAST ISDS1_NS; break;
11579 case MESSAGE_NS_UNSIGNED:
11580 message_namespace = BAD_CAST ISDS_NS; break;
11581 case MESSAGE_NS_SIGNED_INCOMING:
11582 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
11583 case MESSAGE_NS_SIGNED_OUTGOING:
11584 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
11585 case MESSAGE_NS_SIGNED_DELIVERY:
11586 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
11587 default:
11588 return IE_ENUM;
11591 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
11592 return IE_ERROR;
11593 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
11594 return IE_ERROR;
11595 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
11596 return IE_ERROR;
11597 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
11598 return IE_ERROR;
11599 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
11600 return IE_ERROR;
11601 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
11602 return IE_ERROR;
11603 return IE_SUCCESS;