Fixed a bug. User identification didn't work properly.
[libisds.git] / src / isds.c
blob3329a00d4cf703b1c732ed9137133b5296ace2ea
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> /* For uint8_t and intmax_t */
8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
9 #include "utils.h"
10 #if HAVE_LIBCURL
11 #include "soap.h"
12 #endif
13 #include "validator.h"
14 #include "crypto.h"
15 #include "physxml.h"
16 #include "system.h"
18 /* Global variables.
19 * Allocated in isds_init() and deallocated in isds_cleanup(). */
20 unsigned int log_facilities;
21 isds_log_level log_level;
22 isds_log_callback log_callback;
23 void *log_callback_data;
24 const char *version_gpgme = N_("n/a");
25 const char *version_gcrypt = N_("n/a");
26 const char *version_openssl = N_("n/a");
27 const char *version_expat = N_("n/a");
29 /* Locators */
30 /* Base URL of production ISDS instance */
31 const char isds_locator[] = "https://ws1.mojedatovaschranka.cz/";
32 const char isds_cert_locator[] = "https://ws1c.mojedatovaschranka.cz/";
33 const char isds_otp_locator[] = "https://www.mojedatovaschranka.cz/";
35 /* Base URL of production ISDS instance */
36 const char isds_testing_locator[] = "https://ws1.czebox.cz/";
37 const char isds_cert_testing_locator[] = "https://ws1c.czebox.cz/";
38 const char isds_otp_testing_locator[] = "https://www.czebox.cz/";
40 /* Extension to MIME type map */
41 static const xmlChar *extension_map_mime[] = {
42 BAD_CAST "cer", BAD_CAST "application/x-x509-ca-cert",
43 BAD_CAST "crt", BAD_CAST "application/x-x509-ca-cert",
44 BAD_CAST "der", BAD_CAST "application/x-x509-ca-cert",
45 BAD_CAST "doc", BAD_CAST "application/msword",
46 BAD_CAST "docx", BAD_CAST "application/vnd.openxmlformats-officedocument."
47 "wordprocessingml.document",
48 BAD_CAST "dbf", BAD_CAST "application/octet-stream",
49 BAD_CAST "prj", BAD_CAST "application/octet-stream",
50 BAD_CAST "qix", BAD_CAST "application/octet-stream",
51 BAD_CAST "sbn", BAD_CAST "application/octet-stream",
52 BAD_CAST "sbx", BAD_CAST "application/octet-stream",
53 BAD_CAST "shp", BAD_CAST "application/octet-stream",
54 BAD_CAST "shx", BAD_CAST "application/octet-stream",
55 BAD_CAST "dgn", BAD_CAST "application/octet-stream",
56 BAD_CAST "dwg", BAD_CAST "image/vnd.dwg",
57 BAD_CAST "edi", BAD_CAST "application/edifact",
58 BAD_CAST "fo", BAD_CAST "application/vnd.software602.filler.form+xml",
59 BAD_CAST "gfs", BAD_CAST "application/xml",
60 BAD_CAST "gml", BAD_CAST "application/xml",
61 BAD_CAST "gif", BAD_CAST "image/gif",
62 BAD_CAST "htm", BAD_CAST "text/html",
63 BAD_CAST "html", BAD_CAST "text/html",
64 BAD_CAST "isdoc", BAD_CAST "text/isdoc",
65 BAD_CAST "isdocx", BAD_CAST "text/isdocx",
66 BAD_CAST "jfif", BAD_CAST "image/jpeg",
67 BAD_CAST "jpg", BAD_CAST "image/jpeg",
68 BAD_CAST "jpeg", BAD_CAST "image/jpeg",
69 BAD_CAST "mpeg", BAD_CAST "video/mpeg",
70 BAD_CAST "mpeg1", BAD_CAST "video/mpeg",
71 BAD_CAST "mpeg2", BAD_CAST "video/mpeg",
72 BAD_CAST "mpg", BAD_CAST "video/mpeg",
73 BAD_CAST "mp2", BAD_CAST "audio/mpeg",
74 BAD_CAST "mp3", BAD_CAST "audio/mpeg",
75 BAD_CAST "odp", BAD_CAST "application/vnd.oasis.opendocument.presentation",
76 BAD_CAST "ods", BAD_CAST "application/vnd.oasis.opendocument.spreadsheet",
77 BAD_CAST "odt", BAD_CAST "application/vnd.oasis.opendocument.text",
78 BAD_CAST "pdf", BAD_CAST "application/pdf",
79 BAD_CAST "p7b", BAD_CAST "application/pkcs7-certificates",
80 BAD_CAST "p7c", BAD_CAST "application/pkcs7-mime",
81 BAD_CAST "p7m", BAD_CAST "application/pkcs7-mime",
82 BAD_CAST "p7f", BAD_CAST "application/pkcs7-signature",
83 BAD_CAST "p7s", BAD_CAST "application/pkcs7-signature",
84 BAD_CAST "pk7", BAD_CAST "application/pkcs7-mime",
85 BAD_CAST "png", BAD_CAST "image/png",
86 BAD_CAST "ppt", BAD_CAST "application/vnd.ms-powerpoint",
87 BAD_CAST "pptx", BAD_CAST "application/vnd.openxmlformats-officedocument."
88 "presentationml.presentation",
89 BAD_CAST "rtf", BAD_CAST "application/rtf",
90 BAD_CAST "tif", BAD_CAST "image/tiff",
91 BAD_CAST "tiff", BAD_CAST "image/tiff",
92 BAD_CAST "tsr", BAD_CAST "application/timestamp-reply",
93 BAD_CAST "tst", BAD_CAST "application/timestamp-reply",
94 BAD_CAST "txt", BAD_CAST "text/plain",
95 BAD_CAST "wav", BAD_CAST "audio/wav",
96 BAD_CAST "xls", BAD_CAST "application/vnd.ms-excel",
97 BAD_CAST "xlsx", BAD_CAST "application/vnd.openxmlformats-officedocument."
98 "spreadsheetml.sheet",
99 BAD_CAST "xml", BAD_CAST "application/xml",
100 BAD_CAST "xsd", BAD_CAST "application/xml",
101 BAD_CAST "zfo", BAD_CAST "application/vnd.software602.filler.form-xml-zip"
104 /* Structure type to hold conversion table from status code to isds_error and
105 * long message */
106 struct code_map_isds_error {
107 const xmlChar **codes; /* NULL terminated array of status codes */
108 const char **meanings; /* Mapping to non-localized long messages */
109 const isds_error *errors; /* Mapping to isds_error code */
112 /* Deallocate structure isds_pki_credentials and NULL it.
113 * Pass-phrase is discarded.
114 * @pki credentials to to free */
115 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
116 if(!pki || !*pki) return;
118 free((*pki)->engine);
119 free((*pki)->certificate);
120 free((*pki)->key);
122 if ((*pki)->passphrase) {
123 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
124 free((*pki)->passphrase);
127 zfree((*pki));
131 /* Free isds_list with all member data.
132 * @list list to free, on return will be NULL */
133 void isds_list_free(struct isds_list **list) {
134 struct isds_list *item, *next_item;
136 if (!list || !*list) return;
138 for(item = *list; item; item = next_item) {
139 if (item->destructor) (item->destructor)(&(item->data));
140 next_item = item->next;
141 free(item);
144 *list = NULL;
148 /* Deallocate structure isds_hash and NULL it.
149 * @hash hash to to free */
150 void isds_hash_free(struct isds_hash **hash) {
151 if(!hash || !*hash) return;
152 free((*hash)->value);
153 zfree((*hash));
157 /* Deallocate structure isds_PersonName recursively and NULL it */
158 void isds_PersonName_free(struct isds_PersonName **person_name) {
159 if (!person_name || !*person_name) return;
161 free((*person_name)->pnFirstName);
162 free((*person_name)->pnMiddleName);
163 free((*person_name)->pnLastName);
164 free((*person_name)->pnLastNameAtBirth);
166 free(*person_name);
167 *person_name = NULL;
171 /* Deallocate structure isds_BirthInfo recursively and NULL it */
172 void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
173 if (!birth_info || !*birth_info) return;
175 free((*birth_info)->biDate);
176 free((*birth_info)->biCity);
177 free((*birth_info)->biCounty);
178 free((*birth_info)->biState);
180 free(*birth_info);
181 *birth_info = NULL;
185 /* Deallocate structure isds_Address recursively and NULL it */
186 void isds_Address_free(struct isds_Address **address) {
187 if (!address || !*address) return;
189 free((*address)->adCity);
190 free((*address)->adStreet);
191 free((*address)->adNumberInStreet);
192 free((*address)->adNumberInMunicipality);
193 free((*address)->adZipCode);
194 free((*address)->adState);
196 free(*address);
197 *address = NULL;
201 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
202 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
203 if (!db_owner_info || !*db_owner_info) return;
205 free((*db_owner_info)->dbID);
206 free((*db_owner_info)->dbType);
207 free((*db_owner_info)->ic);
208 isds_PersonName_free(&((*db_owner_info)->personName));
209 free((*db_owner_info)->firmName);
210 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
211 isds_Address_free(&((*db_owner_info)->address));
212 free((*db_owner_info)->nationality);
213 free((*db_owner_info)->email);
214 free((*db_owner_info)->telNumber);
215 free((*db_owner_info)->identifier);
216 free((*db_owner_info)->registryCode);
217 free((*db_owner_info)->dbState);
218 free((*db_owner_info)->dbEffectiveOVM);
219 free((*db_owner_info)->dbOpenAddressing);
221 free(*db_owner_info);
222 *db_owner_info = NULL;
225 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
226 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
227 if (!db_user_info || !*db_user_info) return;
229 free((*db_user_info)->userID);
230 free((*db_user_info)->userType);
231 free((*db_user_info)->userPrivils);
232 isds_PersonName_free(&((*db_user_info)->personName));
233 isds_Address_free(&((*db_user_info)->address));
234 free((*db_user_info)->biDate);
235 free((*db_user_info)->ic);
236 free((*db_user_info)->firmName);
237 free((*db_user_info)->caStreet);
238 free((*db_user_info)->caCity);
239 free((*db_user_info)->caZipCode);
240 free((*db_user_info)->caState);
242 zfree(*db_user_info);
246 /* Deallocate struct isds_event recursively and NULL it */
247 void isds_event_free(struct isds_event **event) {
248 if (!event || !*event) return;
250 free((*event)->time);
251 free((*event)->type);
252 free((*event)->description);
253 zfree(*event);
257 /* Deallocate struct isds_envelope recursively and NULL it */
258 void isds_envelope_free(struct isds_envelope **envelope) {
259 if (!envelope || !*envelope) return;
261 free((*envelope)->dmID);
262 free((*envelope)->dbIDSender);
263 free((*envelope)->dmSender);
264 free((*envelope)->dmSenderAddress);
265 free((*envelope)->dmSenderType);
266 free((*envelope)->dmRecipient);
267 free((*envelope)->dmRecipientAddress);
268 free((*envelope)->dmAmbiguousRecipient);
269 free((*envelope)->dmType);
271 free((*envelope)->dmOrdinal);
272 free((*envelope)->dmMessageStatus);
273 free((*envelope)->dmDeliveryTime);
274 free((*envelope)->dmAcceptanceTime);
275 isds_hash_free(&(*envelope)->hash);
276 free((*envelope)->timestamp);
277 isds_list_free(&(*envelope)->events);
279 free((*envelope)->dmSenderOrgUnit);
280 free((*envelope)->dmSenderOrgUnitNum);
281 free((*envelope)->dbIDRecipient);
282 free((*envelope)->dmRecipientOrgUnit);
283 free((*envelope)->dmRecipientOrgUnitNum);
284 free((*envelope)->dmToHands);
285 free((*envelope)->dmAnnotation);
286 free((*envelope)->dmRecipientRefNumber);
287 free((*envelope)->dmSenderRefNumber);
288 free((*envelope)->dmRecipientIdent);
289 free((*envelope)->dmSenderIdent);
291 free((*envelope)->dmLegalTitleLaw);
292 free((*envelope)->dmLegalTitleYear);
293 free((*envelope)->dmLegalTitleSect);
294 free((*envelope)->dmLegalTitlePar);
295 free((*envelope)->dmLegalTitlePoint);
297 free((*envelope)->dmPersonalDelivery);
298 free((*envelope)->dmAllowSubstDelivery);
300 free((*envelope)->dmOVM);
301 free((*envelope)->dmPublishOwnID);
303 free(*envelope);
304 *envelope = NULL;
308 /* Deallocate struct isds_message recursively and NULL it */
309 void isds_message_free(struct isds_message **message) {
310 if (!message || !*message) return;
312 free((*message)->raw);
313 isds_envelope_free(&((*message)->envelope));
314 isds_list_free(&((*message)->documents));
315 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
317 free(*message);
318 *message = NULL;
322 /* Deallocate struct isds_document recursively and NULL it */
323 void isds_document_free(struct isds_document **document) {
324 if (!document || !*document) return;
326 if (!(*document)->is_xml) {
327 free((*document)->data);
329 free((*document)->dmMimeType);
330 free((*document)->dmFileGuid);
331 free((*document)->dmUpFileGuid);
332 free((*document)->dmFileDescr);
333 free((*document)->dmFormat);
335 free(*document);
336 *document = NULL;
340 /* Deallocate struct isds_message_copy recursively and NULL it */
341 void isds_message_copy_free(struct isds_message_copy **copy) {
342 if (!copy || !*copy) return;
344 free((*copy)->dbIDRecipient);
345 free((*copy)->dmRecipientOrgUnit);
346 free((*copy)->dmRecipientOrgUnitNum);
347 free((*copy)->dmToHands);
349 free((*copy)->dmStatus);
350 free((*copy)->dmID);
352 zfree(*copy);
356 /* Deallocate struct isds_message_status_change recursively and NULL it */
357 void isds_message_status_change_free(
358 struct isds_message_status_change **message_status_change) {
359 if (!message_status_change || !*message_status_change) return;
361 free((*message_status_change)->dmID);
362 free((*message_status_change)->time);
363 free((*message_status_change)->dmMessageStatus);
365 zfree(*message_status_change);
369 /* Deallocate struct isds_approval recursively and NULL it */
370 void isds_approval_free(struct isds_approval **approval) {
371 if (!approval || !*approval) return;
373 free((*approval)->refference);
375 zfree(*approval);
379 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
380 * The email string is deallocated too. */
381 void isds_credentials_delivery_free(
382 struct isds_credentials_delivery **credentials_delivery) {
383 if (!credentials_delivery || !*credentials_delivery) return;
385 free((*credentials_delivery)->email);
386 free((*credentials_delivery)->token);
387 free((*credentials_delivery)->new_user_name);
389 zfree(*credentials_delivery);
393 /* Deallocate struct isds_commercial_permission recursively and NULL it */
394 void isds_commercial_permission_free(
395 struct isds_commercial_permission **permission) {
396 if (NULL == permission || NULL == *permission) return;
398 free((*permission)->recipient);
399 free((*permission)->payer);
400 free((*permission)->expiration);
401 free((*permission)->count);
402 free((*permission)->reply_identifier);
404 zfree(*permission);
408 /* Deallocate struct isds_credit_event recursively and NULL it */
409 void isds_credit_event_free(struct isds_credit_event **event) {
410 if (NULL == event || NULL == *event) return;
412 free((*event)->time);
413 switch ((*event)->type) {
414 case ISDS_CREDIT_CHARGED:
415 free((*event)->details.charged.transaction);
416 break;
417 case ISDS_CREDIT_DISCHARGED:
418 free((*event)->details.discharged.transaction);
419 break;
420 case ISDS_CREDIT_MESSAGE_SENT:
421 free((*event)->details.message_sent.recipient);
422 free((*event)->details.message_sent.message_id);
423 break;
424 case ISDS_CREDIT_STORAGE_SET:
425 free((*event)->details.storage_set.new_valid_from);
426 free((*event)->details.storage_set.new_valid_to);
427 free((*event)->details.storage_set.old_capacity);
428 free((*event)->details.storage_set.old_valid_from);
429 free((*event)->details.storage_set.old_valid_to);
430 free((*event)->details.storage_set.initiator);
431 break;
432 case ISDS_CREDIT_EXPIRED:
433 break;
436 zfree(*event);
440 /* Deallocate struct isds_fulltext_result recursively and NULL it */
441 void isds_fulltext_result_free(
442 struct isds_fulltext_result **result) {
443 if (NULL == result || NULL == *result) return;
445 free((*result)->dbID);
446 free((*result)->name);
447 isds_list_free(&((*result)->name_match_start));
448 isds_list_free(&((*result)->name_match_end));
449 free((*result)->address);
450 isds_list_free(&((*result)->address_match_start));
451 isds_list_free(&((*result)->address_match_end));
452 free((*result)->ic);
453 free((*result)->biDate);
455 zfree(*result);
459 /* *DUP_OR_ERROR macros needs error label */
460 #define STRDUP_OR_ERROR(new, template) { \
461 if (!template) { \
462 (new) = NULL; \
463 } else { \
464 (new) = strdup(template); \
465 if (!new) goto error; \
469 #define FLATDUP_OR_ERROR(new, template) { \
470 if (!template) { \
471 (new) = NULL; \
472 } else { \
473 (new) = malloc(sizeof(*(new))); \
474 if (!new) goto error; \
475 memcpy((new), (template), sizeof(*(template))); \
479 /* Copy structure isds_pki_credentials recursively. */
480 struct isds_pki_credentials *isds_pki_credentials_duplicate(
481 const struct isds_pki_credentials *template) {
482 struct isds_pki_credentials *new = NULL;
484 if(!template) return NULL;
486 new = calloc(1, sizeof(*new));
487 if (!new) return NULL;
489 STRDUP_OR_ERROR(new->engine, template->engine);
490 new->certificate_format = template->certificate_format;
491 STRDUP_OR_ERROR(new->certificate, template->certificate);
492 new->key_format = template->key_format;
493 STRDUP_OR_ERROR(new->key, template->key);
494 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
496 return new;
498 error:
499 isds_pki_credentials_free(&new);
500 return NULL;
504 /* Copy structure isds_PersonName recursively */
505 struct isds_PersonName *isds_PersonName_duplicate(
506 const struct isds_PersonName *src) {
507 struct isds_PersonName *new = NULL;
509 if (!src) return NULL;
511 new = calloc(1, sizeof(*new));
512 if (!new) return NULL;
514 STRDUP_OR_ERROR(new->pnFirstName, src->pnFirstName);
515 STRDUP_OR_ERROR(new->pnMiddleName, src->pnMiddleName);
516 STRDUP_OR_ERROR(new->pnLastName, src->pnLastName);
517 STRDUP_OR_ERROR(new->pnLastNameAtBirth, src->pnLastNameAtBirth);
519 return new;
521 error:
522 isds_PersonName_free(&new);
523 return NULL;
527 /* Copy structure isds_BirthInfo recursively */
528 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
529 const struct isds_BirthInfo *template) {
530 struct isds_BirthInfo *new = NULL;
532 if (!template) return NULL;
534 new = calloc(1, sizeof(*new));
535 if (!new) return NULL;
537 FLATDUP_OR_ERROR(new->biDate, template->biDate);
538 STRDUP_OR_ERROR(new->biCity, template->biCity);
539 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
540 STRDUP_OR_ERROR(new->biState, template->biState);
542 return new;
544 error:
545 isds_BirthInfo_free(&new);
546 return NULL;
550 /* Copy structure isds_Address recursively */
551 struct isds_Address *isds_Address_duplicate(
552 const struct isds_Address *src) {
553 struct isds_Address *new = NULL;
555 if (!src) return NULL;
557 new = calloc(1, sizeof(*new));
558 if (!new) return NULL;
560 STRDUP_OR_ERROR(new->adCity, src->adCity);
561 STRDUP_OR_ERROR(new->adStreet, src->adStreet);
562 STRDUP_OR_ERROR(new->adNumberInStreet, src->adNumberInStreet);
563 STRDUP_OR_ERROR(new->adNumberInMunicipality,
564 src->adNumberInMunicipality);
565 STRDUP_OR_ERROR(new->adZipCode, src->adZipCode);
566 STRDUP_OR_ERROR(new->adState, src->adState);
568 return new;
570 error:
571 isds_Address_free(&new);
572 return NULL;
576 /* Copy structure isds_DbOwnerInfo recursively */
577 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
578 const struct isds_DbOwnerInfo *src) {
579 struct isds_DbOwnerInfo *new = NULL;
580 if (!src) return NULL;
582 new = calloc(1, sizeof(*new));
583 if (!new) return NULL;
585 STRDUP_OR_ERROR(new->dbID, src->dbID);
586 FLATDUP_OR_ERROR(new->dbType, src->dbType);
587 STRDUP_OR_ERROR(new->ic, src->ic);
589 if (src->personName) {
590 if (!(new->personName =
591 isds_PersonName_duplicate(src->personName)))
592 goto error;
595 STRDUP_OR_ERROR(new->firmName, src->firmName);
597 if (src->birthInfo) {
598 if (!(new->birthInfo =
599 isds_BirthInfo_duplicate(src->birthInfo)))
600 goto error;
603 if (src->address) {
604 if (!(new->address = isds_Address_duplicate(src->address)))
605 goto error;
608 STRDUP_OR_ERROR(new->nationality, src->nationality);
609 STRDUP_OR_ERROR(new->email, src->email);
610 STRDUP_OR_ERROR(new->telNumber, src->telNumber);
611 STRDUP_OR_ERROR(new->identifier, src->identifier);
612 STRDUP_OR_ERROR(new->registryCode, src->registryCode);
613 FLATDUP_OR_ERROR(new->dbState, src->dbState);
614 FLATDUP_OR_ERROR(new->dbEffectiveOVM, src->dbEffectiveOVM);
615 FLATDUP_OR_ERROR(new->dbOpenAddressing, src->dbOpenAddressing);
617 return new;
619 error:
620 isds_DbOwnerInfo_free(&new);
621 return NULL;
625 /* Copy structure isds_DbUserInfo recursively */
626 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
627 const struct isds_DbUserInfo *src) {
628 struct isds_DbUserInfo *new = NULL;
629 if (!src) return NULL;
631 new = calloc(1, sizeof(*new));
632 if (!new) return NULL;
634 STRDUP_OR_ERROR(new->userID, src->userID);
635 FLATDUP_OR_ERROR(new->userType, src->userType);
636 FLATDUP_OR_ERROR(new->userPrivils, src->userPrivils);
638 if (src->personName) {
639 if (!(new->personName =
640 isds_PersonName_duplicate(src->personName)))
641 goto error;
644 if (src->address) {
645 if (!(new->address = isds_Address_duplicate(src->address)))
646 goto error;
649 FLATDUP_OR_ERROR(new->biDate, src->biDate);
650 STRDUP_OR_ERROR(new->ic, src->ic);
651 STRDUP_OR_ERROR(new->firmName, src->firmName);
652 STRDUP_OR_ERROR(new->caStreet, src->caStreet);
653 STRDUP_OR_ERROR(new->caCity, src->caCity);
654 STRDUP_OR_ERROR(new->caZipCode, src->caZipCode);
655 STRDUP_OR_ERROR(new->caState, src->caState);
657 return new;
659 error:
660 isds_DbUserInfo_free(&new);
661 return NULL;
664 #undef FLATDUP_OR_ERROR
665 #undef STRDUP_OR_ERROR
668 /* Logs libxml2 errors. Should be registered to libxml2 library.
669 * @ctx is unused currently
670 * @msg is printf-like formated message from libxml2 (UTF-8?)
671 * @... are variadic arguments for @msg */
672 static void log_xml(void *ctx, const char *msg, ...) {
673 va_list ap;
674 char *text = NULL;
676 /* Silent warning for unused function argument.
677 * The prototype is an API of libxml2's xmlSetGenericErrorFunc(). */
678 (void)ctx;
680 if (!msg) return;
682 va_start(ap, msg);
683 isds_vasprintf(&text, msg, ap);
684 va_end(ap);
686 if (text)
687 isds_log(ILF_XML, ILL_ERR, "%s", text);
688 free(text);
692 /* Initialize ISDS library.
693 * Global function, must be called before other functions.
694 * If it fails you can not use ISDS library and must call isds_cleanup() to
695 * free partially initialized global variables. */
696 isds_error isds_init(void) {
697 /* NULL global variables */
698 log_facilities = ILF_ALL;
699 log_level = ILL_WARNING;
700 log_callback = NULL;
701 log_callback_data = NULL;
703 #if ENABLE_NLS
704 /* Initialize gettext */
705 bindtextdomain(PACKAGE, LOCALEDIR);
706 #endif
708 #if HAVE_LIBCURL
709 /* Initialize CURL */
710 if (curl_global_init(CURL_GLOBAL_ALL)) {
711 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
712 return IE_ERROR;
714 #endif /* HAVE_LIBCURL */
716 /* Initialise cryptographic back-ends. */
717 if (IE_SUCCESS != _isds_init_crypto()) {
718 isds_log(ILF_ISDS, ILL_CRIT,
719 _("Initialization of cryptographic back-end failed\n"));
720 return IE_ERROR;
723 /* This can _exit() current program. Find not so assertive check. */
724 LIBXML_TEST_VERSION;
725 xmlSetGenericErrorFunc(NULL, log_xml);
727 /* Check expat */
728 if (_isds_init_expat(&version_expat)) {
729 isds_log(ILF_ISDS, ILL_CRIT,
730 _("expat library initialization failed\n"));
731 return IE_ERROR;
734 /* Allocate global variables */
737 return IE_SUCCESS;
741 /* Deinitialize ISDS library.
742 * Global function, must be called as last library function. */
743 isds_error isds_cleanup(void) {
744 /* XML */
745 xmlCleanupParser();
747 #if HAVE_LIBCURL
748 /* Curl */
749 curl_global_cleanup();
750 #endif
752 return IE_SUCCESS;
756 /* Return version string of this library. Version of dependencies can be
757 * embedded. Do no try to parse it. You must free it. */
758 char *isds_version(void) {
759 char *buffer = NULL;
761 isds_asprintf(&buffer,
762 #if HAVE_LIBCURL
763 # ifndef USE_OPENSSL_BACKEND
764 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
765 # else
766 _("%s (%s, %s, %s, libxml2 %s)"),
767 # endif
768 #else
769 # ifndef USE_OPENSSL_BACKEND
770 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
771 # else
772 _("%s (%s, %s, libxml2 %s)"),
773 # endif
774 #endif
775 PACKAGE_VERSION,
776 #if HAVE_LIBCURL
777 curl_version(),
778 #endif
779 #ifndef USE_OPENSSL_BACKEND
780 version_gpgme, version_gcrypt,
781 #else
782 version_openssl,
783 #endif
784 version_expat, xmlParserVersion);
785 return buffer;
789 /* Return text description of ISDS error */
790 const char *isds_strerror(const isds_error error) {
791 switch (error) {
792 case IE_SUCCESS:
793 return(_("Success")); break;
794 case IE_ERROR:
795 return(_("Unspecified error")); break;
796 case IE_NOTSUP:
797 return(_("Not supported")); break;
798 case IE_INVAL:
799 return(_("Invalid value")); break;
800 case IE_INVALID_CONTEXT:
801 return(_("Invalid context")); break;
802 case IE_NOT_LOGGED_IN:
803 return(_("Not logged in")); break;
804 case IE_CONNECTION_CLOSED:
805 return(_("Connection closed")); break;
806 case IE_TIMED_OUT:
807 return(_("Timed out")); break;
808 case IE_NOEXIST:
809 return(_("Not exist")); break;
810 case IE_NOMEM:
811 return(_("Out of memory")); break;
812 case IE_NETWORK:
813 return(_("Network problem")); break;
814 case IE_HTTP:
815 return(_("HTTP problem")); break;
816 case IE_SOAP:
817 return(_("SOAP problem")); break;
818 case IE_XML:
819 return(_("XML problem")); break;
820 case IE_ISDS:
821 return(_("ISDS server problem")); break;
822 case IE_ENUM:
823 return(_("Invalid enum value")); break;
824 case IE_DATE:
825 return(_("Invalid date value")); break;
826 case IE_2BIG:
827 return(_("Too big")); break;
828 case IE_2SMALL:
829 return(_("Too small")); break;
830 case IE_NOTUNIQ:
831 return(_("Value not unique")); break;
832 case IE_NOTEQUAL:
833 return(_("Values not equal")); break;
834 case IE_PARTIAL_SUCCESS:
835 return(_("Some suboperations failed")); break;
836 case IE_ABORTED:
837 return(_("Operation aborted")); break;
838 case IE_SECURITY:
839 return(_("Security problem")); break;
840 default:
841 return(_("Unknown error"));
846 /* Create ISDS context.
847 * Each context can be used for different sessions to (possibly) different
848 * ISDS server with different credentials. */
849 struct isds_ctx *isds_ctx_create(void) {
850 struct isds_ctx *context;
851 context = malloc(sizeof(*context));
852 if (context) memset(context, 0, sizeof(*context));
853 return context;
856 #if HAVE_LIBCURL
857 /* Close possibly opened connection to Czech POINT document deposit without
858 * resetting long_message buffer.
859 * XXX: Do not use czp_close_connection() if you do not want to destroy log
860 * message.
861 * @context is Czech POINT session context. */
862 static isds_error czp_do_close_connection(struct isds_ctx *context) {
863 if (!context) return IE_INVALID_CONTEXT;
864 _isds_close_connection(context);
865 return IE_SUCCESS;
869 /* Discard credentials.
870 * @context is ISDS context
871 * @discard_saved_username is true for removing saved username, false for
872 * keeping it.
873 * Only that. It does not cause log out, connection close or similar. */
874 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context,
875 _Bool discard_saved_username) {
876 if(!context) return IE_INVALID_CONTEXT;
878 if (context->username) {
879 memset(context->username, 0, strlen(context->username));
880 zfree(context->username);
882 if (context->password) {
883 memset(context->password, 0, strlen(context->password));
884 zfree(context->password);
886 isds_pki_credentials_free(&context->pki_credentials);
887 if (discard_saved_username && context->saved_username) {
888 memset(context->saved_username, 0, strlen(context->saved_username));
889 zfree(context->saved_username);
892 return IE_SUCCESS;
894 #endif /* HAVE_LIBCURL */
897 /* Destroy ISDS context and free memory.
898 * @context will be NULLed on success. */
899 isds_error isds_ctx_free(struct isds_ctx **context) {
900 if (!context || !*context) {
901 return IE_INVALID_CONTEXT;
904 #if HAVE_LIBCURL
905 /* Discard credentials and close connection */
906 switch ((*context)->type) {
907 case CTX_TYPE_NONE: break;
908 case CTX_TYPE_ISDS: isds_logout(*context); break;
909 case CTX_TYPE_CZP:
910 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
911 czp_do_close_connection(*context); break;
914 /* For sure */
915 _isds_discard_credentials(*context, 1);
917 /* Free other structures */
918 free((*context)->url);
919 free((*context)->tls_verify_server);
920 free((*context)->tls_ca_file);
921 free((*context)->tls_ca_dir);
922 free((*context)->tls_crl_file);
923 #endif /* HAVE_LIBCURL */
924 free((*context)->long_message);
926 free(*context);
927 *context = NULL;
928 return IE_SUCCESS;
932 /* Return long message text produced by library function, e.g. detailed error
933 * message. Returned pointer is only valid until new library function is
934 * called for the same context. Could be NULL, especially if NULL context is
935 * supplied. Return string is locale encoded. */
936 char *isds_long_message(const struct isds_ctx *context) {
937 if (!context) return NULL;
938 return context->long_message;
942 /* Stores message into context' long_message buffer.
943 * Application can pick the message up using isds_long_message().
944 * NULL @message truncates the buffer but does not deallocate it.
945 * @message is coded in locale encoding */
946 _hidden isds_error isds_log_message(struct isds_ctx *context,
947 const char *message) {
948 char *buffer;
949 size_t length;
951 if (!context) return IE_INVALID_CONTEXT;
953 /* FIXME: Check for integer overflow */
954 length = 1 + ((message) ? strlen(message) : 0);
955 buffer = realloc(context->long_message, length);
956 if (!buffer) return IE_NOMEM;
958 if (message)
959 strcpy(buffer, message);
960 else
961 *buffer = '\0';
963 context->long_message = buffer;
964 return IE_SUCCESS;
968 /* Appends message into context' long_message buffer.
969 * Application can pick the message up using isds_long_message().
970 * NULL message has void effect. */
971 _hidden isds_error isds_append_message(struct isds_ctx *context,
972 const char *message) {
973 char *buffer;
974 size_t old_length, length;
976 if (!context) return IE_INVALID_CONTEXT;
977 if (!message) return IE_SUCCESS;
978 if (!context->long_message)
979 return isds_log_message(context, message);
981 old_length = strlen(context->long_message);
982 /* FIXME: Check for integer overflow */
983 length = 1 + old_length + strlen(message);
984 buffer = realloc(context->long_message, length);
985 if (!buffer) return IE_NOMEM;
987 strcpy(buffer + old_length, message);
989 context->long_message = buffer;
990 return IE_SUCCESS;
994 /* Stores formatted message into context' long_message buffer.
995 * Application can pick the message up using isds_long_message(). */
996 _hidden isds_error isds_printf_message(struct isds_ctx *context,
997 const char *format, ...) {
998 va_list ap;
999 int length;
1001 if (!context) return IE_INVALID_CONTEXT;
1002 va_start(ap, format);
1003 length = isds_vasprintf(&(context->long_message), format, ap);
1004 va_end(ap);
1006 return (length < 0) ? IE_ERROR: IE_SUCCESS;
1010 /* Set logging up.
1011 * @facilities is bit mask of isds_log_facility values,
1012 * @level is verbosity level. */
1013 void isds_set_logging(const unsigned int facilities,
1014 const isds_log_level level) {
1015 log_facilities = facilities;
1016 log_level = level;
1020 /* Register callback function libisds calls when new global log message is
1021 * produced by library. Library logs to stderr by default.
1022 * @callback is function provided by application libisds will call. See type
1023 * definition for @callback argument explanation. Pass NULL to revert logging to
1024 * default behaviour.
1025 * @data is application specific data @callback gets as last argument */
1026 void isds_set_log_callback(isds_log_callback callback, void *data) {
1027 log_callback = callback;
1028 log_callback_data = data;
1032 /* Log @message in class @facility with log @level into global log. @message
1033 * is printf(3) formatting string, variadic arguments may be necessary.
1034 * For debugging purposes. */
1035 _hidden isds_error isds_log(const isds_log_facility facility,
1036 const isds_log_level level, const char *message, ...) {
1037 va_list ap;
1038 char *buffer = NULL;
1039 int length;
1041 if (level > log_level) return IE_SUCCESS;
1042 if (!(log_facilities & facility)) return IE_SUCCESS;
1043 if (!message) return IE_INVAL;
1045 if (log_callback) {
1046 /* Pass message to application supplied callback function */
1047 va_start(ap, message);
1048 length = isds_vasprintf(&buffer, message, ap);
1049 va_end(ap);
1051 if (length == -1) {
1052 return IE_ERROR;
1054 if (length > 0) {
1055 log_callback(facility, level, buffer, length, log_callback_data);
1057 free(buffer);
1058 } else {
1059 /* Default: Log it to stderr */
1060 va_start(ap, message);
1061 vfprintf(stderr, message, ap);
1062 va_end(ap);
1063 /* Line buffered printf is default.
1064 * fflush(stderr);*/
1067 return IE_SUCCESS;
1071 /* Set timeout in milliseconds for each network job like connecting to server
1072 * or sending message. Use 0 to disable timeout limits. */
1073 isds_error isds_set_timeout(struct isds_ctx *context,
1074 const unsigned int timeout) {
1075 if (!context) return IE_INVALID_CONTEXT;
1076 zfree(context->long_message);
1078 #if HAVE_LIBCURL
1079 context->timeout = timeout;
1081 if (context->curl) {
1082 CURLcode curl_err;
1084 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
1085 if (!curl_err)
1086 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1087 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
1088 context->timeout);
1089 #else
1090 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
1091 context->timeout / 1000);
1092 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1093 if (curl_err) return IE_ERROR;
1096 return IE_SUCCESS;
1097 #else /* not HAVE_LIBCURL */
1098 return IE_NOTSUP;
1099 #endif
1103 /* Register callback function libisds calls periodically during HTTP data
1104 * transfer.
1105 * @context is session context
1106 * @callback is function provided by application libisds will call. See type
1107 * definition for @callback argument explanation.
1108 * @data is application specific data @callback gets as last argument */
1109 isds_error isds_set_progress_callback(struct isds_ctx *context,
1110 isds_progress_callback callback, void *data) {
1111 if (!context) return IE_INVALID_CONTEXT;
1112 zfree(context->long_message);
1114 #if HAVE_LIBCURL
1115 context->progress_callback = callback;
1116 context->progress_callback_data = data;
1118 return IE_SUCCESS;
1119 #else /* not HAVE_LIBCURL */
1120 return IE_NOTSUP;
1121 #endif
1125 /* Change context settings.
1126 * @context is context which setting will be applied to
1127 * @option is name of option. It determines the type of last argument. See
1128 * isds_option definition for more info.
1129 * @... is value of new setting. Type is determined by @option
1130 * */
1131 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1132 ...) {
1133 isds_error err = IE_SUCCESS;
1134 va_list ap;
1135 #if HAVE_LIBCURL
1136 char *pointer, *string;
1137 #endif
1139 if (!context) return IE_INVALID_CONTEXT;
1140 zfree(context->long_message);
1142 va_start(ap, option);
1144 #define REPLACE_VA_BOOLEAN(destination) { \
1145 if (!(destination)) { \
1146 (destination) = malloc(sizeof(*(destination))); \
1147 if (!(destination)) { \
1148 err = IE_NOMEM; goto leave; \
1151 *(destination) = (_Bool) !!va_arg(ap, int); \
1154 #define REPLACE_VA_STRING(destination) { \
1155 string = va_arg(ap, char *); \
1156 if (string) { \
1157 pointer = realloc((destination), 1 + strlen(string)); \
1158 if (!pointer) { err = IE_NOMEM; goto leave; } \
1159 strcpy(pointer, string); \
1160 (destination) = pointer; \
1161 } else { \
1162 free(destination); \
1163 (destination) = NULL; \
1167 switch (option) {
1168 case IOPT_TLS_VERIFY_SERVER:
1169 #if HAVE_LIBCURL
1170 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1171 #else
1172 err = IE_NOTSUP; goto leave;
1173 #endif
1174 break;
1175 case IOPT_TLS_CA_FILE:
1176 #if HAVE_LIBCURL
1177 REPLACE_VA_STRING(context->tls_ca_file);
1178 #else
1179 err = IE_NOTSUP; goto leave;
1180 #endif
1181 break;
1182 case IOPT_TLS_CA_DIRECTORY:
1183 #if HAVE_LIBCURL
1184 REPLACE_VA_STRING(context->tls_ca_dir);
1185 #else
1186 err = IE_NOTSUP; goto leave;
1187 #endif
1188 break;
1189 case IOPT_TLS_CRL_FILE:
1190 #if HAVE_LIBCURL
1191 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1192 REPLACE_VA_STRING(context->tls_crl_file);
1193 #else
1194 isds_log_message(context,
1195 _("Curl library does not support CRL definition"));
1196 err = IE_NOTSUP;
1197 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1198 #else
1199 err = IE_NOTSUP; goto leave;
1200 #endif /* not HAVE_LIBCURL */
1201 break;
1202 case IOPT_NORMALIZE_MIME_TYPE:
1203 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1204 break;
1206 default:
1207 err = IE_ENUM; goto leave;
1210 #undef REPLACE_VA_STRING
1211 #undef REPLACE_VA_BOOLEAN
1213 leave:
1214 va_end(ap);
1215 return err;
1219 #if HAVE_LIBCURL
1220 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1221 * Destination for NULL argument will not be touched.
1222 * Destination pointers must be freed before calling this function.
1223 * If @username is @context->saved_username, the saved_username will not be
1224 * replaced. The saved_username is clobbered only if context has set otp
1225 * member.
1226 * Return IE_SUCCESS on success. */
1227 static isds_error _isds_store_credentials(struct isds_ctx *context,
1228 const char *username, const char *password,
1229 const struct isds_pki_credentials *pki_credentials) {
1230 if (NULL == context) return IE_INVALID_CONTEXT;
1232 /* FIXME: mlock password
1233 * (I have a library) */
1235 if (username) {
1236 context->username = strdup(username);
1237 if (context->otp && context->saved_username != username)
1238 context->saved_username = strdup(username);
1240 if (password) {
1241 if (NULL == context->otp_credentials)
1242 context->password = strdup(password);
1243 else
1244 context->password = _isds_astrcat(password,
1245 context->otp_credentials->otp_code);
1247 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1249 if ((NULL != username && NULL == context->username) ||
1250 (NULL != password && NULL == context->password) ||
1251 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1252 (context->otp && NULL != context->username &&
1253 NULL == context->saved_username)) {
1254 return IE_NOMEM;
1257 return IE_SUCCESS;
1259 #endif
1262 /* Connect and log into ISDS server.
1263 * All required arguments will be copied, you do not have to keep them after
1264 * that.
1265 * ISDS supports six different authentication methods. Exact method is
1266 * selected on @username, @password, @pki_credentials, and @otp arguments:
1267 * - If @pki_credentials == NULL, @username and @password must be supplied
1268 * and then
1269 * - If @otp == NULL, simple authentication by username and password will
1270 * be proceeded.
1271 * - If @otp != NULL, authentication by username and password and OTP
1272 * will be used.
1273 * - If @pki_credentials != NULL, then
1274 * - If @username == NULL, only certificate will be used
1275 * - If @username != NULL, then
1276 * - If @password == NULL, then certificate will be used and
1277 * @username shifts meaning to box ID. This is used for hosted
1278 * services.
1279 * - Otherwise all three arguments will be used.
1280 * Please note, that different cases require different certificate type
1281 * (system qualified one or commercial non qualified one). This library
1282 * does not check such political issues. Please see ISDS Specification
1283 * for more details.
1284 * @url is base address of ISDS web service. Pass extern isds_locator
1285 * variable to use production ISDS instance without client certificate
1286 * authentication (or extern isds_cert_locator with client certificate
1287 * authentication or extern isds_otp_locators with OTP authentication).
1288 * Passing NULL has the same effect, autoselection between isds_locator,
1289 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1290 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1291 * isds_otp_testing_locator) variable to select testing instance.
1292 * @username is user name of ISDS user or box ID
1293 * @password is user's secret password
1294 * @pki_credentials defines public key cryptographic material to use in client
1295 * authentication.
1296 * @otp selects one-time password authentication method to use, defines OTP
1297 * code (if known) and returns fine grade resolution of OTP procedure.
1298 * @return:
1299 * IE_SUCCESS if authentication succeeds
1300 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1301 * requested, fine grade reason will be set into @otp->resolution. Error
1302 * message from server can be obtained by isds_long_message() call.
1303 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1304 * server has sent OTP code through side channel. Application is expected to
1305 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1306 * this call to complete second phase of TOTP authentication;
1307 * or other appropriate error. */
1308 isds_error isds_login(struct isds_ctx *context, const char *url,
1309 const char *username, const char *password,
1310 const struct isds_pki_credentials *pki_credentials,
1311 struct isds_otp *otp) {
1312 #if HAVE_LIBCURL
1313 isds_error err = IE_NOT_LOGGED_IN;
1314 isds_error soap_err;
1315 xmlNsPtr isds_ns = NULL;
1316 xmlNodePtr request = NULL;
1317 #endif /* HAVE_LIBCURL */
1319 if (!context) return IE_INVALID_CONTEXT;
1320 zfree(context->long_message);
1322 #if HAVE_LIBCURL
1323 /* Close connection if already logged in */
1324 if (context->curl) {
1325 _isds_close_connection(context);
1328 /* Store configuration */
1329 context->type = CTX_TYPE_ISDS;
1330 zfree(context->url);
1332 /* Mangle base URI according to requested authentication method */
1333 if (NULL == pki_credentials) {
1334 isds_log(ILF_SEC, ILL_INFO,
1335 _("Selected authentication method: no certificate, "
1336 "username and password\n"));
1337 if (!username || !password) {
1338 isds_log_message(context,
1339 _("Both username and password must be supplied"));
1340 return IE_INVAL;
1342 context->otp_credentials = otp;
1343 context->otp = (NULL != context->otp_credentials);
1345 if (!context->otp) {
1346 /* Default locator is official system (without certificate or
1347 * OTP) */
1348 context->url = strdup((NULL != url) ? url : isds_locator);
1349 } else {
1350 const char *authenticator_uri = NULL;
1351 if (!url) url = isds_otp_locator;
1352 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1353 switch (context->otp_credentials->method) {
1354 case OTP_HMAC:
1355 isds_log(ILF_SEC, ILL_INFO,
1356 _("Selected authentication method: "
1357 "HMAC-based one-time password\n"));
1358 authenticator_uri =
1359 "%sas/processLogin?type=hotp&uri=%sapps/";
1360 break;
1361 case OTP_TIME:
1362 isds_log(ILF_SEC, ILL_INFO,
1363 _("Selected authentication method: "
1364 "Time-based one-time password\n"));
1365 if (context->otp_credentials->otp_code == NULL) {
1366 isds_log(ILF_SEC, ILL_INFO,
1367 _("OTP code has not been provided by "
1368 "application, requesting server for "
1369 "new one.\n"));
1370 authenticator_uri =
1371 "%sas/processLogin?type=totp&sendSms=true&"
1372 "uri=%sapps/";
1373 } else {
1374 isds_log(ILF_SEC, ILL_INFO,
1375 _("OTP code has been provided by "
1376 "application, not requesting server "
1377 "for new one.\n"));
1378 authenticator_uri =
1379 "%sas/processLogin?type=totp&"
1380 "uri=%sapps/";
1382 break;
1383 default:
1384 isds_log_message(context,
1385 _("Unknown one-time password authentication "
1386 "method requested by application"));
1387 return IE_ENUM;
1389 if (-1 == isds_asprintf(&context->url, authenticator_uri, url, url))
1390 return IE_NOMEM;
1392 } else {
1393 /* Default locator is official system (with client certificate) */
1394 context->otp = 0;
1395 context->otp_credentials = NULL;
1396 if (!url) url = isds_cert_locator;
1398 if (!username) {
1399 isds_log(ILF_SEC, ILL_INFO,
1400 _("Selected authentication method: system certificate, "
1401 "no username and no password\n"));
1402 password = NULL;
1403 context->url = _isds_astrcat(url, "cert/");
1404 } else {
1405 if (!password) {
1406 isds_log(ILF_SEC, ILL_INFO,
1407 _("Selected authentication method: system certificate, "
1408 "box ID and no password\n"));
1409 context->url = _isds_astrcat(url, "hspis/");
1410 } else {
1411 isds_log(ILF_SEC, ILL_INFO,
1412 _("Selected authentication method: commercial "
1413 "certificate, username and password\n"));
1414 context->url = _isds_astrcat(url, "certds/");
1418 if (!(context->url))
1419 return IE_NOMEM;
1421 /* Prepare CURL handle */
1422 context->curl = curl_easy_init();
1423 if (!(context->curl))
1424 return IE_ERROR;
1426 /* Build log-in request */
1427 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1428 if (!request) {
1429 isds_log_message(context, _("Could not build ISDS log-in request"));
1430 return IE_ERROR;
1432 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1433 if(!isds_ns) {
1434 isds_log_message(context, _("Could not create ISDS name space"));
1435 xmlFreeNode(request);
1436 return IE_ERROR;
1438 xmlSetNs(request, isds_ns);
1440 /* Store credentials */
1441 _isds_discard_credentials(context, 1);
1442 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1443 _isds_discard_credentials(context, 1);
1444 xmlFreeNode(request);
1445 return IE_NOMEM;
1448 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1449 username, url);
1451 /* XXX: ISDS documentation does not specify response body for
1452 * DummyOperation request. However real server sends back
1453 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1454 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1455 * SOAP body content, e.g. the dmStatus element. */
1457 /* Send log-in request */
1458 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1460 if (context->otp) {
1461 /* Revert context URL from OTP authentication service URL to OTP web
1462 * service base URL for subsequent calls. Potenial isds_login() retry
1463 * will re-set context URL again. */
1464 zfree(context->url);
1465 context->url = _isds_astrcat(url, "apps/");
1466 if (context->url == NULL) {
1467 soap_err = IE_NOMEM;
1469 /* Detach pointer to OTP credentials from context */
1470 context->otp_credentials = NULL;
1473 /* Remove credentials */
1474 _isds_discard_credentials(context, 0);
1476 /* Destroy log-in request */
1477 xmlFreeNode(request);
1479 if (soap_err) {
1480 _isds_close_connection(context);
1481 return soap_err;
1484 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1485 * authentication succeeded if soap_err == IE_SUCCESS */
1486 err = IE_SUCCESS;
1488 if (!err)
1489 isds_log(ILF_ISDS, ILL_DEBUG,
1490 _("User %s has been logged into server %s successfully\n"),
1491 username, url);
1492 return err;
1493 #else /* not HAVE_LIBCURL */
1494 return IE_NOTSUP;
1495 #endif
1499 /* Log out from ISDS server discards credentials and connection configuration. */
1500 isds_error isds_logout(struct isds_ctx *context) {
1501 if (!context) return IE_INVALID_CONTEXT;
1502 zfree(context->long_message);
1504 #if HAVE_LIBCURL
1505 if (context->curl) {
1506 if (context->otp) {
1507 isds_error err = _isds_invalidate_otp_cookie(context);
1508 if (err) return err;
1511 /* Close connection */
1512 _isds_close_connection(context);
1514 /* Discard credentials for sure. They should not survive isds_login(),
1515 * even successful .*/
1516 _isds_discard_credentials(context, 1);
1518 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1519 } else {
1520 _isds_discard_credentials(context, 1);
1522 zfree(context->url);
1523 return IE_SUCCESS;
1524 #else /* not HAVE_LIBCURL */
1525 return IE_NOTSUP;
1526 #endif
1530 /* Verify connection to ISDS is alive and server is responding.
1531 * Send dummy request to ISDS and expect dummy response. */
1532 isds_error isds_ping(struct isds_ctx *context) {
1533 #if HAVE_LIBCURL
1534 isds_error soap_err;
1535 xmlNsPtr isds_ns = NULL;
1536 xmlNodePtr request = NULL;
1537 #endif /* HAVE_LIBCURL */
1539 if (!context) return IE_INVALID_CONTEXT;
1540 zfree(context->long_message);
1542 #if HAVE_LIBCURL
1543 /* Check if connection is established */
1544 if (!context->curl) return IE_CONNECTION_CLOSED;
1547 /* Build dummy request */
1548 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1549 if (!request) {
1550 isds_log_message(context, _("Could build ISDS dummy request"));
1551 return IE_ERROR;
1553 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1554 if(!isds_ns) {
1555 isds_log_message(context, _("Could not create ISDS name space"));
1556 xmlFreeNode(request);
1557 return IE_ERROR;
1559 xmlSetNs(request, isds_ns);
1561 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1563 /* XXX: ISDS documentation does not specify response body for
1564 * DummyOperation request. However real server sends back
1565 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1566 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1567 * SOAP body content, e.g. the dmStatus element. */
1569 /* Send dummy request */
1570 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1572 /* Destroy log-in request */
1573 xmlFreeNode(request);
1575 if (soap_err) {
1576 isds_log(ILF_ISDS, ILL_DEBUG,
1577 _("ISDS server could not be contacted\n"));
1578 return soap_err;
1581 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1582 * authentication succeeded if soap_err == IE_SUCCESS */
1585 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1587 return IE_SUCCESS;
1588 #else /* not HAVE_LIBCURL */
1589 return IE_NOTSUP;
1590 #endif
1594 /* Send bogus request to ISDS.
1595 * Just for test purposes */
1596 isds_error isds_bogus_request(struct isds_ctx *context) {
1597 #if HAVE_LIBCURL
1598 isds_error err;
1599 xmlNsPtr isds_ns = NULL;
1600 xmlNodePtr request = NULL;
1601 xmlDocPtr response = NULL;
1602 xmlChar *code = NULL, *message = NULL;
1603 #endif
1605 if (!context) return IE_INVALID_CONTEXT;
1606 zfree(context->long_message);
1608 #if HAVE_LIBCURL
1609 /* Check if connection is established */
1610 if (!context->curl) {
1611 /* Testing printf message */
1612 isds_printf_message(context, "%s", _("I said connection closed"));
1613 return IE_CONNECTION_CLOSED;
1617 /* Build dummy request */
1618 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1619 if (!request) {
1620 isds_log_message(context, _("Could build ISDS bogus request"));
1621 return IE_ERROR;
1623 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1624 if(!isds_ns) {
1625 isds_log_message(context, _("Could not create ISDS name space"));
1626 xmlFreeNode(request);
1627 return IE_ERROR;
1629 xmlSetNs(request, isds_ns);
1631 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1633 /* Sent bogus request */
1634 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1636 /* Destroy request */
1637 xmlFreeNode(request);
1639 if (err) {
1640 isds_log(ILF_ISDS, ILL_DEBUG,
1641 _("Processing ISDS response on bogus request failed\n"));
1642 xmlFreeDoc(response);
1643 return err;
1646 /* Check for response status */
1647 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1648 &code, &message, NULL);
1649 if (err) {
1650 isds_log(ILF_ISDS, ILL_DEBUG,
1651 _("ISDS response on bogus request is missing status\n"));
1652 free(code);
1653 free(message);
1654 xmlFreeDoc(response);
1655 return err;
1657 if (xmlStrcmp(code, BAD_CAST "0000")) {
1658 char *code_locale = _isds_utf82locale((char*)code);
1659 char *message_locale = _isds_utf82locale((char*)message);
1660 isds_log(ILF_ISDS, ILL_DEBUG,
1661 _("Server refused bogus request (code=%s, message=%s)\n"),
1662 code_locale, message_locale);
1663 /* XXX: Literal error messages from ISDS are Czech messages
1664 * (English sometimes) in UTF-8. It's hard to catch them for
1665 * translation. Successfully gettextized would return in locale
1666 * encoding, unsuccessfully translated would pass in UTF-8. */
1667 isds_log_message(context, message_locale);
1668 free(code_locale);
1669 free(message_locale);
1670 free(code);
1671 free(message);
1672 xmlFreeDoc(response);
1673 return IE_ISDS;
1677 free(code);
1678 free(message);
1679 xmlFreeDoc(response);
1681 isds_log(ILF_ISDS, ILL_DEBUG,
1682 _("Bogus message accepted by server. This should not happen.\n"));
1684 return IE_SUCCESS;
1685 #else /* not HAVE_LIBCURL */
1686 return IE_NOTSUP;
1687 #endif
1691 #if HAVE_LIBCURL
1692 /* Serialize XML subtree to buffer preserving XML indentation.
1693 * @context is session context
1694 * @subtree is XML element to be serialized (with children)
1695 * @buffer is automatically reallocated buffer where serialize to
1696 * @length is size of serialized stream in bytes
1697 * @return standard error code, free @buffer in case of error */
1698 static isds_error serialize_subtree(struct isds_ctx *context,
1699 xmlNodePtr subtree, void **buffer, size_t *length) {
1700 isds_error err = IE_SUCCESS;
1701 xmlBufferPtr xml_buffer = NULL;
1702 xmlSaveCtxtPtr save_ctx = NULL;
1703 xmlDocPtr subtree_doc = NULL;
1704 xmlNodePtr subtree_copy;
1705 xmlNsPtr isds_ns;
1706 void *new_buffer;
1708 if (!context) return IE_INVALID_CONTEXT;
1709 if (!buffer) return IE_INVAL;
1710 zfree(*buffer);
1711 if (!subtree || !length) return IE_INVAL;
1713 /* Make temporary XML document with @subtree root element */
1714 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1715 * It can result in not well-formed on invalid XML tree (e.g. name space
1716 * prefix definition can miss. */
1717 /*FIXME */
1719 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1720 if (!subtree_doc) {
1721 isds_log_message(context, _("Could not build temporary document"));
1722 err = IE_ERROR;
1723 goto leave;
1726 /* XXX: Copy subtree and attach the copy to document.
1727 * One node can not bee attached into more document at the same time.
1728 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1729 * automatically.
1730 * XXX: Check xmlSaveTree() too. */
1731 subtree_copy = xmlCopyNodeList(subtree);
1732 if (!subtree_copy) {
1733 isds_log_message(context, _("Could not copy subtree"));
1734 err = IE_ERROR;
1735 goto leave;
1737 xmlDocSetRootElement(subtree_doc, subtree_copy);
1739 /* Only this way we get namespace definition as @xmlns:isds,
1740 * otherwise we get namespace prefix without definition */
1741 /* FIXME: Don't overwrite original default namespace */
1742 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1743 if(!isds_ns) {
1744 isds_log_message(context, _("Could not create ISDS name space"));
1745 err = IE_ERROR;
1746 goto leave;
1748 xmlSetNs(subtree_copy, isds_ns);
1751 /* Serialize the document into buffer */
1752 xml_buffer = xmlBufferCreate();
1753 if (!xml_buffer) {
1754 isds_log_message(context, _("Could not create xmlBuffer"));
1755 err = IE_ERROR;
1756 goto leave;
1758 /* Last argument 0 means to not format the XML tree */
1759 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1760 if (!save_ctx) {
1761 isds_log_message(context, _("Could not create XML serializer"));
1762 err = IE_ERROR;
1763 goto leave;
1765 /* XXX: According LibXML documentation, this function does not return
1766 * meaningful value yet */
1767 xmlSaveDoc(save_ctx, subtree_doc);
1768 if (-1 == xmlSaveFlush(save_ctx)) {
1769 isds_log_message(context,
1770 _("Could not serialize XML subtree"));
1771 err = IE_ERROR;
1772 goto leave;
1774 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1775 * even after xmlSaveFlush(). Thus close it here */
1776 xmlSaveClose(save_ctx); save_ctx = NULL;
1779 /* Store and detach buffer from xml_buffer */
1780 *buffer = xml_buffer->content;
1781 *length = xml_buffer->use;
1782 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1784 /* Shrink buffer */
1785 new_buffer = realloc(*buffer, *length);
1786 if (new_buffer) *buffer = new_buffer;
1788 leave:
1789 if (err) {
1790 zfree(*buffer);
1791 *length = 0;
1794 xmlSaveClose(save_ctx);
1795 xmlBufferFree(xml_buffer);
1796 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1797 return err;
1799 #endif /* HAVE_LIBCURL */
1802 #if 0
1803 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1804 * @context is session context
1805 * @document is original document where @nodeset points to
1806 * @nodeset is XPath node set to dump (recursively)
1807 * @buffer is automatically reallocated buffer where serialize to
1808 * @length is size of serialized stream in bytes
1809 * @return standard error code, free @buffer in case of error */
1810 static isds_error dump_nodeset(struct isds_ctx *context,
1811 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1812 void **buffer, size_t *length) {
1813 isds_error err = IE_SUCCESS;
1814 xmlBufferPtr xml_buffer = NULL;
1815 void *new_buffer;
1817 if (!context) return IE_INVALID_CONTEXT;
1818 if (!buffer) return IE_INVAL;
1819 zfree(*buffer);
1820 if (!document || !nodeset || !length) return IE_INVAL;
1821 *length = 0;
1823 /* Empty node set results into NULL buffer */
1824 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1825 goto leave;
1828 /* Resulting the document into buffer */
1829 xml_buffer = xmlBufferCreate();
1830 if (!xml_buffer) {
1831 isds_log_message(context, _("Could not create xmlBuffer"));
1832 err = IE_ERROR;
1833 goto leave;
1836 /* Iterate over all nodes */
1837 for (int i = 0; i < nodeset->nodeNr; i++) {
1838 /* Serialize node.
1839 * XXX: xmlNodeDump() appends to xml_buffer. */
1840 if (-1 ==
1841 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1842 isds_log_message(context, _("Could not dump XML node"));
1843 err = IE_ERROR;
1844 goto leave;
1848 /* Store and detach buffer from xml_buffer */
1849 *buffer = xml_buffer->content;
1850 *length = xml_buffer->use;
1851 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1853 /* Shrink buffer */
1854 new_buffer = realloc(*buffer, *length);
1855 if (new_buffer) *buffer = new_buffer;
1858 leave:
1859 if (err) {
1860 zfree(*buffer);
1861 *length = 0;
1864 xmlBufferFree(xml_buffer);
1865 return err;
1867 #endif
1869 #if 0
1870 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1871 * @context is session context
1872 * @document is original document where @nodeset points to
1873 * @nodeset is XPath node set to dump (recursively)
1874 * @buffer is automatically reallocated buffer where serialize to
1875 * @length is size of serialized stream in bytes
1876 * @return standard error code, free @buffer in case of error */
1877 static isds_error dump_nodeset(struct isds_ctx *context,
1878 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1879 void **buffer, size_t *length) {
1880 isds_error err = IE_SUCCESS;
1881 xmlBufferPtr xml_buffer = NULL;
1882 xmlSaveCtxtPtr save_ctx = NULL;
1883 void *new_buffer;
1885 if (!context) return IE_INVALID_CONTEXT;
1886 if (!buffer) return IE_INVAL;
1887 zfree(*buffer);
1888 if (!document || !nodeset || !length) return IE_INVAL;
1889 *length = 0;
1891 /* Empty node set results into NULL buffer */
1892 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1893 goto leave;
1896 /* Resulting the document into buffer */
1897 xml_buffer = xmlBufferCreate();
1898 if (!xml_buffer) {
1899 isds_log_message(context, _("Could not create xmlBuffer"));
1900 err = IE_ERROR;
1901 goto leave;
1903 if (xmlSubstituteEntitiesDefault(1)) {
1904 isds_log_message(context, _("Could not disable attribute escaping"));
1905 err = IE_ERROR;
1906 goto leave;
1908 /* Last argument means:
1909 * 0 to not format the XML tree
1910 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1911 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1912 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1913 if (!save_ctx) {
1914 isds_log_message(context, _("Could not create XML serializer"));
1915 err = IE_ERROR;
1916 goto leave;
1918 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1919 isds_log_message(context, _("Could not disable attribute escaping"));
1920 err = IE_ERROR;
1921 goto leave;
1925 /* Iterate over all nodes */
1926 for (int i = 0; i < nodeset->nodeNr; i++) {
1927 /* Serialize node.
1928 * XXX: xmlNodeDump() appends to xml_buffer. */
1929 /*if (-1 ==
1930 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1932 /* XXX: According LibXML documentation, this function does not return
1933 * meaningful value yet */
1934 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1935 if (-1 == xmlSaveFlush(save_ctx)) {
1936 isds_log_message(context,
1937 _("Could not serialize XML subtree"));
1938 err = IE_ERROR;
1939 goto leave;
1943 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1944 * even after xmlSaveFlush(). Thus close it here */
1945 xmlSaveClose(save_ctx); save_ctx = NULL;
1947 /* Store and detach buffer from xml_buffer */
1948 *buffer = xml_buffer->content;
1949 *length = xml_buffer->use;
1950 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1952 /* Shrink buffer */
1953 new_buffer = realloc(*buffer, *length);
1954 if (new_buffer) *buffer = new_buffer;
1956 leave:
1957 if (err) {
1958 zfree(*buffer);
1959 *length = 0;
1962 xmlSaveClose(save_ctx);
1963 xmlBufferFree(xml_buffer);
1964 return err;
1966 #endif
1969 #if HAVE_LIBCURL
1970 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1971 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1972 if (!string || !type) return IE_INVAL;
1974 if (!xmlStrcmp(string, BAD_CAST "FO"))
1975 *type = DBTYPE_FO;
1976 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1977 *type = DBTYPE_PFO;
1978 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1979 *type = DBTYPE_PFO_ADVOK;
1980 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1981 *type = DBTYPE_PFO_DANPOR;
1982 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1983 *type = DBTYPE_PFO_INSSPR;
1984 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1985 *type = DBTYPE_PO;
1986 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1987 *type = DBTYPE_PO_ZAK;
1988 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1989 *type = DBTYPE_PO_REQ;
1990 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1991 *type = DBTYPE_OVM;
1992 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1993 *type = DBTYPE_OVM_NOTAR;
1994 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1995 *type = DBTYPE_OVM_EXEKUT;
1996 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1997 *type = DBTYPE_OVM_REQ;
1998 else
1999 return IE_ENUM;
2000 return IE_SUCCESS;
2004 /* Convert ISDS dbType enum @type to UTF-8 string.
2005 * @Return pointer to static string, or NULL if unknown enum value */
2006 static const xmlChar *isds_DbType2string(const isds_DbType type) {
2007 switch(type) {
2008 /* DBTYPE_SYSTEM is invalid value from point of view of public
2009 * SOAP interface. */
2010 case DBTYPE_FO: return(BAD_CAST "FO"); break;
2011 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
2012 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
2013 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
2014 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
2015 case DBTYPE_PO: return(BAD_CAST "PO"); break;
2016 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
2017 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
2018 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
2019 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
2020 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
2021 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
2022 default: return NULL; break;
2027 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2028 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
2029 if (!string || !type) return IE_INVAL;
2031 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2032 *type = USERTYPE_PRIMARY;
2033 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2034 *type = USERTYPE_ENTRUSTED;
2035 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2036 *type = USERTYPE_ADMINISTRATOR;
2037 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2038 *type = USERTYPE_OFFICIAL;
2039 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2040 *type = USERTYPE_OFFICIAL_CERT;
2041 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2042 *type = USERTYPE_LIQUIDATOR;
2043 else
2044 return IE_ENUM;
2045 return IE_SUCCESS;
2049 /* Convert ISDS userType enum @type to UTF-8 string.
2050 * @Return pointer to static string, or NULL if unknown enum value */
2051 static const xmlChar *isds_UserType2string(const isds_UserType type) {
2052 switch(type) {
2053 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
2054 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
2055 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
2056 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
2057 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
2058 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
2059 default: return NULL; break;
2064 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2065 static isds_error string2isds_sender_type(const xmlChar *string,
2066 isds_sender_type *type) {
2067 if (!string || !type) return IE_INVAL;
2069 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2070 *type = SENDERTYPE_PRIMARY;
2071 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2072 *type = SENDERTYPE_ENTRUSTED;
2073 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2074 *type = SENDERTYPE_ADMINISTRATOR;
2075 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2076 *type = SENDERTYPE_OFFICIAL;
2077 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2078 *type = SENDERTYPE_VIRTUAL;
2079 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2080 *type = SENDERTYPE_OFFICIAL_CERT;
2081 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2082 *type = SENDERTYPE_LIQUIDATOR;
2083 else
2084 return IE_ENUM;
2085 return IE_SUCCESS;
2089 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2090 static isds_error string2isds_payment_type(const xmlChar *string,
2091 isds_payment_type *type) {
2092 if (!string || !type) return IE_INVAL;
2094 if (!xmlStrcmp(string, BAD_CAST "K"))
2095 *type = PAYMENT_SENDER;
2096 else if (!xmlStrcmp(string, BAD_CAST "O"))
2097 *type = PAYMENT_RESPONSE;
2098 else if (!xmlStrcmp(string, BAD_CAST "G"))
2099 *type = PAYMENT_SPONSOR;
2100 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2101 *type = PAYMENT_SPONSOR_LIMITED;
2102 else if (!xmlStrcmp(string, BAD_CAST "D"))
2103 *type = PAYMENT_SPONSOR_EXTERNAL;
2104 else if (!xmlStrcmp(string, BAD_CAST "E"))
2105 *type = PAYMENT_STAMP;
2106 else
2107 return IE_ENUM;
2108 return IE_SUCCESS;
2112 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2113 * ciEventType is integer but we convert it from string representation
2114 * directly. */
2115 static isds_error string2isds_credit_event_type(const xmlChar *string,
2116 isds_credit_event_type *type) {
2117 if (!string || !type) return IE_INVAL;
2119 if (!xmlStrcmp(string, BAD_CAST "1"))
2120 *type = ISDS_CREDIT_CHARGED;
2121 else if (!xmlStrcmp(string, BAD_CAST "2"))
2122 *type = ISDS_CREDIT_DISCHARGED;
2123 else if (!xmlStrcmp(string, BAD_CAST "3"))
2124 *type = ISDS_CREDIT_MESSAGE_SENT;
2125 else if (!xmlStrcmp(string, BAD_CAST "4"))
2126 *type = ISDS_CREDIT_STORAGE_SET;
2127 else if (!xmlStrcmp(string, BAD_CAST "5"))
2128 *type = ISDS_CREDIT_EXPIRED;
2129 else
2130 return IE_ENUM;
2131 return IE_SUCCESS;
2135 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2136 * @Return pointer to static string, or NULL if unknown enum value */
2137 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2138 switch(type) {
2139 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2140 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2141 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2142 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2143 default: return NULL; break;
2148 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2149 * ISDSSearch2/searchType value.
2150 * @Return pointer to static string, or NULL if unknown enum value */
2151 static const xmlChar *isds_fulltext_target2string(
2152 const isds_fulltext_target type) {
2153 switch(type) {
2154 case FULLTEXT_ALL: return(BAD_CAST "GENERAL"); break;
2155 case FULLTEXT_ADDRESS: return(BAD_CAST "ADDRESS"); break;
2156 case FULLTEXT_IC: return(BAD_CAST "ICO"); break;
2157 case FULLTEXT_BOX_ID: return(BAD_CAST "DBID"); break;
2158 default: return NULL; break;
2161 #endif /* HAVE_LIBCURL */
2164 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2165 * @Return IE_ENUM if @string is not valid enum member */
2166 static isds_error string2isds_FileMetaType(const xmlChar *string,
2167 isds_FileMetaType *type) {
2168 if (!string || !type) return IE_INVAL;
2170 if (!xmlStrcmp(string, BAD_CAST "main"))
2171 *type = FILEMETATYPE_MAIN;
2172 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2173 *type = FILEMETATYPE_ENCLOSURE;
2174 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2175 *type = FILEMETATYPE_SIGNATURE;
2176 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2177 *type = FILEMETATYPE_META;
2178 else
2179 return IE_ENUM;
2180 return IE_SUCCESS;
2184 /* Convert UTF-8 @string to ISDS hash @algorithm.
2185 * @Return IE_ENUM if @string is not valid enum member */
2186 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2187 isds_hash_algorithm *algorithm) {
2188 if (!string || !algorithm) return IE_INVAL;
2190 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2191 *algorithm = HASH_ALGORITHM_MD5;
2192 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2193 *algorithm = HASH_ALGORITHM_SHA_1;
2194 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2195 *algorithm = HASH_ALGORITHM_SHA_224;
2196 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2197 *algorithm = HASH_ALGORITHM_SHA_256;
2198 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2199 *algorithm = HASH_ALGORITHM_SHA_384;
2200 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2201 *algorithm = HASH_ALGORITHM_SHA_512;
2202 else
2203 return IE_ENUM;
2204 return IE_SUCCESS;
2208 #if HAVE_LIBCURL
2209 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2210 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2211 if (!time || !string) return IE_INVAL;
2213 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2214 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2215 return IE_ERROR;
2217 return IE_SUCCESS;
2221 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2222 * respects the @time microseconds too. */
2223 static isds_error timeval2timestring(const struct timeval *time,
2224 xmlChar **string) {
2225 struct tm broken;
2227 if (!time || !string) return IE_INVAL;
2229 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
2230 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2232 /* TODO: small negative year should be formatted as "-0012". This is not
2233 * true for glibc "%04d". We should implement it.
2234 * time->tv_usec type is su_seconds_t which is required to be signed
2235 * integer to accomodate values from range [-1, 1000000].
2236 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2237 if (-1 == isds_asprintf((char **) string,
2238 "%04d-%02d-%02dT%02d:%02d:%02d.%06jd",
2239 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2240 broken.tm_hour, broken.tm_min, broken.tm_sec,
2241 (intmax_t)time->tv_usec))
2242 return IE_ERROR;
2244 return IE_SUCCESS;
2246 #endif /* HAVE_LIBCURL */
2249 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2250 * It respects microseconds too. Microseconds are rounded half up.
2251 * In case of error, @time will be freed. */
2252 static isds_error timestring2timeval(const xmlChar *string,
2253 struct timeval **time) {
2254 struct tm broken;
2255 char *offset, *delim, *endptr;
2256 const int subsecond_resolution = 6;
2257 char subseconds[subsecond_resolution + 1];
2258 _Bool round_up = 0;
2259 int offset_hours, offset_minutes;
2260 int i;
2261 long int long_number;
2262 #ifdef _WIN32
2263 int tmp;
2264 #endif
2266 if (!time) return IE_INVAL;
2267 if (!string) {
2268 zfree(*time);
2269 return IE_INVAL;
2272 memset(&broken, 0, sizeof(broken));
2274 if (!*time) {
2275 *time = calloc(1, sizeof(**time));
2276 if (!*time) return IE_NOMEM;
2277 } else {
2278 memset(*time, 0, sizeof(**time));
2282 /* xsd:date is ISO 8601 string, thus ASCII */
2283 /*TODO: negative year */
2285 #ifdef _WIN32
2286 i = 0;
2287 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2288 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2289 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2290 &i)) < 6) {
2291 zfree(*time);
2292 return IE_DATE;
2295 broken.tm_year -= 1900;
2296 broken.tm_mon--;
2297 offset = (char*)string + i;
2298 #else
2299 /* Parse date and time without subseconds and offset */
2300 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2301 if (!offset) {
2302 zfree(*time);
2303 return IE_DATE;
2305 #endif
2307 /* Get subseconds */
2308 if (*offset == '.' ) {
2309 offset++;
2311 /* Copy first 6 digits, pad it with zeros.
2312 * Current server implementation uses only millisecond resolution. */
2313 /* TODO: isdigit() is locale sensitive */
2314 for (i = 0;
2315 i < subsecond_resolution && isdigit(*offset);
2316 i++, offset++) {
2317 subseconds[i] = *offset;
2319 if (subsecond_resolution == i && isdigit(*offset)) {
2320 /* Check 7th digit for rounding */
2321 if (*offset >= '5') round_up = 1;
2322 offset++;
2324 for (; i < subsecond_resolution; i++) {
2325 subseconds[i] = '0';
2327 subseconds[subsecond_resolution] = '\0';
2329 /* Convert it into integer */
2330 long_number = strtol(subseconds, &endptr, 10);
2331 if (*endptr != '\0' || long_number == LONG_MIN ||
2332 long_number == LONG_MAX) {
2333 zfree(*time);
2334 return IE_DATE;
2336 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2337 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2338 * microseconds" and "the type shall be a signed integer capable of
2339 * storing values at least in the range [-1, 1000000]. */
2340 if (long_number < -1 || long_number >= 1000000) {
2341 zfree(*time);
2342 return IE_DATE;
2344 (*time)->tv_usec = long_number;
2346 /* Round the subseconds */
2347 if (round_up) {
2348 if (999999 == (*time)->tv_usec) {
2349 (*time)->tv_usec = 0;
2350 broken.tm_sec++;
2351 } else {
2352 (*time)->tv_usec++;
2356 /* move to the zone offset delimiter or signal NULL*/
2357 delim = strchr(offset, '-');
2358 if (!delim)
2359 delim = strchr(offset, '+');
2360 if (!delim)
2361 delim = strchr(offset, 'Z');
2362 offset = delim;
2365 /* Get zone offset */
2366 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2367 * "" equals to "Z" and it means UTC zone. */
2368 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2369 * colon separator */
2370 if (offset && (*offset == '-' || *offset == '+')) {
2371 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2372 zfree(*time);
2373 return IE_DATE;
2375 if (*offset == '+') {
2376 broken.tm_hour -= offset_hours;
2377 broken.tm_min -= offset_minutes;
2378 } else {
2379 broken.tm_hour += offset_hours;
2380 broken.tm_min += offset_minutes;
2384 /* Convert to time_t */
2385 (*time)->tv_sec = _isds_timegm(&broken);
2386 if ((*time)->tv_sec == (time_t) -1) {
2387 zfree(*time);
2388 return IE_DATE;
2391 return IE_SUCCESS;
2395 /* Convert unsigned int into isds_message_status.
2396 * @context is session context
2397 * @number is pointer to number value. NULL will be treated as invalid value.
2398 * @status is automatically reallocated status
2399 * @return IE_SUCCESS, or error code and free status */
2400 static isds_error uint2isds_message_status(struct isds_ctx *context,
2401 const unsigned long int *number, isds_message_status **status) {
2402 if (!context) return IE_INVALID_CONTEXT;
2403 if (!status) return IE_INVAL;
2405 free(*status); *status = NULL;
2406 if (!number) return IE_INVAL;
2408 if (*number < 1 || *number > 10) {
2409 isds_printf_message(context, _("Invalid message status value: %lu"),
2410 *number);
2411 return IE_ENUM;
2414 *status = malloc(sizeof(**status));
2415 if (!*status) return IE_NOMEM;
2417 **status = 1 << *number;
2418 return IE_SUCCESS;
2422 /* Convert event description string into isds_event members type and
2423 * description
2424 * @string is raw event description starting with event prefix
2425 * @event is structure where to store type and stripped description to
2426 * @return standard error code, unknown prefix is not classified as an error.
2427 * */
2428 static isds_error eventstring2event(const xmlChar *string,
2429 struct isds_event* event) {
2430 const xmlChar *known_prefixes[] = {
2431 BAD_CAST "EV0:",
2432 BAD_CAST "EV1:",
2433 BAD_CAST "EV2:",
2434 BAD_CAST "EV3:",
2435 BAD_CAST "EV4:",
2436 BAD_CAST "EV5:",
2437 BAD_CAST "EV11:",
2438 BAD_CAST "EV12:",
2439 BAD_CAST "EV13:"
2441 const isds_event_type types[] = {
2442 EVENT_ENTERED_SYSTEM,
2443 EVENT_ACCEPTED_BY_RECIPIENT,
2444 EVENT_ACCEPTED_BY_FICTION,
2445 EVENT_UNDELIVERABLE,
2446 EVENT_COMMERCIAL_ACCEPTED,
2447 EVENT_DELIVERED,
2448 EVENT_PRIMARY_LOGIN,
2449 EVENT_ENTRUSTED_LOGIN,
2450 EVENT_SYSCERT_LOGIN
2452 unsigned int index;
2453 size_t length;
2455 if (!string || !event) return IE_INVAL;
2457 if (!event->type) {
2458 event->type = malloc(sizeof(*event->type));
2459 if (!(event->type)) return IE_NOMEM;
2461 zfree(event->description);
2463 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2464 index++) {
2465 length = xmlUTF8Strlen(known_prefixes[index]);
2467 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2468 /* Prefix is known */
2469 *event->type = types[index];
2471 /* Strip prefix from description and spaces */
2472 /* TODO: Recognize all white spaces from UCS blank class and
2473 * operate on UTF-8 chars. */
2474 for (; string[length] != '\0' && string[length] == ' '; length++);
2475 event->description = strdup((char *) (string + length));
2476 if (!(event->description)) return IE_NOMEM;
2478 return IE_SUCCESS;
2482 /* Unknown event prefix.
2483 * XSD allows any string */
2484 char *string_locale = _isds_utf82locale((char *) string);
2485 isds_log(ILF_ISDS, ILL_WARNING,
2486 _("Unknown delivery info event prefix: %s\n"), string_locale);
2487 free(string_locale);
2489 *event->type = EVENT_UKNOWN;
2490 event->description = strdup((char *) string);
2491 if (!(event->description)) return IE_NOMEM;
2493 return IE_SUCCESS;
2497 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2498 * and leave label */
2499 #define EXTRACT_STRING(element, string) { \
2500 xmlXPathFreeObject(result); \
2501 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2502 if (NULL == (result)) { \
2503 err = IE_ERROR; \
2504 goto leave; \
2506 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2507 if (result->nodesetval->nodeNr > 1) { \
2508 isds_printf_message(context, _("Multiple %s element"), element); \
2509 err = IE_ERROR; \
2510 goto leave; \
2512 (string) = (char *) \
2513 xmlXPathCastNodeSetToString(result->nodesetval); \
2514 if (NULL == (string)) { \
2515 err = IE_ERROR; \
2516 goto leave; \
2521 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2523 char *string = NULL; \
2524 EXTRACT_STRING(element, string); \
2526 if (string) { \
2527 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2528 if (!(booleanPtr)) { \
2529 free(string); \
2530 err = IE_NOMEM; \
2531 goto leave; \
2534 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2535 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2536 *(booleanPtr) = 1; \
2537 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2538 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2539 *(booleanPtr) = 0; \
2540 else { \
2541 char *string_locale = _isds_utf82locale((char*)string); \
2542 isds_printf_message(context, \
2543 _("%s value is not valid boolean: %s"), \
2544 element, string_locale); \
2545 free(string_locale); \
2546 free(string); \
2547 err = IE_ERROR; \
2548 goto leave; \
2551 free(string); \
2555 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2557 char *string = NULL; \
2558 EXTRACT_STRING(element, string); \
2560 if (NULL == string) { \
2561 isds_printf_message(context, _("%s element is empty"), element); \
2562 err = IE_ERROR; \
2563 goto leave; \
2565 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2566 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2567 (boolean) = 1; \
2568 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2569 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2570 (boolean) = 0; \
2571 else { \
2572 char *string_locale = _isds_utf82locale((char*)string); \
2573 isds_printf_message(context, \
2574 _("%s value is not valid boolean: %s"), \
2575 element, string_locale); \
2576 free(string_locale); \
2577 free(string); \
2578 err = IE_ERROR; \
2579 goto leave; \
2582 free(string); \
2585 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2587 char *string = NULL; \
2588 EXTRACT_STRING(element, string); \
2589 if (string) { \
2590 long int number; \
2591 char *endptr; \
2593 number = strtol((char*)string, &endptr, 10); \
2595 if (*endptr != '\0') { \
2596 char *string_locale = _isds_utf82locale((char *)string); \
2597 isds_printf_message(context, \
2598 _("%s is not valid integer: %s"), \
2599 element, string_locale); \
2600 free(string_locale); \
2601 free(string); \
2602 err = IE_ISDS; \
2603 goto leave; \
2606 if (number == LONG_MIN || number == LONG_MAX) { \
2607 char *string_locale = _isds_utf82locale((char *)string); \
2608 isds_printf_message(context, \
2609 _("%s value out of range of long int: %s"), \
2610 element, string_locale); \
2611 free(string_locale); \
2612 free(string); \
2613 err = IE_ERROR; \
2614 goto leave; \
2617 free(string); string = NULL; \
2619 if (!(preallocated)) { \
2620 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2621 if (!(longintPtr)) { \
2622 err = IE_NOMEM; \
2623 goto leave; \
2626 *(longintPtr) = number; \
2630 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2632 char *string = NULL; \
2633 EXTRACT_STRING(element, string); \
2634 if (string) { \
2635 long int number; \
2636 char *endptr; \
2638 number = strtol((char*)string, &endptr, 10); \
2640 if (*endptr != '\0') { \
2641 char *string_locale = _isds_utf82locale((char *)string); \
2642 isds_printf_message(context, \
2643 _("%s is not valid integer: %s"), \
2644 element, string_locale); \
2645 free(string_locale); \
2646 free(string); \
2647 err = IE_ISDS; \
2648 goto leave; \
2651 if (number == LONG_MIN || number == LONG_MAX) { \
2652 char *string_locale = _isds_utf82locale((char *)string); \
2653 isds_printf_message(context, \
2654 _("%s value out of range of long int: %s"), \
2655 element, string_locale); \
2656 free(string_locale); \
2657 free(string); \
2658 err = IE_ERROR; \
2659 goto leave; \
2662 free(string); string = NULL; \
2663 if (number < 0) { \
2664 isds_printf_message(context, \
2665 _("%s value is negative: %ld"), element, number); \
2666 err = IE_ERROR; \
2667 goto leave; \
2670 if (!(preallocated)) { \
2671 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2672 if (!(ulongintPtr)) { \
2673 err = IE_NOMEM; \
2674 goto leave; \
2677 *(ulongintPtr) = number; \
2681 #define EXTRACT_DATE(element, tmPtr) { \
2682 char *string = NULL; \
2683 EXTRACT_STRING(element, string); \
2684 if (NULL != string) { \
2685 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2686 if (NULL == (tmPtr)) { \
2687 free(string); \
2688 err = IE_NOMEM; \
2689 goto leave; \
2691 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2692 if (err) { \
2693 if (err == IE_NOTSUP) { \
2694 err = IE_ISDS; \
2695 char *string_locale = _isds_utf82locale(string); \
2696 char *element_locale = _isds_utf82locale(element); \
2697 isds_printf_message(context, _("Invalid %s value: %s"), \
2698 element_locale, string_locale); \
2699 free(string_locale); \
2700 free(element_locale); \
2702 free(string); \
2703 goto leave; \
2705 free(string); \
2709 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2710 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2711 NULL); \
2712 if ((required) && (!string)) { \
2713 char *attribute_locale = _isds_utf82locale(attribute); \
2714 char *element_locale = \
2715 _isds_utf82locale((char *)xpath_ctx->node->name); \
2716 isds_printf_message(context, \
2717 _("Could not extract required %s attribute value from " \
2718 "%s element"), attribute_locale, element_locale); \
2719 free(element_locale); \
2720 free(attribute_locale); \
2721 err = IE_ERROR; \
2722 goto leave; \
2727 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2729 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2730 (xmlChar *) (string)); \
2731 if (!node) { \
2732 isds_printf_message(context, \
2733 _("Could not add %s child to %s element"), \
2734 element, (parent)->name); \
2735 err = IE_ERROR; \
2736 goto leave; \
2740 #define INSERT_STRING(parent, element, string) \
2741 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2743 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2745 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2746 else { INSERT_STRING(parent, element, "false"); } \
2749 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2751 if (booleanPtr) { \
2752 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2753 } else { \
2754 INSERT_STRING(parent, element, NULL); \
2758 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2759 if ((longintPtr)) { \
2760 /* FIXME: locale sensitive */ \
2761 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2762 err = IE_NOMEM; \
2763 goto leave; \
2765 INSERT_STRING(parent, element, buffer) \
2766 free(buffer); (buffer) = NULL; \
2767 } else { INSERT_STRING(parent, element, NULL) } \
2770 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2771 if ((ulongintPtr)) { \
2772 /* FIXME: locale sensitive */ \
2773 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2774 err = IE_NOMEM; \
2775 goto leave; \
2777 INSERT_STRING(parent, element, buffer) \
2778 free(buffer); (buffer) = NULL; \
2779 } else { INSERT_STRING(parent, element, NULL) } \
2782 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2784 /* FIXME: locale sensitive */ \
2785 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2786 err = IE_NOMEM; \
2787 goto leave; \
2789 INSERT_STRING(parent, element, buffer) \
2790 free(buffer); (buffer) = NULL; \
2793 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2794 * new attribute. */
2795 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2797 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2798 (xmlChar *) (string)); \
2799 if (!attribute_node) { \
2800 isds_printf_message(context, _("Could not add %s " \
2801 "attribute to %s element"), \
2802 (attribute), (parent)->name); \
2803 err = IE_ERROR; \
2804 goto leave; \
2808 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2809 if (string) { \
2810 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2811 if (length > (maximum)) { \
2812 isds_printf_message(context, \
2813 ngettext("%s has more than %d characters", \
2814 "%s has more than %d characters", (maximum)), \
2815 (name), (maximum)); \
2816 err = IE_2BIG; \
2817 goto leave; \
2819 if (length < (minimum)) { \
2820 isds_printf_message(context, \
2821 ngettext("%s has less than %d characters", \
2822 "%s has less than %d characters", (minimum)), \
2823 (name), (minimum)); \
2824 err = IE_2SMALL; \
2825 goto leave; \
2830 #define INSERT_ELEMENT(child, parent, element) \
2832 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2833 if (!(child)) { \
2834 isds_printf_message(context, \
2835 _("Could not add %s child to %s element"), \
2836 (element), (parent)->name); \
2837 err = IE_ERROR; \
2838 goto leave; \
2843 /* Find child element by name in given XPath context and switch context onto
2844 * it. The child must be uniq and must exist. Otherwise fails.
2845 * @context is ISDS context
2846 * @child is child element name
2847 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2848 * into it child. In error case, the @xpath_ctx keeps original value. */
2849 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2850 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2851 isds_error err = IE_SUCCESS;
2852 xmlXPathObjectPtr result = NULL;
2854 if (!context) return IE_INVALID_CONTEXT;
2855 if (!child || !xpath_ctx) return IE_INVAL;
2857 /* Find child */
2858 result = xmlXPathEvalExpression(child, xpath_ctx);
2859 if (!result) {
2860 err = IE_XML;
2861 goto leave;
2864 /* No match */
2865 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2866 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2867 char *child_locale = _isds_utf82locale((char*) child);
2868 isds_printf_message(context,
2869 _("%s element does not contain %s child"),
2870 parent_locale, child_locale);
2871 free(child_locale);
2872 free(parent_locale);
2873 err = IE_NOEXIST;
2874 goto leave;
2877 /* More matches */
2878 if (result->nodesetval->nodeNr > 1) {
2879 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2880 char *child_locale = _isds_utf82locale((char*) child);
2881 isds_printf_message(context,
2882 _("%s element contains multiple %s children"),
2883 parent_locale, child_locale);
2884 free(child_locale);
2885 free(parent_locale);
2886 err = IE_NOTUNIQ;
2887 goto leave;
2890 /* Switch context */
2891 xpath_ctx->node = result->nodesetval->nodeTab[0];
2893 leave:
2894 xmlXPathFreeObject(result);
2895 return err;
2900 #if HAVE_LIBCURL
2901 /* Find and convert XSD:gPersonName group in current node into structure
2902 * @context is ISDS context
2903 * @personName is automatically reallocated person name structure. If no member
2904 * value is found, will be freed.
2905 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2906 * elements
2907 * In case of error @personName will be freed. */
2908 static isds_error extract_gPersonName(struct isds_ctx *context,
2909 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2910 isds_error err = IE_SUCCESS;
2911 xmlXPathObjectPtr result = NULL;
2913 if (!context) return IE_INVALID_CONTEXT;
2914 if (!personName) return IE_INVAL;
2915 isds_PersonName_free(personName);
2916 if (!xpath_ctx) return IE_INVAL;
2919 *personName = calloc(1, sizeof(**personName));
2920 if (!*personName) {
2921 err = IE_NOMEM;
2922 goto leave;
2925 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2926 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2927 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2928 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2930 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2931 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2932 isds_PersonName_free(personName);
2934 leave:
2935 if (err) isds_PersonName_free(personName);
2936 xmlXPathFreeObject(result);
2937 return err;
2941 /* Find and convert XSD:gAddress group in current node into structure
2942 * @context is ISDS context
2943 * @address is automatically reallocated address structure. If no member
2944 * value is found, will be freed.
2945 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2946 * elements
2947 * In case of error @address will be freed. */
2948 static isds_error extract_gAddress(struct isds_ctx *context,
2949 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2950 isds_error err = IE_SUCCESS;
2951 xmlXPathObjectPtr result = NULL;
2953 if (!context) return IE_INVALID_CONTEXT;
2954 if (!address) return IE_INVAL;
2955 isds_Address_free(address);
2956 if (!xpath_ctx) return IE_INVAL;
2959 *address = calloc(1, sizeof(**address));
2960 if (!*address) {
2961 err = IE_NOMEM;
2962 goto leave;
2965 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2966 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2967 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2968 EXTRACT_STRING("isds:adNumberInMunicipality",
2969 (*address)->adNumberInMunicipality);
2970 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2971 EXTRACT_STRING("isds:adState", (*address)->adState);
2973 if (!(*address)->adCity && !(*address)->adStreet &&
2974 !(*address)->adNumberInStreet &&
2975 !(*address)->adNumberInMunicipality &&
2976 !(*address)->adZipCode && !(*address)->adState)
2977 isds_Address_free(address);
2979 leave:
2980 if (err) isds_Address_free(address);
2981 xmlXPathFreeObject(result);
2982 return err;
2986 /* Find and convert isds:biDate element in current node into structure
2987 * @context is ISDS context
2988 * @biDate is automatically reallocated birth date structure. If no member
2989 * value is found, will be freed.
2990 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2991 * element
2992 * In case of error @biDate will be freed. */
2993 static isds_error extract_BiDate(struct isds_ctx *context,
2994 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2995 isds_error err = IE_SUCCESS;
2996 xmlXPathObjectPtr result = NULL;
2997 char *string = NULL;
2999 if (!context) return IE_INVALID_CONTEXT;
3000 if (!biDate) return IE_INVAL;
3001 zfree(*biDate);
3002 if (!xpath_ctx) return IE_INVAL;
3004 EXTRACT_STRING("isds:biDate", string);
3005 if (string) {
3006 *biDate = calloc(1, sizeof(**biDate));
3007 if (!*biDate) {
3008 err = IE_NOMEM;
3009 goto leave;
3011 err = _isds_datestring2tm((xmlChar *)string, *biDate);
3012 if (err) {
3013 if (err == IE_NOTSUP) {
3014 err = IE_ISDS;
3015 char *string_locale = _isds_utf82locale(string);
3016 isds_printf_message(context,
3017 _("Invalid isds:biDate value: %s"), string_locale);
3018 free(string_locale);
3020 goto leave;
3024 leave:
3025 if (err) zfree(*biDate);
3026 free(string);
3027 xmlXPathFreeObject(result);
3028 return err;
3032 /* Convert isds:dBOwnerInfo XML tree into structure
3033 * @context is ISDS context
3034 * @db_owner_info is automatically reallocated box owner info structure
3035 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
3036 * In case of error @db_owner_info will be freed. */
3037 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
3038 struct isds_DbOwnerInfo **db_owner_info,
3039 xmlXPathContextPtr xpath_ctx) {
3040 isds_error err = IE_SUCCESS;
3041 xmlXPathObjectPtr result = NULL;
3042 char *string = NULL;
3044 if (!context) return IE_INVALID_CONTEXT;
3045 if (!db_owner_info) return IE_INVAL;
3046 isds_DbOwnerInfo_free(db_owner_info);
3047 if (!xpath_ctx) return IE_INVAL;
3050 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3051 if (!*db_owner_info) {
3052 err = IE_NOMEM;
3053 goto leave;
3056 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
3058 EXTRACT_STRING("isds:dbType", string);
3059 if (string) {
3060 (*db_owner_info)->dbType =
3061 calloc(1, sizeof(*((*db_owner_info)->dbType)));
3062 if (!(*db_owner_info)->dbType) {
3063 err = IE_NOMEM;
3064 goto leave;
3066 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
3067 if (err) {
3068 zfree((*db_owner_info)->dbType);
3069 if (err == IE_ENUM) {
3070 err = IE_ISDS;
3071 char *string_locale = _isds_utf82locale(string);
3072 isds_printf_message(context, _("Unknown isds:dbType: %s"),
3073 string_locale);
3074 free(string_locale);
3076 goto leave;
3078 zfree(string);
3081 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
3083 err = extract_gPersonName(context, &(*db_owner_info)->personName,
3084 xpath_ctx);
3085 if (err) goto leave;
3087 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
3089 (*db_owner_info)->birthInfo =
3090 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
3091 if (!(*db_owner_info)->birthInfo) {
3092 err = IE_NOMEM;
3093 goto leave;
3095 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
3096 xpath_ctx);
3097 if (err) goto leave;
3098 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3099 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3100 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3101 if (!(*db_owner_info)->birthInfo->biDate &&
3102 !(*db_owner_info)->birthInfo->biCity &&
3103 !(*db_owner_info)->birthInfo->biCounty &&
3104 !(*db_owner_info)->birthInfo->biState)
3105 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3107 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3108 if (err) goto leave;
3110 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3111 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3112 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3113 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3114 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3116 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3118 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3119 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3120 (*db_owner_info)->dbOpenAddressing);
3122 leave:
3123 if (err) isds_DbOwnerInfo_free(db_owner_info);
3124 free(string);
3125 xmlXPathFreeObject(result);
3126 return err;
3130 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3131 * @context is session context
3132 * @owner is libisds structure with box description
3133 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3134 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3135 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
3137 isds_error err = IE_SUCCESS;
3138 xmlNodePtr node;
3139 xmlChar *string = NULL;
3141 if (!context) return IE_INVALID_CONTEXT;
3142 if (!owner || !db_owner_info) return IE_INVAL;
3145 /* Build XSD:tDbOwnerInfo */
3146 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3147 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3149 /* dbType */
3150 if (owner->dbType) {
3151 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
3152 if (!type_string) {
3153 isds_printf_message(context, _("Invalid dbType value: %d"),
3154 *(owner->dbType));
3155 err = IE_ENUM;
3156 goto leave;
3158 INSERT_STRING(db_owner_info, "dbType", type_string);
3160 INSERT_STRING(db_owner_info, "ic", owner->ic);
3161 if (owner->personName) {
3162 INSERT_STRING(db_owner_info, "pnFirstName",
3163 owner->personName->pnFirstName);
3164 INSERT_STRING(db_owner_info, "pnMiddleName",
3165 owner->personName->pnMiddleName);
3166 INSERT_STRING(db_owner_info, "pnLastName",
3167 owner->personName->pnLastName);
3168 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3169 owner->personName->pnLastNameAtBirth);
3171 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3172 if (owner->birthInfo) {
3173 if (owner->birthInfo->biDate) {
3174 if (!tm2datestring(owner->birthInfo->biDate, &string))
3175 INSERT_STRING(db_owner_info, "biDate", string);
3176 free(string); string = NULL;
3178 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
3179 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
3180 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
3182 if (owner->address) {
3183 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
3184 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
3185 INSERT_STRING(db_owner_info, "adNumberInStreet",
3186 owner->address->adNumberInStreet);
3187 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3188 owner->address->adNumberInMunicipality);
3189 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
3190 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
3192 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3193 INSERT_STRING(db_owner_info, "email", owner->email);
3194 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3196 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3197 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3199 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3200 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3202 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3204 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3205 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3206 owner->dbOpenAddressing);
3208 leave:
3209 free(string);
3210 return err;
3214 /* Convert XSD:tDbUserInfo XML tree into structure
3215 * @context is ISDS context
3216 * @db_user_info is automatically reallocated user info structure
3217 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3218 * In case of error @db_user_info will be freed. */
3219 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3220 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3221 isds_error err = IE_SUCCESS;
3222 xmlXPathObjectPtr result = NULL;
3223 char *string = NULL;
3225 if (!context) return IE_INVALID_CONTEXT;
3226 if (!db_user_info) return IE_INVAL;
3227 isds_DbUserInfo_free(db_user_info);
3228 if (!xpath_ctx) return IE_INVAL;
3231 *db_user_info = calloc(1, sizeof(**db_user_info));
3232 if (!*db_user_info) {
3233 err = IE_NOMEM;
3234 goto leave;
3237 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3239 EXTRACT_STRING("isds:userType", string);
3240 if (string) {
3241 (*db_user_info)->userType =
3242 calloc(1, sizeof(*((*db_user_info)->userType)));
3243 if (!(*db_user_info)->userType) {
3244 err = IE_NOMEM;
3245 goto leave;
3247 err = string2isds_UserType((xmlChar *)string,
3248 (*db_user_info)->userType);
3249 if (err) {
3250 zfree((*db_user_info)->userType);
3251 if (err == IE_ENUM) {
3252 err = IE_ISDS;
3253 char *string_locale = _isds_utf82locale(string);
3254 isds_printf_message(context,
3255 _("Unknown isds:userType value: %s"), string_locale);
3256 free(string_locale);
3258 goto leave;
3260 zfree(string);
3263 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3265 (*db_user_info)->personName =
3266 calloc(1, sizeof(*((*db_user_info)->personName)));
3267 if (!(*db_user_info)->personName) {
3268 err = IE_NOMEM;
3269 goto leave;
3272 err = extract_gPersonName(context, &(*db_user_info)->personName,
3273 xpath_ctx);
3274 if (err) goto leave;
3276 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3277 if (err) goto leave;
3279 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3280 if (err) goto leave;
3282 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3283 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3285 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3286 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3287 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3289 /* ???: Default value is "CZ" according specification. Should we provide
3290 * it? */
3291 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3293 leave:
3294 if (err) isds_DbUserInfo_free(db_user_info);
3295 free(string);
3296 xmlXPathFreeObject(result);
3297 return err;
3301 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3302 * @context is session context
3303 * @user is libisds structure with user description
3304 * @db_user_info is XML element of XSD:tDbUserInfo */
3305 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3306 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3308 isds_error err = IE_SUCCESS;
3309 xmlNodePtr node;
3310 xmlChar *string = NULL;
3312 if (!context) return IE_INVALID_CONTEXT;
3313 if (!user || !db_user_info) return IE_INVAL;
3315 /* Build XSD:tDbUserInfo */
3316 if (user->personName) {
3317 INSERT_STRING(db_user_info, "pnFirstName",
3318 user->personName->pnFirstName);
3319 INSERT_STRING(db_user_info, "pnMiddleName",
3320 user->personName->pnMiddleName);
3321 INSERT_STRING(db_user_info, "pnLastName",
3322 user->personName->pnLastName);
3323 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3324 user->personName->pnLastNameAtBirth);
3326 if (user->address) {
3327 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3328 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3329 INSERT_STRING(db_user_info, "adNumberInStreet",
3330 user->address->adNumberInStreet);
3331 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3332 user->address->adNumberInMunicipality);
3333 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3334 INSERT_STRING(db_user_info, "adState", user->address->adState);
3336 if (user->biDate) {
3337 if (!tm2datestring(user->biDate, &string))
3338 INSERT_STRING(db_user_info, "biDate", string);
3339 zfree(string);
3341 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3342 INSERT_STRING(db_user_info, "userID", user->userID);
3344 /* userType */
3345 if (user->userType) {
3346 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3347 if (!type_string) {
3348 isds_printf_message(context, _("Invalid userType value: %d"),
3349 *(user->userType));
3350 err = IE_ENUM;
3351 goto leave;
3353 INSERT_STRING(db_user_info, "userType", type_string);
3356 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3357 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3358 INSERT_STRING(db_user_info, "ic", user->ic);
3359 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3360 INSERT_STRING(db_user_info, "firmName", user->firmName);
3361 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3362 INSERT_STRING(db_user_info, "caCity", user->caCity);
3363 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3364 INSERT_STRING(db_user_info, "caState", user->caState);
3366 leave:
3367 free(string);
3368 return err;
3372 /* Convert XSD:tPDZRec XML tree into structure
3373 * @context is ISDS context
3374 * @permission is automatically reallocated commercial permission structure
3375 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3376 * In case of error @permission will be freed. */
3377 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3378 struct isds_commercial_permission **permission,
3379 xmlXPathContextPtr xpath_ctx) {
3380 isds_error err = IE_SUCCESS;
3381 xmlXPathObjectPtr result = NULL;
3382 char *string = NULL;
3384 if (!context) return IE_INVALID_CONTEXT;
3385 if (!permission) return IE_INVAL;
3386 isds_commercial_permission_free(permission);
3387 if (!xpath_ctx) return IE_INVAL;
3390 *permission = calloc(1, sizeof(**permission));
3391 if (!*permission) {
3392 err = IE_NOMEM;
3393 goto leave;
3396 EXTRACT_STRING("isds:PDZType", string);
3397 if (string) {
3398 err = string2isds_payment_type((xmlChar *)string,
3399 &(*permission)->type);
3400 if (err) {
3401 if (err == IE_ENUM) {
3402 err = IE_ISDS;
3403 char *string_locale = _isds_utf82locale(string);
3404 isds_printf_message(context,
3405 _("Unknown isds:PDZType value: %s"), string_locale);
3406 free(string_locale);
3408 goto leave;
3410 zfree(string);
3413 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3414 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3416 EXTRACT_STRING("isds:PDZExpire", string);
3417 if (string) {
3418 err = timestring2timeval((xmlChar *) string,
3419 &((*permission)->expiration));
3420 if (err) {
3421 char *string_locale = _isds_utf82locale(string);
3422 if (err == IE_DATE) err = IE_ISDS;
3423 isds_printf_message(context,
3424 _("Could not convert PDZExpire as ISO time: %s"),
3425 string_locale);
3426 free(string_locale);
3427 goto leave;
3429 zfree(string);
3432 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3433 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3435 leave:
3436 if (err) isds_commercial_permission_free(permission);
3437 free(string);
3438 xmlXPathFreeObject(result);
3439 return err;
3443 /* Convert XSD:tCiRecord XML tree into structure
3444 * @context is ISDS context
3445 * @event is automatically reallocated commercial credit event structure
3446 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3447 * In case of error @event will be freed. */
3448 static isds_error extract_CiRecord(struct isds_ctx *context,
3449 struct isds_credit_event **event,
3450 xmlXPathContextPtr xpath_ctx) {
3451 isds_error err = IE_SUCCESS;
3452 xmlXPathObjectPtr result = NULL;
3453 char *string = NULL;
3454 long int *number_ptr;
3456 if (!context) return IE_INVALID_CONTEXT;
3457 if (!event) return IE_INVAL;
3458 isds_credit_event_free(event);
3459 if (!xpath_ctx) return IE_INVAL;
3462 *event = calloc(1, sizeof(**event));
3463 if (!*event) {
3464 err = IE_NOMEM;
3465 goto leave;
3468 EXTRACT_STRING("isds:ciEventTime", string);
3469 if (string) {
3470 err = timestring2timeval((xmlChar *) string,
3471 &(*event)->time);
3472 if (err) {
3473 char *string_locale = _isds_utf82locale(string);
3474 if (err == IE_DATE) err = IE_ISDS;
3475 isds_printf_message(context,
3476 _("Could not convert ciEventTime as ISO time: %s"),
3477 string_locale);
3478 free(string_locale);
3479 goto leave;
3481 zfree(string);
3484 EXTRACT_STRING("isds:ciEventType", string);
3485 if (string) {
3486 err = string2isds_credit_event_type((xmlChar *)string,
3487 &(*event)->type);
3488 if (err) {
3489 if (err == IE_ENUM) {
3490 err = IE_ISDS;
3491 char *string_locale = _isds_utf82locale(string);
3492 isds_printf_message(context,
3493 _("Unknown isds:ciEventType value: %s"), string_locale);
3494 free(string_locale);
3496 goto leave;
3498 zfree(string);
3501 number_ptr = &((*event)->credit_change);
3502 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3503 number_ptr = &(*event)->new_credit;
3504 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3506 switch((*event)->type) {
3507 case ISDS_CREDIT_CHARGED:
3508 EXTRACT_STRING("isds:ciTransID",
3509 (*event)->details.charged.transaction);
3510 break;
3511 case ISDS_CREDIT_DISCHARGED:
3512 EXTRACT_STRING("isds:ciTransID",
3513 (*event)->details.discharged.transaction);
3514 break;
3515 case ISDS_CREDIT_MESSAGE_SENT:
3516 EXTRACT_STRING("isds:ciRecipientID",
3517 (*event)->details.message_sent.recipient);
3518 EXTRACT_STRING("isds:ciPDZID",
3519 (*event)->details.message_sent.message_id);
3520 break;
3521 case ISDS_CREDIT_STORAGE_SET:
3522 number_ptr = &((*event)->details.storage_set.new_capacity);
3523 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3524 EXTRACT_DATE("isds:ciNewFrom",
3525 (*event)->details.storage_set.new_valid_from);
3526 EXTRACT_DATE("isds:ciNewTo",
3527 (*event)->details.storage_set.new_valid_to);
3528 EXTRACT_LONGINT("isds:ciOldCapacity",
3529 (*event)->details.storage_set.old_capacity, 0);
3530 EXTRACT_DATE("isds:ciOldFrom",
3531 (*event)->details.storage_set.old_valid_from);
3532 EXTRACT_DATE("isds:ciOldTo",
3533 (*event)->details.storage_set.old_valid_to);
3534 EXTRACT_STRING("isds:ciDoneBy",
3535 (*event)->details.storage_set.initiator);
3536 break;
3537 case ISDS_CREDIT_EXPIRED:
3538 break;
3541 leave:
3542 if (err) isds_credit_event_free(event);
3543 free(string);
3544 xmlXPathFreeObject(result);
3545 return err;
3549 #endif /* HAVE_LIBCURL */
3552 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3553 * isds_envelope structure. The envelope is automatically allocated but not
3554 * reallocated. The date are just appended into envelope structure.
3555 * @context is ISDS context
3556 * @envelope is automatically allocated message envelope structure
3557 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3558 * In case of error @envelope will be freed. */
3559 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3560 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3561 isds_error err = IE_SUCCESS;
3562 xmlXPathObjectPtr result = NULL;
3564 if (!context) return IE_INVALID_CONTEXT;
3565 if (!envelope) return IE_INVAL;
3566 if (!xpath_ctx) return IE_INVAL;
3569 if (!*envelope) {
3570 /* Allocate envelope */
3571 *envelope = calloc(1, sizeof(**envelope));
3572 if (!*envelope) {
3573 err = IE_NOMEM;
3574 goto leave;
3576 } else {
3577 /* Else free former data */
3578 zfree((*envelope)->dmSenderOrgUnit);
3579 zfree((*envelope)->dmSenderOrgUnitNum);
3580 zfree((*envelope)->dbIDRecipient);
3581 zfree((*envelope)->dmRecipientOrgUnit);
3582 zfree((*envelope)->dmRecipientOrgUnitNum);
3583 zfree((*envelope)->dmToHands);
3584 zfree((*envelope)->dmAnnotation);
3585 zfree((*envelope)->dmRecipientRefNumber);
3586 zfree((*envelope)->dmSenderRefNumber);
3587 zfree((*envelope)->dmRecipientIdent);
3588 zfree((*envelope)->dmSenderIdent);
3589 zfree((*envelope)->dmLegalTitleLaw);
3590 zfree((*envelope)->dmLegalTitleYear);
3591 zfree((*envelope)->dmLegalTitleSect);
3592 zfree((*envelope)->dmLegalTitlePar);
3593 zfree((*envelope)->dmLegalTitlePoint);
3594 zfree((*envelope)->dmPersonalDelivery);
3595 zfree((*envelope)->dmAllowSubstDelivery);
3598 /* Extract envelope elements added by sender or ISDS
3599 * (XSD: gMessageEnvelopeSub type) */
3600 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3601 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3602 (*envelope)->dmSenderOrgUnitNum, 0);
3603 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3604 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3605 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3606 (*envelope)->dmRecipientOrgUnitNum, 0);
3607 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3608 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3609 EXTRACT_STRING("isds:dmRecipientRefNumber",
3610 (*envelope)->dmRecipientRefNumber);
3611 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3612 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3613 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3615 /* Extract envelope elements regarding law reference */
3616 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3617 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3618 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3619 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3620 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3622 /* Extract envelope other elements */
3623 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3624 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3625 (*envelope)->dmAllowSubstDelivery);
3627 leave:
3628 if (err) isds_envelope_free(envelope);
3629 xmlXPathFreeObject(result);
3630 return err;
3635 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3636 * isds_envelope structure. The envelope is automatically allocated but not
3637 * reallocated. The date are just appended into envelope structure.
3638 * @context is ISDS context
3639 * @envelope is automatically allocated message envelope structure
3640 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3641 * In case of error @envelope will be freed. */
3642 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3643 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3644 isds_error err = IE_SUCCESS;
3645 xmlXPathObjectPtr result = NULL;
3647 if (!context) return IE_INVALID_CONTEXT;
3648 if (!envelope) return IE_INVAL;
3649 if (!xpath_ctx) return IE_INVAL;
3652 if (!*envelope) {
3653 /* Allocate envelope */
3654 *envelope = calloc(1, sizeof(**envelope));
3655 if (!*envelope) {
3656 err = IE_NOMEM;
3657 goto leave;
3659 } else {
3660 /* Else free former data */
3661 zfree((*envelope)->dmID);
3662 zfree((*envelope)->dbIDSender);
3663 zfree((*envelope)->dmSender);
3664 zfree((*envelope)->dmSenderAddress);
3665 zfree((*envelope)->dmSenderType);
3666 zfree((*envelope)->dmRecipient);
3667 zfree((*envelope)->dmRecipientAddress);
3668 zfree((*envelope)->dmAmbiguousRecipient);
3671 /* Extract envelope elements added by ISDS
3672 * (XSD: gMessageEnvelope type) */
3673 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3674 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3675 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3676 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3677 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3678 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3679 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3680 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3681 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3682 (*envelope)->dmAmbiguousRecipient);
3684 /* Extract envelope elements added by sender and ISDS
3685 * (XSD: gMessageEnvelope type) */
3686 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3687 if (err) goto leave;
3689 leave:
3690 if (err) isds_envelope_free(envelope);
3691 xmlXPathFreeObject(result);
3692 return err;
3696 /* Convert other envelope elements from XML tree into isds_envelope structure:
3697 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3698 * The envelope is automatically allocated but not reallocated.
3699 * The data are just appended into envelope structure.
3700 * @context is ISDS context
3701 * @envelope is automatically allocated message envelope structure
3702 * @xpath_ctx is XPath context with current node as parent desired elements
3703 * In case of error @envelope will be freed. */
3704 static isds_error append_status_size_times(struct isds_ctx *context,
3705 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3706 isds_error err = IE_SUCCESS;
3707 xmlXPathObjectPtr result = NULL;
3708 char *string = NULL;
3709 unsigned long int *unumber = NULL;
3711 if (!context) return IE_INVALID_CONTEXT;
3712 if (!envelope) return IE_INVAL;
3713 if (!xpath_ctx) return IE_INVAL;
3716 if (!*envelope) {
3717 /* Allocate new */
3718 *envelope = calloc(1, sizeof(**envelope));
3719 if (!*envelope) {
3720 err = IE_NOMEM;
3721 goto leave;
3723 } else {
3724 /* Free old data */
3725 zfree((*envelope)->dmMessageStatus);
3726 zfree((*envelope)->dmAttachmentSize);
3727 zfree((*envelope)->dmDeliveryTime);
3728 zfree((*envelope)->dmAcceptanceTime);
3732 /* dmMessageStatus element is mandatory */
3733 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3734 if (!unumber) {
3735 isds_log_message(context,
3736 _("Missing mandatory sisds:dmMessageStatus integer"));
3737 err = IE_ISDS;
3738 goto leave;
3740 err = uint2isds_message_status(context, unumber,
3741 &((*envelope)->dmMessageStatus));
3742 if (err) {
3743 if (err == IE_ENUM) err = IE_ISDS;
3744 goto leave;
3746 free(unumber); unumber = NULL;
3748 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3751 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3752 if (string) {
3753 err = timestring2timeval((xmlChar *) string,
3754 &((*envelope)->dmDeliveryTime));
3755 if (err) {
3756 char *string_locale = _isds_utf82locale(string);
3757 if (err == IE_DATE) err = IE_ISDS;
3758 isds_printf_message(context,
3759 _("Could not convert dmDeliveryTime as ISO time: %s"),
3760 string_locale);
3761 free(string_locale);
3762 goto leave;
3764 zfree(string);
3767 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3768 if (string) {
3769 err = timestring2timeval((xmlChar *) string,
3770 &((*envelope)->dmAcceptanceTime));
3771 if (err) {
3772 char *string_locale = _isds_utf82locale(string);
3773 if (err == IE_DATE) err = IE_ISDS;
3774 isds_printf_message(context,
3775 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3776 string_locale);
3777 free(string_locale);
3778 goto leave;
3780 zfree(string);
3783 leave:
3784 if (err) isds_envelope_free(envelope);
3785 free(unumber);
3786 free(string);
3787 xmlXPathFreeObject(result);
3788 return err;
3792 /* Convert message type attribute of current element into isds_envelope
3793 * structure.
3794 * TODO: This function can be incorporated into append_status_size_times() as
3795 * they are called always together.
3796 * The envelope is automatically allocated but not reallocated.
3797 * The data are just appended into envelope structure.
3798 * @context is ISDS context
3799 * @envelope is automatically allocated message envelope structure
3800 * @xpath_ctx is XPath context with current node as parent of attribute
3801 * carrying message type
3802 * In case of error @envelope will be freed. */
3803 static isds_error append_message_type(struct isds_ctx *context,
3804 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3805 isds_error err = IE_SUCCESS;
3807 if (!context) return IE_INVALID_CONTEXT;
3808 if (!envelope) return IE_INVAL;
3809 if (!xpath_ctx) return IE_INVAL;
3812 if (!*envelope) {
3813 /* Allocate new */
3814 *envelope = calloc(1, sizeof(**envelope));
3815 if (!*envelope) {
3816 err = IE_NOMEM;
3817 goto leave;
3819 } else {
3820 /* Free old data */
3821 zfree((*envelope)->dmType);
3825 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3827 if (!(*envelope)->dmType) {
3828 /* Use default value */
3829 (*envelope)->dmType = strdup("V");
3830 if (!(*envelope)->dmType) {
3831 err = IE_NOMEM;
3832 goto leave;
3834 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3835 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3836 isds_printf_message(context,
3837 _("Message type in dmType attribute is not 1 character long: "
3838 "%s"),
3839 type_locale);
3840 free(type_locale);
3841 err = IE_ISDS;
3842 goto leave;
3845 leave:
3846 if (err) isds_envelope_free(envelope);
3847 return err;
3851 #if HAVE_LIBCURL
3852 /* Convert dmType isds_envelope member into XML attribute and append it to
3853 * current node.
3854 * @context is ISDS context
3855 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3856 * @dm_envelope is XML element the resulting attribute will be appended to.
3857 * @return error code, in case of error context' message is filled. */
3858 static isds_error insert_message_type(struct isds_ctx *context,
3859 const char *type, xmlNodePtr dm_envelope) {
3860 isds_error err = IE_SUCCESS;
3861 xmlAttrPtr attribute_node;
3863 if (!context) return IE_INVALID_CONTEXT;
3864 if (!dm_envelope) return IE_INVAL;
3866 /* Insert optional message type */
3867 if (type) {
3868 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3869 char *type_locale = _isds_utf82locale(type);
3870 isds_printf_message(context,
3871 _("Message type in envelope is not 1 character long: %s"),
3872 type_locale);
3873 free(type_locale);
3874 err = IE_INVAL;
3875 goto leave;
3877 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3880 leave:
3881 return err;
3883 #endif /* HAVE_LIBCURL */
3886 /* Extract message document into reallocated document structure
3887 * @context is ISDS context
3888 * @document is automatically reallocated message documents structure
3889 * @xpath_ctx is XPath context with current node as isds:dmFile
3890 * In case of error @document will be freed. */
3891 static isds_error extract_document(struct isds_ctx *context,
3892 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3893 isds_error err = IE_SUCCESS;
3894 xmlXPathObjectPtr result = NULL;
3895 xmlNodePtr file_node;
3896 char *string = NULL;
3898 if (!context) return IE_INVALID_CONTEXT;
3899 if (!document) return IE_INVAL;
3900 isds_document_free(document);
3901 if (!xpath_ctx) return IE_INVAL;
3902 file_node = xpath_ctx->node;
3904 *document = calloc(1, sizeof(**document));
3905 if (!*document) {
3906 err = IE_NOMEM;
3907 goto leave;
3910 /* Extract document meta data */
3911 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3912 if (context->normalize_mime_type) {
3913 const char *normalized_type =
3914 isds_normalize_mime_type((*document)->dmMimeType);
3915 if (NULL != normalized_type &&
3916 normalized_type != (*document)->dmMimeType) {
3917 char *new_type = strdup(normalized_type);
3918 if (NULL == new_type) {
3919 isds_printf_message(context,
3920 _("Not enough memory to normalize document MIME type"));
3921 err = IE_NOMEM;
3922 goto leave;
3924 free((*document)->dmMimeType);
3925 (*document)->dmMimeType = new_type;
3929 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3930 err = string2isds_FileMetaType((xmlChar*)string,
3931 &((*document)->dmFileMetaType));
3932 if (err) {
3933 char *meta_type_locale = _isds_utf82locale(string);
3934 isds_printf_message(context,
3935 _("Document has invalid dmFileMetaType attribute value: %s"),
3936 meta_type_locale);
3937 free(meta_type_locale);
3938 err = IE_ISDS;
3939 goto leave;
3941 zfree(string);
3943 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3944 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3945 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3946 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3949 /* Extract document data.
3950 * Base64 encoded blob or XML subtree must be presented. */
3952 /* Check for dmEncodedContent */
3953 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3954 xpath_ctx);
3955 if (!result) {
3956 err = IE_XML;
3957 goto leave;
3960 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3961 /* Here we have Base64 blob */
3962 (*document)->is_xml = 0;
3964 if (result->nodesetval->nodeNr > 1) {
3965 isds_printf_message(context,
3966 _("Document has more dmEncodedContent elements"));
3967 err = IE_ISDS;
3968 goto leave;
3971 xmlXPathFreeObject(result); result = NULL;
3972 EXTRACT_STRING("isds:dmEncodedContent", string);
3974 /* Decode non-empty document */
3975 if (string && string[0] != '\0') {
3976 (*document)->data_length =
3977 _isds_b64decode(string, &((*document)->data));
3978 if ((*document)->data_length == (size_t) -1) {
3979 isds_printf_message(context,
3980 _("Error while Base64-decoding document content"));
3981 err = IE_ERROR;
3982 goto leave;
3985 } else {
3986 /* No Base64 blob, try XML document */
3987 xmlXPathFreeObject(result); result = NULL;
3988 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3989 xpath_ctx);
3990 if (!result) {
3991 err = IE_XML;
3992 goto leave;
3995 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3996 /* Here we have XML document */
3997 (*document)->is_xml = 1;
3999 if (result->nodesetval->nodeNr > 1) {
4000 isds_printf_message(context,
4001 _("Document has more dmXMLContent elements"));
4002 err = IE_ISDS;
4003 goto leave;
4006 /* XXX: We cannot serialize the content simply because:
4007 * - XML document may point out of its scope (e.g. to message
4008 * envelope)
4009 * - isds:dmXMLContent can contain more elements, no element,
4010 * a text node only
4011 * - it's not the XML way
4012 * Thus we provide the only right solution: XML DOM. Let's
4013 * application to cope with this hot potato :) */
4014 (*document)->xml_node_list =
4015 result->nodesetval->nodeTab[0]->children;
4016 } else {
4017 /* No base64 blob, nor XML document */
4018 isds_printf_message(context,
4019 _("Document has no dmEncodedContent, nor dmXMLContent "
4020 "element"));
4021 err = IE_ISDS;
4022 goto leave;
4027 leave:
4028 if (err) isds_document_free(document);
4029 free(string);
4030 xmlXPathFreeObject(result);
4031 xpath_ctx->node = file_node;
4032 return err;
4037 /* Extract message documents into reallocated list of documents
4038 * @context is ISDS context
4039 * @documents is automatically reallocated message documents list structure
4040 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4041 * In case of error @documents will be freed. */
4042 static isds_error extract_documents(struct isds_ctx *context,
4043 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
4044 isds_error err = IE_SUCCESS;
4045 xmlXPathObjectPtr result = NULL;
4046 xmlNodePtr files_node;
4047 struct isds_list *document, *prev_document = NULL;
4049 if (!context) return IE_INVALID_CONTEXT;
4050 if (!documents) return IE_INVAL;
4051 isds_list_free(documents);
4052 if (!xpath_ctx) return IE_INVAL;
4053 files_node = xpath_ctx->node;
4055 /* Find documents */
4056 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
4057 if (!result) {
4058 err = IE_XML;
4059 goto leave;
4062 /* No match */
4063 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4064 isds_printf_message(context,
4065 _("Message does not contain any document"));
4066 err = IE_ISDS;
4067 goto leave;
4071 /* Iterate over documents */
4072 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4074 /* Allocate and append list item */
4075 document = calloc(1, sizeof(*document));
4076 if (!document) {
4077 err = IE_NOMEM;
4078 goto leave;
4080 document->destructor = (void (*)(void **))isds_document_free;
4081 if (i == 0) *documents = document;
4082 else prev_document->next = document;
4083 prev_document = document;
4085 /* Extract document */
4086 xpath_ctx->node = result->nodesetval->nodeTab[i];
4087 err = extract_document(context,
4088 (struct isds_document **) &(document->data), xpath_ctx);
4089 if (err) goto leave;
4093 leave:
4094 if (err) isds_list_free(documents);
4095 xmlXPathFreeObject(result);
4096 xpath_ctx->node = files_node;
4097 return err;
4101 #if HAVE_LIBCURL
4102 /* Convert isds:dmRecord XML tree into structure
4103 * @context is ISDS context
4104 * @envelope is automatically reallocated message envelope structure
4105 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4106 * In case of error @envelope will be freed. */
4107 static isds_error extract_DmRecord(struct isds_ctx *context,
4108 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4109 isds_error err = IE_SUCCESS;
4110 xmlXPathObjectPtr result = NULL;
4112 if (!context) return IE_INVALID_CONTEXT;
4113 if (!envelope) return IE_INVAL;
4114 isds_envelope_free(envelope);
4115 if (!xpath_ctx) return IE_INVAL;
4118 *envelope = calloc(1, sizeof(**envelope));
4119 if (!*envelope) {
4120 err = IE_NOMEM;
4121 goto leave;
4125 /* Extract tRecord data */
4126 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4128 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4129 * dmAcceptanceTime. */
4130 err = append_status_size_times(context, envelope, xpath_ctx);
4131 if (err) goto leave;
4133 /* Extract envelope elements added by sender and ISDS
4134 * (XSD: gMessageEnvelope type) */
4135 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4136 if (err) goto leave;
4138 /* Get message type */
4139 err = append_message_type(context, envelope, xpath_ctx);
4140 if (err) goto leave;
4143 leave:
4144 if (err) isds_envelope_free(envelope);
4145 xmlXPathFreeObject(result);
4146 return err;
4150 /* Convert XSD:tStateChangesRecord type XML tree into structure
4151 * @context is ISDS context
4152 * @changed_status is automatically reallocated message state change structure
4153 * @xpath_ctx is XPath context with current node as element of
4154 * XSD:tStateChangesRecord type
4155 * In case of error @changed_status will be freed. */
4156 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4157 struct isds_message_status_change **changed_status,
4158 xmlXPathContextPtr xpath_ctx) {
4159 isds_error err = IE_SUCCESS;
4160 xmlXPathObjectPtr result = NULL;
4161 unsigned long int *unumber = NULL;
4162 char *string = NULL;
4164 if (!context) return IE_INVALID_CONTEXT;
4165 if (!changed_status) return IE_INVAL;
4166 isds_message_status_change_free(changed_status);
4167 if (!xpath_ctx) return IE_INVAL;
4170 *changed_status = calloc(1, sizeof(**changed_status));
4171 if (!*changed_status) {
4172 err = IE_NOMEM;
4173 goto leave;
4177 /* Extract tGetStateChangesInput data */
4178 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4180 /* dmEventTime is mandatory */
4181 EXTRACT_STRING("isds:dmEventTime", string);
4182 if (string) {
4183 err = timestring2timeval((xmlChar *) string,
4184 &((*changed_status)->time));
4185 if (err) {
4186 char *string_locale = _isds_utf82locale(string);
4187 if (err == IE_DATE) err = IE_ISDS;
4188 isds_printf_message(context,
4189 _("Could not convert dmEventTime as ISO time: %s"),
4190 string_locale);
4191 free(string_locale);
4192 goto leave;
4194 zfree(string);
4197 /* dmMessageStatus element is mandatory */
4198 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4199 if (!unumber) {
4200 isds_log_message(context,
4201 _("Missing mandatory isds:dmMessageStatus integer"));
4202 err = IE_ISDS;
4203 goto leave;
4205 err = uint2isds_message_status(context, unumber,
4206 &((*changed_status)->dmMessageStatus));
4207 if (err) {
4208 if (err == IE_ENUM) err = IE_ISDS;
4209 goto leave;
4211 zfree(unumber);
4214 leave:
4215 free(unumber);
4216 free(string);
4217 if (err) isds_message_status_change_free(changed_status);
4218 xmlXPathFreeObject(result);
4219 return err;
4221 #endif /* HAVE_LIBCURL */
4224 /* Find and convert isds:dmHash XML tree into structure
4225 * @context is ISDS context
4226 * @envelope is automatically reallocated message hash structure
4227 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4228 * In case of error @hash will be freed. */
4229 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4230 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4231 isds_error err = IE_SUCCESS;
4232 xmlNodePtr old_ctx_node;
4233 xmlXPathObjectPtr result = NULL;
4234 char *string = NULL;
4236 if (!context) return IE_INVALID_CONTEXT;
4237 if (!hash) return IE_INVAL;
4238 isds_hash_free(hash);
4239 if (!xpath_ctx) return IE_INVAL;
4241 old_ctx_node = xpath_ctx->node;
4243 *hash = calloc(1, sizeof(**hash));
4244 if (!*hash) {
4245 err = IE_NOMEM;
4246 goto leave;
4249 /* Locate dmHash */
4250 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4251 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4252 err = IE_ISDS;
4253 goto leave;
4255 if (err) {
4256 err = IE_ERROR;
4257 goto leave;
4260 /* Get hash algorithm */
4261 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4262 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4263 if (err) {
4264 if (err == IE_ENUM) {
4265 char *string_locale = _isds_utf82locale(string);
4266 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4267 string_locale);
4268 free(string_locale);
4270 goto leave;
4272 zfree(string);
4274 /* Get hash value */
4275 EXTRACT_STRING(".", string);
4276 if (!string) {
4277 isds_printf_message(context,
4278 _("sisds:dmHash element is missing hash value"));
4279 err = IE_ISDS;
4280 goto leave;
4282 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4283 if ((*hash)->length == (size_t) -1) {
4284 isds_printf_message(context,
4285 _("Error while Base64-decoding hash value"));
4286 err = IE_ERROR;
4287 goto leave;
4290 leave:
4291 if (err) isds_hash_free(hash);
4292 free(string);
4293 xmlXPathFreeObject(result);
4294 xpath_ctx->node = old_ctx_node;
4295 return err;
4299 /* Find and append isds:dmQTimestamp XML tree into envelope.
4300 * Because one service is allowed to miss time-stamp content, and we think
4301 * other could too (flaw in specification), this function is deliberated and
4302 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4303 * @context is ISDS context
4304 * @envelope is automatically allocated envelope structure
4305 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4306 * child
4307 * In case of error @envelope will be freed. */
4308 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4309 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4310 isds_error err = IE_SUCCESS;
4311 xmlXPathObjectPtr result = NULL;
4312 char *string = NULL;
4314 if (!context) return IE_INVALID_CONTEXT;
4315 if (!envelope) return IE_INVAL;
4316 if (!xpath_ctx) {
4317 isds_envelope_free(envelope);
4318 return IE_INVAL;
4321 if (!*envelope) {
4322 *envelope = calloc(1, sizeof(**envelope));
4323 if (!*envelope) {
4324 err = IE_NOMEM;
4325 goto leave;
4327 } else {
4328 zfree((*envelope)->timestamp);
4329 (*envelope)->timestamp_length = 0;
4332 /* Get dmQTimestamp */
4333 EXTRACT_STRING("sisds:dmQTimestamp", string);
4334 if (!string) {
4335 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4336 goto leave;
4338 (*envelope)->timestamp_length =
4339 _isds_b64decode(string, &((*envelope)->timestamp));
4340 if ((*envelope)->timestamp_length == (size_t) -1) {
4341 isds_printf_message(context,
4342 _("Error while Base64-decoding time stamp value"));
4343 err = IE_ERROR;
4344 goto leave;
4347 leave:
4348 if (err) isds_envelope_free(envelope);
4349 free(string);
4350 xmlXPathFreeObject(result);
4351 return err;
4355 /* Convert XSD tReturnedMessage XML tree into message structure.
4356 * It does not store serialized XML tree into message->raw.
4357 * It does store (pointer to) parsed XML tree into message->xml if needed.
4358 * @context is ISDS context
4359 * @include_documents Use true if documents must be extracted
4360 * (tReturnedMessage XSD type), use false if documents shall be omitted
4361 * (tReturnedMessageEnvelope).
4362 * @message is automatically reallocated message structure
4363 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4364 * type
4365 * In case of error @message will be freed. */
4366 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4367 const _Bool include_documents, struct isds_message **message,
4368 xmlXPathContextPtr xpath_ctx) {
4369 isds_error err = IE_SUCCESS;
4370 xmlNodePtr message_node;
4372 if (!context) return IE_INVALID_CONTEXT;
4373 if (!message) return IE_INVAL;
4374 isds_message_free(message);
4375 if (!xpath_ctx) return IE_INVAL;
4378 *message = calloc(1, sizeof(**message));
4379 if (!*message) {
4380 err = IE_NOMEM;
4381 goto leave;
4384 /* Save message XPATH context node */
4385 message_node = xpath_ctx->node;
4388 /* Extract dmDM */
4389 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4390 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4391 if (err) { err = IE_ERROR; goto leave; }
4392 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4393 if (err) goto leave;
4395 if (include_documents) {
4396 struct isds_list *item;
4398 /* Extract dmFiles */
4399 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4400 xpath_ctx);
4401 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4402 err = IE_ISDS; goto leave;
4404 if (err) { err = IE_ERROR; goto leave; }
4405 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4406 if (err) goto leave;
4408 /* Store xmlDoc of this message if needed */
4409 /* Only if we got a XML document in all the documents. */
4410 for (item = (*message)->documents; item; item = item->next) {
4411 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4412 (*message)->xml = xpath_ctx->doc;
4413 break;
4419 /* Restore context to message */
4420 xpath_ctx->node = message_node;
4422 /* Extract dmHash */
4423 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4424 xpath_ctx);
4425 if (err) goto leave;
4427 /* Extract dmQTimestamp, */
4428 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4429 xpath_ctx);
4430 if (err) goto leave;
4432 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4433 * dmAcceptanceTime. */
4434 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4435 if (err) goto leave;
4437 /* Get message type */
4438 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4439 if (err) goto leave;
4441 leave:
4442 if (err) isds_message_free(message);
4443 return err;
4447 /* Extract message event into reallocated isds_event structure
4448 * @context is ISDS context
4449 * @event is automatically reallocated message event structure
4450 * @xpath_ctx is XPath context with current node as isds:dmEvent
4451 * In case of error @event will be freed. */
4452 static isds_error extract_event(struct isds_ctx *context,
4453 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4454 isds_error err = IE_SUCCESS;
4455 xmlXPathObjectPtr result = NULL;
4456 xmlNodePtr event_node;
4457 char *string = NULL;
4459 if (!context) return IE_INVALID_CONTEXT;
4460 if (!event) return IE_INVAL;
4461 isds_event_free(event);
4462 if (!xpath_ctx) return IE_INVAL;
4463 event_node = xpath_ctx->node;
4465 *event = calloc(1, sizeof(**event));
4466 if (!*event) {
4467 err = IE_NOMEM;
4468 goto leave;
4471 /* Extract event data.
4472 * All elements are optional according XSD. That's funny. */
4473 EXTRACT_STRING("sisds:dmEventTime", string);
4474 if (string) {
4475 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4476 if (err) {
4477 char *string_locale = _isds_utf82locale(string);
4478 if (err == IE_DATE) err = IE_ISDS;
4479 isds_printf_message(context,
4480 _("Could not convert dmEventTime as ISO time: %s"),
4481 string_locale);
4482 free(string_locale);
4483 goto leave;
4485 zfree(string);
4488 /* dmEventDescr element has prefix and the rest */
4489 EXTRACT_STRING("sisds:dmEventDescr", string);
4490 if (string) {
4491 err = eventstring2event((xmlChar *) string, *event);
4492 if (err) goto leave;
4493 zfree(string);
4496 leave:
4497 if (err) isds_event_free(event);
4498 free(string);
4499 xmlXPathFreeObject(result);
4500 xpath_ctx->node = event_node;
4501 return err;
4505 /* Convert element of XSD tEventsArray type from XML tree into
4506 * isds_list of isds_event's structure. The list is automatically reallocated.
4507 * @context is ISDS context
4508 * @events is automatically reallocated list of event structures
4509 * @xpath_ctx is XPath context with current node as tEventsArray
4510 * In case of error @events will be freed. */
4511 static isds_error extract_events(struct isds_ctx *context,
4512 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4513 isds_error err = IE_SUCCESS;
4514 xmlXPathObjectPtr result = NULL;
4515 xmlNodePtr events_node;
4516 struct isds_list *event, *prev_event = NULL;
4518 if (!context) return IE_INVALID_CONTEXT;
4519 if (!events) return IE_INVAL;
4520 if (!xpath_ctx) return IE_INVAL;
4521 events_node = xpath_ctx->node;
4523 /* Free old list */
4524 isds_list_free(events);
4526 /* Find events */
4527 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4528 if (!result) {
4529 err = IE_XML;
4530 goto leave;
4533 /* No match */
4534 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4535 isds_printf_message(context,
4536 _("Delivery info does not contain any event"));
4537 err = IE_ISDS;
4538 goto leave;
4542 /* Iterate over events */
4543 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4545 /* Allocate and append list item */
4546 event = calloc(1, sizeof(*event));
4547 if (!event) {
4548 err = IE_NOMEM;
4549 goto leave;
4551 event->destructor = (void (*)(void **))isds_event_free;
4552 if (i == 0) *events = event;
4553 else prev_event->next = event;
4554 prev_event = event;
4556 /* Extract event */
4557 xpath_ctx->node = result->nodesetval->nodeTab[i];
4558 err = extract_event(context,
4559 (struct isds_event **) &(event->data), xpath_ctx);
4560 if (err) goto leave;
4564 leave:
4565 if (err) isds_list_free(events);
4566 xmlXPathFreeObject(result);
4567 xpath_ctx->node = events_node;
4568 return err;
4572 #if HAVE_LIBCURL
4573 /* Insert Base64 encoded data as element with text child.
4574 * @context is session context
4575 * @parent is XML node to append @element with @data as child
4576 * @ns is XML namespace of @element, use NULL to inherit from @parent
4577 * @element is UTF-8 encoded name of new element
4578 * @data is bit stream to encode into @element
4579 * @length is size of @data in bytes
4580 * @return standard error code and fill long error message if needed */
4581 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4582 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4583 const void *data, size_t length) {
4584 isds_error err = IE_SUCCESS;
4585 xmlNodePtr node;
4587 if (!context) return IE_INVALID_CONTEXT;
4588 if (!data && length > 0) return IE_INVAL;
4589 if (!parent || !element) return IE_INVAL;
4591 xmlChar *base64data = NULL;
4592 base64data = (xmlChar *) _isds_b64encode(data, length);
4593 if (!base64data) {
4594 isds_printf_message(context,
4595 ngettext("Not enough memory to encode %zd byte into Base64",
4596 "Not enough memory to encode %zd bytes into Base64",
4597 length),
4598 length);
4599 err = IE_NOMEM;
4600 goto leave;
4602 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4604 leave:
4605 free(base64data);
4606 return err;
4610 /* Convert isds_document structure into XML tree and append to dmFiles node.
4611 * @context is session context
4612 * @document is ISDS document
4613 * @dm_files is XML element the resulting tree will be appended to as a child.
4614 * @return error code, in case of error context' message is filled. */
4615 static isds_error insert_document(struct isds_ctx *context,
4616 struct isds_document *document, xmlNodePtr dm_files) {
4617 isds_error err = IE_SUCCESS;
4618 xmlNodePtr new_file = NULL, file = NULL, node;
4619 xmlAttrPtr attribute_node;
4621 if (!context) return IE_INVALID_CONTEXT;
4622 if (!document || !dm_files) return IE_INVAL;
4624 /* Allocate new dmFile */
4625 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4626 if (!new_file) {
4627 isds_printf_message(context, _("Could not allocate main dmFile"));
4628 err = IE_ERROR;
4629 goto leave;
4631 /* Append the new dmFile.
4632 * XXX: Main document must go first */
4633 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4634 file = xmlAddPrevSibling(dm_files->children, new_file);
4635 else
4636 file = xmlAddChild(dm_files, new_file);
4638 if (!file) {
4639 xmlFreeNode(new_file); new_file = NULL;
4640 isds_printf_message(context, _("Could not add dmFile child to "
4641 "%s element"), dm_files->name);
4642 err = IE_ERROR;
4643 goto leave;
4646 /* @dmMimeType is required */
4647 if (!document->dmMimeType) {
4648 isds_log_message(context,
4649 _("Document is missing mandatory MIME type definition"));
4650 err = IE_INVAL;
4651 goto leave;
4653 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4655 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4656 if (!string) {
4657 isds_printf_message(context,
4658 _("Document has unknown dmFileMetaType: %ld"),
4659 document->dmFileMetaType);
4660 err = IE_ENUM;
4661 goto leave;
4663 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4665 if (document->dmFileGuid) {
4666 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4668 if (document->dmUpFileGuid) {
4669 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4672 /* @dmFileDescr is required */
4673 if (!document->dmFileDescr) {
4674 isds_log_message(context,
4675 _("Document is missing mandatory description (title)"));
4676 err = IE_INVAL;
4677 goto leave;
4679 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4681 if (document->dmFormat) {
4682 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4686 /* Insert content (body) of the document. */
4687 if (document->is_xml) {
4688 /* XML document requested */
4690 /* Allocate new dmXMLContent */
4691 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4692 if (!xmlcontent) {
4693 isds_printf_message(context,
4694 _("Could not allocate dmXMLContent element"));
4695 err = IE_ERROR;
4696 goto leave;
4698 /* Append it */
4699 node = xmlAddChild(file, xmlcontent);
4700 if (!node) {
4701 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4702 isds_printf_message(context,
4703 _("Could not add dmXMLContent child to %s element"),
4704 file->name);
4705 err = IE_ERROR;
4706 goto leave;
4709 /* Copy non-empty node list */
4710 if (document->xml_node_list) {
4711 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4712 document->xml_node_list);
4713 if (!content) {
4714 isds_printf_message(context,
4715 _("Not enough memory to copy XML document"));
4716 err = IE_NOMEM;
4717 goto leave;
4720 if (!xmlAddChildList(node, content)) {
4721 xmlFreeNodeList(content);
4722 isds_printf_message(context,
4723 _("Error while adding XML document into dmXMLContent"));
4724 err = IE_XML;
4725 goto leave;
4727 /* XXX: We cannot free the content here because it's part of node's
4728 * document since now. It will be freed with it automatically. */
4730 } else {
4731 /* Binary document requested */
4732 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4733 document->data, document->data_length);
4734 if (err) goto leave;
4737 leave:
4738 return err;
4742 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4743 * The copy must be preallocated, the date are just appended into structure.
4744 * @context is ISDS context
4745 * @copy is message copy structure
4746 * @xpath_ctx is XPath context with current node as tMStatus */
4747 static isds_error append_TMStatus(struct isds_ctx *context,
4748 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4749 isds_error err = IE_SUCCESS;
4750 xmlXPathObjectPtr result = NULL;
4751 char *code = NULL, *message = NULL;
4753 if (!context) return IE_INVALID_CONTEXT;
4754 if (!copy || !xpath_ctx) return IE_INVAL;
4756 /* Free old values */
4757 zfree(copy->dmStatus);
4758 zfree(copy->dmID);
4760 /* Get error specific to this copy */
4761 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4762 if (!code) {
4763 isds_log_message(context,
4764 _("Missing isds:dmStatusCode under "
4765 "XSD:tMStatus type element"));
4766 err = IE_ISDS;
4767 goto leave;
4770 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4771 /* This copy failed */
4772 copy->error = IE_ISDS;
4773 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4774 if (message) {
4775 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4776 if (!copy->dmStatus) {
4777 copy->dmStatus = code;
4778 code = NULL;
4780 } else {
4781 copy->dmStatus = code;
4782 code = NULL;
4784 } else {
4785 /* This copy succeeded. In this case only, message ID is valid */
4786 copy->error = IE_SUCCESS;
4788 EXTRACT_STRING("isds:dmID", copy->dmID);
4789 if (!copy->dmID) {
4790 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4791 "but did not returned assigned message ID\n"));
4792 err = IE_ISDS;
4796 leave:
4797 free(code);
4798 free(message);
4799 xmlXPathFreeObject(result);
4800 return err;
4804 /* Insert struct isds_approval data (box approval) into XML tree
4805 * @context is session context
4806 * @approval is libisds structure with approval description. NULL is
4807 * acceptable.
4808 * @parent is XML element to append @approval to */
4809 static isds_error insert_GExtApproval(struct isds_ctx *context,
4810 const struct isds_approval *approval, xmlNodePtr parent) {
4812 isds_error err = IE_SUCCESS;
4813 xmlNodePtr node;
4815 if (!context) return IE_INVALID_CONTEXT;
4816 if (!parent) return IE_INVAL;
4818 if (!approval) return IE_SUCCESS;
4820 /* Build XSD:gExtApproval */
4821 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4822 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4824 leave:
4825 return err;
4829 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4830 * code
4831 * @context is session context
4832 * @service_name is name of SERVICE_DB_ACCESS
4833 * @response is reallocated server SOAP body response as XML document
4834 * @raw_response is reallocated bit stream with response body. Use
4835 * NULL if you don't care
4836 * @raw_response_length is size of @raw_response in bytes
4837 * @code is reallocated ISDS status code
4838 * @status_message is reallocated ISDS status message
4839 * @return error coded from lower layer, context message will be set up
4840 * appropriately. */
4841 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4842 const xmlChar *service_name,
4843 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4844 xmlChar **code, xmlChar **status_message) {
4846 isds_error err = IE_SUCCESS;
4847 char *service_name_locale = NULL;
4848 xmlNodePtr request = NULL, node;
4849 xmlNsPtr isds_ns = NULL;
4851 if (!context) return IE_INVALID_CONTEXT;
4852 if (!service_name) return IE_INVAL;
4853 if (!response || !code || !status_message) return IE_INVAL;
4854 if (!raw_response_length && raw_response) return IE_INVAL;
4856 /* Free output argument */
4857 xmlFreeDoc(*response); *response = NULL;
4858 if (raw_response) zfree(*raw_response);
4859 zfree(*code);
4860 zfree(*status_message);
4863 /* Check if connection is established
4864 * TODO: This check should be done downstairs. */
4865 if (!context->curl) return IE_CONNECTION_CLOSED;
4867 service_name_locale = _isds_utf82locale((char*)service_name);
4868 if (!service_name_locale) {
4869 err = IE_NOMEM;
4870 goto leave;
4873 /* Build request */
4874 request = xmlNewNode(NULL, service_name);
4875 if (!request) {
4876 isds_printf_message(context,
4877 _("Could not build %s request"), service_name_locale);
4878 err = IE_ERROR;
4879 goto leave;
4881 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4882 if(!isds_ns) {
4883 isds_log_message(context, _("Could not create ISDS name space"));
4884 err = IE_ERROR;
4885 goto leave;
4887 xmlSetNs(request, isds_ns);
4890 /* Add XSD:tDummyInput child */
4891 INSERT_STRING(request, "dbDummy", NULL);
4894 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4895 service_name_locale);
4897 /* Send request */
4898 err = _isds(context, SERVICE_DB_ACCESS, request, response,
4899 raw_response, raw_response_length);
4900 xmlFreeNode(request); request = NULL;
4902 if (err) {
4903 isds_log(ILF_ISDS, ILL_DEBUG,
4904 _("Processing ISDS response on %s request failed\n"),
4905 service_name_locale);
4906 goto leave;
4909 /* Check for response status */
4910 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4911 code, status_message, NULL);
4912 if (err) {
4913 isds_log(ILF_ISDS, ILL_DEBUG,
4914 _("ISDS response on %s request is missing status\n"),
4915 service_name_locale);
4916 goto leave;
4919 /* Request processed, but nothing found */
4920 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4921 char *code_locale = _isds_utf82locale((char*) *code);
4922 char *status_message_locale =
4923 _isds_utf82locale((char*) *status_message);
4924 isds_log(ILF_ISDS, ILL_DEBUG,
4925 _("Server refused %s request (code=%s, message=%s)\n"),
4926 service_name_locale, code_locale, status_message_locale);
4927 isds_log_message(context, status_message_locale);
4928 free(code_locale);
4929 free(status_message_locale);
4930 err = IE_ISDS;
4931 goto leave;
4934 leave:
4935 free(service_name_locale);
4936 xmlFreeNode(request);
4937 return err;
4939 #endif
4942 /* Get data about logged in user and his box. */
4943 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4944 struct isds_DbOwnerInfo **db_owner_info) {
4945 isds_error err = IE_SUCCESS;
4946 #if HAVE_LIBCURL
4947 xmlDocPtr response = NULL;
4948 xmlChar *code = NULL, *message = NULL;
4949 xmlXPathContextPtr xpath_ctx = NULL;
4950 xmlXPathObjectPtr result = NULL;
4951 char *string = NULL;
4952 #endif
4954 if (!context) return IE_INVALID_CONTEXT;
4955 zfree(context->long_message);
4956 if (!db_owner_info) return IE_INVAL;
4957 isds_DbOwnerInfo_free(db_owner_info);
4959 #if HAVE_LIBCURL
4960 /* Check if connection is established */
4961 if (!context->curl) return IE_CONNECTION_CLOSED;
4964 /* Do request and check for success */
4965 err = build_send_check_dbdummy_request(context,
4966 BAD_CAST "GetOwnerInfoFromLogin",
4967 &response, NULL, NULL, &code, &message);
4968 if (err) goto leave;
4971 /* Extract data */
4972 /* Prepare structure */
4973 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4974 if (!*db_owner_info) {
4975 err = IE_NOMEM;
4976 goto leave;
4978 xpath_ctx = xmlXPathNewContext(response);
4979 if (!xpath_ctx) {
4980 err = IE_ERROR;
4981 goto leave;
4983 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4984 err = IE_ERROR;
4985 goto leave;
4988 /* Set context node */
4989 result = xmlXPathEvalExpression(BAD_CAST
4990 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4991 if (!result) {
4992 err = IE_ERROR;
4993 goto leave;
4995 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4996 isds_log_message(context, _("Missing dbOwnerInfo element"));
4997 err = IE_ISDS;
4998 goto leave;
5000 if (result->nodesetval->nodeNr > 1) {
5001 isds_log_message(context, _("Multiple dbOwnerInfo element"));
5002 err = IE_ISDS;
5003 goto leave;
5005 xpath_ctx->node = result->nodesetval->nodeTab[0];
5006 xmlXPathFreeObject(result); result = NULL;
5008 /* Extract it */
5009 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
5012 leave:
5013 if (err) {
5014 isds_DbOwnerInfo_free(db_owner_info);
5017 free(string);
5018 xmlXPathFreeObject(result);
5019 xmlXPathFreeContext(xpath_ctx);
5021 free(code);
5022 free(message);
5023 xmlFreeDoc(response);
5025 if (!err)
5026 isds_log(ILF_ISDS, ILL_DEBUG,
5027 _("GetOwnerInfoFromLogin request processed by server "
5028 "successfully.\n"));
5029 #else /* not HAVE_LIBCURL */
5030 err = IE_NOTSUP;
5031 #endif
5033 return err;
5037 /* Get data about logged in user. */
5038 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
5039 struct isds_DbUserInfo **db_user_info) {
5040 isds_error err = IE_SUCCESS;
5041 #if HAVE_LIBCURL
5042 xmlDocPtr response = NULL;
5043 xmlChar *code = NULL, *message = NULL;
5044 xmlXPathContextPtr xpath_ctx = NULL;
5045 xmlXPathObjectPtr result = NULL;
5046 #endif
5048 if (!context) return IE_INVALID_CONTEXT;
5049 zfree(context->long_message);
5050 if (!db_user_info) return IE_INVAL;
5051 isds_DbUserInfo_free(db_user_info);
5053 #if HAVE_LIBCURL
5054 /* Check if connection is established */
5055 if (!context->curl) return IE_CONNECTION_CLOSED;
5058 /* Do request and check for success */
5059 err = build_send_check_dbdummy_request(context,
5060 BAD_CAST "GetUserInfoFromLogin",
5061 &response, NULL, NULL, &code, &message);
5062 if (err) goto leave;
5065 /* Extract data */
5066 /* Prepare structure */
5067 *db_user_info = calloc(1, sizeof(**db_user_info));
5068 if (!*db_user_info) {
5069 err = IE_NOMEM;
5070 goto leave;
5072 xpath_ctx = xmlXPathNewContext(response);
5073 if (!xpath_ctx) {
5074 err = IE_ERROR;
5075 goto leave;
5077 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5078 err = IE_ERROR;
5079 goto leave;
5082 /* Set context node */
5083 result = xmlXPathEvalExpression(BAD_CAST
5084 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
5085 if (!result) {
5086 err = IE_ERROR;
5087 goto leave;
5089 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5090 isds_log_message(context, _("Missing dbUserInfo element"));
5091 err = IE_ISDS;
5092 goto leave;
5094 if (result->nodesetval->nodeNr > 1) {
5095 isds_log_message(context, _("Multiple dbUserInfo element"));
5096 err = IE_ISDS;
5097 goto leave;
5099 xpath_ctx->node = result->nodesetval->nodeTab[0];
5100 xmlXPathFreeObject(result); result = NULL;
5102 /* Extract it */
5103 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5105 leave:
5106 if (err) {
5107 isds_DbUserInfo_free(db_user_info);
5110 xmlXPathFreeObject(result);
5111 xmlXPathFreeContext(xpath_ctx);
5113 free(code);
5114 free(message);
5115 xmlFreeDoc(response);
5117 if (!err)
5118 isds_log(ILF_ISDS, ILL_DEBUG,
5119 _("GetUserInfoFromLogin request processed by server "
5120 "successfully.\n"));
5121 #else /* not HAVE_LIBCURL */
5122 err = IE_NOTSUP;
5123 #endif
5125 return err;
5129 /* Get expiration time of current password
5130 * @context is session context
5131 * @expiration is automatically reallocated time when password expires. If
5132 * password expiration is disabled, NULL will be returned. In case of error
5133 * it will be nulled too. */
5134 isds_error isds_get_password_expiration(struct isds_ctx *context,
5135 struct timeval **expiration) {
5136 isds_error err = IE_SUCCESS;
5137 #if HAVE_LIBCURL
5138 xmlDocPtr response = NULL;
5139 xmlChar *code = NULL, *message = NULL;
5140 xmlXPathContextPtr xpath_ctx = NULL;
5141 xmlXPathObjectPtr result = NULL;
5142 char *string = NULL;
5143 #endif
5145 if (!context) return IE_INVALID_CONTEXT;
5146 zfree(context->long_message);
5147 if (!expiration) return IE_INVAL;
5148 zfree(*expiration);
5150 #if HAVE_LIBCURL
5151 /* Check if connection is established */
5152 if (!context->curl) return IE_CONNECTION_CLOSED;
5155 /* Do request and check for success */
5156 err = build_send_check_dbdummy_request(context,
5157 BAD_CAST "GetPasswordInfo",
5158 &response, NULL, NULL, &code, &message);
5159 if (err) goto leave;
5162 /* Extract data */
5163 xpath_ctx = xmlXPathNewContext(response);
5164 if (!xpath_ctx) {
5165 err = IE_ERROR;
5166 goto leave;
5168 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5169 err = IE_ERROR;
5170 goto leave;
5173 /* Set context node */
5174 result = xmlXPathEvalExpression(BAD_CAST
5175 "/isds:GetPasswordInfoResponse", xpath_ctx);
5176 if (!result) {
5177 err = IE_ERROR;
5178 goto leave;
5180 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5181 isds_log_message(context,
5182 _("Missing GetPasswordInfoResponse element"));
5183 err = IE_ISDS;
5184 goto leave;
5186 if (result->nodesetval->nodeNr > 1) {
5187 isds_log_message(context,
5188 _("Multiple GetPasswordInfoResponse element"));
5189 err = IE_ISDS;
5190 goto leave;
5192 xpath_ctx->node = result->nodesetval->nodeTab[0];
5193 xmlXPathFreeObject(result); result = NULL;
5195 /* Extract expiration date */
5196 EXTRACT_STRING("isds:pswExpDate", string);
5197 if (string) {
5198 /* And convert it if any returned. Otherwise expiration is disabled. */
5199 err = timestring2timeval((xmlChar *) string, expiration);
5200 if (err) {
5201 char *string_locale = _isds_utf82locale(string);
5202 if (err == IE_DATE) err = IE_ISDS;
5203 isds_printf_message(context,
5204 _("Could not convert pswExpDate as ISO time: %s"),
5205 string_locale);
5206 free(string_locale);
5207 goto leave;
5211 leave:
5212 if (err) {
5213 if (*expiration) {
5214 zfree(*expiration);
5218 free(string);
5219 xmlXPathFreeObject(result);
5220 xmlXPathFreeContext(xpath_ctx);
5222 free(code);
5223 free(message);
5224 xmlFreeDoc(response);
5226 if (!err)
5227 isds_log(ILF_ISDS, ILL_DEBUG,
5228 _("GetPasswordInfo request processed by server "
5229 "successfully.\n"));
5230 #else /* not HAVE_LIBCURL */
5231 err = IE_NOTSUP;
5232 #endif
5234 return err;
5238 #if HAVE_LIBCURL
5239 /* Request delivering new TOTP code from ISDS through side channel before
5240 * changing password.
5241 * @context is session context
5242 * @password is current password.
5243 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5244 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5245 * function for more details.
5246 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5247 * NULL, if you don't care.
5248 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5249 * error code. */
5250 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5251 const char *password, struct isds_otp *otp, char **refnumber) {
5252 isds_error err = IE_SUCCESS;
5253 char *saved_url = NULL; /* No copy */
5254 #if HAVE_CURL_REAUTHORIZATION_BUG
5255 CURL *saved_curl = NULL; /* No copy */
5256 #endif
5257 xmlNsPtr isds_ns = NULL;
5258 xmlNodePtr request = NULL;
5259 xmlDocPtr response = NULL;
5260 xmlChar *code = NULL, *message = NULL;
5261 const xmlChar *codes[] = {
5262 BAD_CAST "2300",
5263 BAD_CAST "2301",
5264 BAD_CAST "2302"
5266 const char *meanings[] = {
5267 N_("Unexpected error"),
5268 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5269 N_("One-time code could not been sent. Try later again.")
5271 const isds_otp_resolution resolutions[] = {
5272 OTP_RESOLUTION_UNKNOWN,
5273 OTP_RESOLUTION_TO_FAST,
5274 OTP_RESOLUTION_TOTP_NOT_SENT
5277 if (NULL == context) return IE_INVALID_CONTEXT;
5278 zfree(context->long_message);
5279 if (NULL == password) {
5280 isds_log_message(context,
5281 _("Second argument (password) of isds_change_password() "
5282 "is NULL"));
5283 return IE_INVAL;
5286 /* Check if connection is established
5287 * TODO: This check should be done downstairs. */
5288 if (!context->curl) return IE_CONNECTION_CLOSED;
5290 if (!context->otp) {
5291 isds_log_message(context, _("This function requires OTP-authenticated "
5292 "context"));
5293 return IE_INVALID_CONTEXT;
5295 if (NULL == otp) {
5296 isds_log_message(context, _("If one-time password authentication "
5297 "method is in use, requesting new OTP code requires "
5298 "one-time credentials argument either"));
5299 return IE_INVAL;
5301 if (otp->method != OTP_TIME) {
5302 isds_log_message(context, _("Requesting new time-based OTP code from "
5303 "server requires one-time password authentication "
5304 "method"));
5305 return IE_INVAL;
5307 if (otp->otp_code != NULL) {
5308 isds_log_message(context, _("Requesting new time-based OTP code from "
5309 "server requires undefined OTP code member in "
5310 "one-time credentials argument"));
5311 return IE_INVAL;
5315 /* Build request */
5316 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5317 if (!request) {
5318 isds_log_message(context, _("Could not build SendSMSCode request"));
5319 return IE_ERROR;
5321 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5322 if(!isds_ns) {
5323 isds_log_message(context, _("Could not create ISDS name space"));
5324 xmlFreeNode(request);
5325 return IE_ERROR;
5327 xmlSetNs(request, isds_ns);
5329 /* Change URL temporarily for sending this request only */
5331 char *new_url = NULL;
5332 if ((err = _isds_build_url_from_context(context,
5333 "%.*sasws/changePassword", &new_url))) {
5334 goto leave;
5336 saved_url = context->url;
5337 context->url = new_url;
5340 /* Store credentials for sending this request only */
5341 context->otp_credentials = otp;
5342 _isds_discard_credentials(context, 0);
5343 if ((err = _isds_store_credentials(context, context->saved_username,
5344 password, NULL))) {
5345 _isds_discard_credentials(context, 0);
5346 goto leave;
5348 #if HAVE_CURL_REAUTHORIZATION_BUG
5349 saved_curl = context->curl;
5350 context->curl = curl_easy_init();
5351 if (NULL == context->curl) {
5352 err = IE_ERROR;
5353 goto leave;
5355 if (context->timeout) {
5356 err = isds_set_timeout(context, context->timeout);
5357 if (err) goto leave;
5359 #endif
5361 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5363 /* Sent request */
5364 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5366 /* Remove temporal credentials */
5367 _isds_discard_credentials(context, 0);
5368 /* Detach pointer to OTP credentials from context */
5369 context->otp_credentials = NULL;
5370 /* Keep context->otp true to keep signaling this is OTP session */
5372 /* Destroy request */
5373 xmlFreeNode(request); request = NULL;
5375 if (err) {
5376 isds_log(ILF_ISDS, ILL_DEBUG,
5377 _("Processing ISDS response on SendSMSCode request failed\n"));
5378 goto leave;
5381 /* Check for response status */
5382 err = isds_response_status(context, SERVICE_ASWS, response,
5383 &code, &message, (xmlChar **)refnumber);
5384 if (err) {
5385 isds_log(ILF_ISDS, ILL_DEBUG,
5386 _("ISDS response on SendSMSCode request is missing "
5387 "status\n"));
5388 goto leave;
5391 /* Check for error */
5392 if (xmlStrcmp(code, BAD_CAST "0000")) {
5393 char *code_locale = _isds_utf82locale((char*)code);
5394 char *message_locale = _isds_utf82locale((char*)message);
5395 size_t i;
5396 isds_log(ILF_ISDS, ILL_DEBUG,
5397 _("Server refused to send new code on SendSMSCode "
5398 "request (code=%s, message=%s)\n"),
5399 code_locale, message_locale);
5401 /* Check for known error codes */
5402 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5403 if (!xmlStrcmp(code, codes[i])) break;
5405 if (i < sizeof(codes)/sizeof(*codes)) {
5406 isds_log_message(context, _(meanings[i]));
5407 /* Mimic otp->resolution according to the code, specification does
5408 * prescribe OTP header to be available. */
5409 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5410 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5411 otp->resolution = resolutions[i];
5412 } else
5413 isds_log_message(context, message_locale);
5415 free(code_locale);
5416 free(message_locale);
5418 err = IE_ISDS;
5419 goto leave;
5422 /* Otherwise new code sent successfully */
5423 /* Mimic otp->resolution according to the code, specification does
5424 * prescribe OTP header to be available. */
5425 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5426 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5428 leave:
5429 if (NULL != saved_url) {
5430 /* Revert URL to original one */
5431 zfree(context->url);
5432 context->url = saved_url;
5434 #if HAVE_CURL_REAUTHORIZATION_BUG
5435 if (NULL != saved_curl) {
5436 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5437 context->curl = saved_curl;
5439 #endif
5441 free(code);
5442 free(message);
5443 xmlFreeDoc(response);
5444 xmlFreeNode(request);
5446 if (!err)
5447 isds_log(ILF_ISDS, ILL_DEBUG,
5448 _("New OTP code has been sent successfully on SendSMSCode "
5449 "request.\n"));
5450 return err;
5454 /* Convert response status code to isds_error code and set long message
5455 * @context is context to save long message to
5456 * @map is mapping from codes to errors and messages. Pass NULL for generic
5457 * handling.
5458 * @code is status code to translate
5459 * @message is non-localized status message to put into long message in case
5460 * of uknown error. It can be NULL if server did not provide any.
5461 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5462 * invalid invocation. */
5463 static isds_error statuscode2isds_error(struct isds_ctx *context,
5464 const struct code_map_isds_error *map,
5465 const xmlChar *code, const xmlChar *message) {
5466 if (NULL == code) {
5467 isds_log_message(context,
5468 _("NULL status code passed to statuscode2isds_error()"));
5469 return IE_INVAL;
5472 if (NULL != map) {
5473 /* Check for known error codes */
5474 for (int i=0; map->codes[i] != NULL; i++) {
5475 if (!xmlStrcmp(code, map->codes[i])) {
5476 isds_log_message(context, _(map->meanings[i]));
5477 return map->errors[i];
5482 /* Other error */
5483 if (xmlStrcmp(code, BAD_CAST "0000")) {
5484 char *message_locale = _isds_utf82locale((char*)message);
5485 if (NULL == message_locale)
5486 isds_log_message(context, _("ISDS server returned unknown error"));
5487 else
5488 isds_log_message(context, message_locale);
5489 free(message_locale);
5490 return IE_ISDS;
5493 return IE_SUCCESS;
5495 #endif
5498 /* Change user password in ISDS.
5499 * User must supply old password, new password will takes effect after some
5500 * time, current session can continue. Password must fulfill some constraints.
5501 * @context is session context
5502 * @old_password is current password.
5503 * @new_password is requested new password
5504 * @otp auxiliary data required if one-time password authentication is in use,
5505 * defines OTP code (if known) and returns fine grade resolution of OTP
5506 * procedure. Pass NULL, if one-time password authentication is not needed.
5507 * Please note the @otp argument must match OTP method used at log-in time. See
5508 * isds_login() function for more details.
5509 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5510 * NULL, if you don't care.
5511 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5512 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5513 * awaiting OTP code that has been delivered by side channel to the user. */
5514 isds_error isds_change_password(struct isds_ctx *context,
5515 const char *old_password, const char *new_password,
5516 struct isds_otp *otp, char **refnumber) {
5517 isds_error err = IE_SUCCESS;
5518 #if HAVE_LIBCURL
5519 char *saved_url = NULL; /* No copy */
5520 #if HAVE_CURL_REAUTHORIZATION_BUG
5521 CURL *saved_curl = NULL; /* No copy */
5522 #endif
5523 xmlNsPtr isds_ns = NULL;
5524 xmlNodePtr request = NULL, node;
5525 xmlDocPtr response = NULL;
5526 xmlChar *code = NULL, *message = NULL;
5527 const xmlChar *codes[] = {
5528 BAD_CAST "1066",
5529 BAD_CAST "1067",
5530 BAD_CAST "1079",
5531 BAD_CAST "1080",
5532 BAD_CAST "1081",
5533 BAD_CAST "1082",
5534 BAD_CAST "1083",
5535 BAD_CAST "1090",
5536 BAD_CAST "1091",
5537 BAD_CAST "2300",
5538 BAD_CAST "9204"
5540 const char *meanings[] = {
5541 N_("Password length must be between 8 and 32 characters"),
5542 N_("Password cannot be reused"), /* Server does not distinguish 1067
5543 and 1091 on ChangePasswordOTP */
5544 N_("Password contains forbidden character"),
5545 N_("Password must contain at least one upper-case letter, "
5546 "one lower-case, and one digit"),
5547 N_("Password cannot contain sequence of three identical characters"),
5548 N_("Password cannot contain user identifier"),
5549 N_("Password is too simmple"),
5550 N_("Old password is not valid"),
5551 N_("Password cannot be reused"),
5552 N_("Unexpected error"),
5553 N_("LDAP update error")
5555 #endif
5557 if (!context) return IE_INVALID_CONTEXT;
5558 zfree(context->long_message);
5559 if (NULL != refnumber)
5560 zfree(*refnumber);
5561 if (NULL == old_password) {
5562 isds_log_message(context,
5563 _("Second argument (old password) of isds_change_password() "
5564 "is NULL"));
5565 return IE_INVAL;
5567 if (NULL == otp && NULL == new_password) {
5568 isds_log_message(context,
5569 _("Third argument (new password) of isds_change_password() "
5570 "is NULL"));
5571 return IE_INVAL;
5574 #if HAVE_LIBCURL
5575 /* Check if connection is established
5576 * TODO: This check should be done downstairs. */
5577 if (!context->curl) return IE_CONNECTION_CLOSED;
5579 if (context->otp && NULL == otp) {
5580 isds_log_message(context, _("If one-time password authentication "
5581 "method is in use, changing password requires one-time "
5582 "credentials either"));
5583 return IE_INVAL;
5586 /* Build ChangeISDSPassword request */
5587 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5588 BAD_CAST "ChangePasswordOTP");
5589 if (!request) {
5590 isds_log_message(context, (NULL == otp) ?
5591 _("Could not build ChangeISDSPassword request") :
5592 _("Could not build ChangePasswordOTP request"));
5593 return IE_ERROR;
5595 isds_ns = xmlNewNs(request,
5596 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5597 NULL);
5598 if(!isds_ns) {
5599 isds_log_message(context, _("Could not create ISDS name space"));
5600 xmlFreeNode(request);
5601 return IE_ERROR;
5603 xmlSetNs(request, isds_ns);
5605 INSERT_STRING(request, "dbOldPassword", old_password);
5606 INSERT_STRING(request, "dbNewPassword", new_password);
5608 if (NULL != otp) {
5609 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5610 switch (otp->method) {
5611 case OTP_HMAC:
5612 isds_log(ILF_SEC, ILL_INFO,
5613 _("Selected authentication method: "
5614 "HMAC-based one-time password\n"));
5615 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5616 break;
5617 case OTP_TIME:
5618 isds_log(ILF_SEC, ILL_INFO,
5619 _("Selected authentication method: "
5620 "Time-based one-time password\n"));
5621 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5622 if (otp->otp_code == NULL) {
5623 isds_log(ILF_SEC, ILL_INFO,
5624 _("OTP code has not been provided by "
5625 "application, requesting server for "
5626 "new one.\n"));
5627 err = _isds_request_totp_code(context, old_password, otp,
5628 refnumber);
5629 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5630 goto leave;
5632 } else {
5633 isds_log(ILF_SEC, ILL_INFO,
5634 _("OTP code has been provided by "
5635 "application, not requesting server "
5636 "for new one.\n"));
5638 break;
5639 default:
5640 isds_log_message(context,
5641 _("Unknown one-time password authentication "
5642 "method requested by application"));
5643 err = IE_ENUM;
5644 goto leave;
5647 /* Change URL temporarily for sending this request only */
5649 char *new_url = NULL;
5650 if ((err = _isds_build_url_from_context(context,
5651 "%.*sasws/changePassword", &new_url))) {
5652 goto leave;
5654 saved_url = context->url;
5655 context->url = new_url;
5658 /* Store credentials for sending this request only */
5659 context->otp_credentials = otp;
5660 _isds_discard_credentials(context, 0);
5661 if ((err = _isds_store_credentials(context, context->saved_username,
5662 old_password, NULL))) {
5663 _isds_discard_credentials(context, 0);
5664 goto leave;
5666 #if HAVE_CURL_REAUTHORIZATION_BUG
5667 saved_curl = context->curl;
5668 context->curl = curl_easy_init();
5669 if (NULL == context->curl) {
5670 err = IE_ERROR;
5671 goto leave;
5673 if (context->timeout) {
5674 err = isds_set_timeout(context, context->timeout);
5675 if (err) goto leave;
5677 #endif
5680 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5681 _("Sending ChangeISDSPassword request to ISDS\n") :
5682 _("Sending ChangePasswordOTP request to ISDS\n"));
5684 /* Sent request */
5685 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5686 request, &response, NULL, NULL);
5688 if (otp) {
5689 /* Remove temporal credentials */
5690 _isds_discard_credentials(context, 0);
5691 /* Detach pointer to OTP credentials from context */
5692 context->otp_credentials = NULL;
5693 /* Keep context->otp true to keep signaling this is OTP session */
5696 /* Destroy request */
5697 xmlFreeNode(request); request = NULL;
5699 if (err) {
5700 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5701 _("Processing ISDS response on ChangeISDSPassword "
5702 "request failed\n") :
5703 _("Processing ISDS response on ChangePasswordOTP "
5704 "request failed\n"));
5705 goto leave;
5708 /* Check for response status */
5709 err = isds_response_status(context,
5710 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5711 &code, &message, (xmlChar **)refnumber);
5712 if (err) {
5713 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5714 _("ISDS response on ChangeISDSPassword request is missing "
5715 "status\n") :
5716 _("ISDS response on ChangePasswordOTP request is missing "
5717 "status\n"));
5718 goto leave;
5721 /* Check for known error codes */
5722 for (size_t i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5723 if (!xmlStrcmp(code, codes[i])) {
5724 char *code_locale = _isds_utf82locale((char*)code);
5725 char *message_locale = _isds_utf82locale((char*)message);
5726 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5727 _("Server refused to change password on ChangeISDSPassword "
5728 "request (code=%s, message=%s)\n") :
5729 _("Server refused to change password on ChangePasswordOTP "
5730 "request (code=%s, message=%s)\n"),
5731 code_locale, message_locale);
5732 free(code_locale);
5733 free(message_locale);
5734 isds_log_message(context, _(meanings[i]));
5735 err = IE_INVAL;
5736 goto leave;
5740 /* Other error */
5741 if (xmlStrcmp(code, BAD_CAST "0000")) {
5742 char *code_locale = _isds_utf82locale((char*)code);
5743 char *message_locale = _isds_utf82locale((char*)message);
5744 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5745 _("Server refused to change password on ChangeISDSPassword "
5746 "request (code=%s, message=%s)\n") :
5747 _("Server refused to change password on ChangePasswordOTP "
5748 "request (code=%s, message=%s)\n"),
5749 code_locale, message_locale);
5750 isds_log_message(context, message_locale);
5751 free(code_locale);
5752 free(message_locale);
5753 err = IE_ISDS;
5754 goto leave;
5757 /* Otherwise password changed successfully */
5759 leave:
5760 if (NULL != saved_url) {
5761 /* Revert URL to original one */
5762 zfree(context->url);
5763 context->url = saved_url;
5765 #if HAVE_CURL_REAUTHORIZATION_BUG
5766 if (NULL != saved_curl) {
5767 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5768 context->curl = saved_curl;
5770 #endif
5772 free(code);
5773 free(message);
5774 xmlFreeDoc(response);
5775 xmlFreeNode(request);
5777 if (!err)
5778 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5779 _("Password changed successfully on ChangeISDSPassword "
5780 "request.\n") :
5781 _("Password changed successfully on ChangePasswordOTP "
5782 "request.\n"));
5783 #else /* not HAVE_LIBCURL */
5784 err = IE_NOTSUP;
5785 #endif
5787 return err;
5791 #if HAVE_LIBCURL
5792 /* Generic middle part with request sending and response check.
5793 * It sends prepared request and checks for error code.
5794 * @context is ISDS session context.
5795 * @service is ISDS service handler
5796 * @service_name is name in scope of given @service
5797 * @request is XML tree with request. Will be freed to save memory.
5798 * @response is XML document outputting ISDS response.
5799 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5800 * @map is mapping from status code to library error. Pass NULL if no special
5801 * handling is requested.
5802 * NULL, if you don't care. */
5803 static isds_error send_destroy_request_check_response(
5804 struct isds_ctx *context,
5805 const isds_service service, const xmlChar *service_name,
5806 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5807 const struct code_map_isds_error *map) {
5808 isds_error err = IE_SUCCESS;
5809 char *service_name_locale = NULL;
5810 xmlChar *code = NULL, *message = NULL;
5813 if (!context) return IE_INVALID_CONTEXT;
5814 if (!service_name || *service_name == '\0' || !request || !*request ||
5815 !response)
5816 return IE_INVAL;
5818 /* Check if connection is established
5819 * TODO: This check should be done downstairs. */
5820 if (!context->curl) return IE_CONNECTION_CLOSED;
5822 service_name_locale = _isds_utf82locale((char*) service_name);
5823 if (!service_name_locale) {
5824 err = IE_NOMEM;
5825 goto leave;
5828 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5829 service_name_locale);
5831 /* Send request */
5832 err = _isds(context, service, *request, response, NULL, NULL);
5833 xmlFreeNode(*request); *request = NULL;
5835 if (err) {
5836 isds_log(ILF_ISDS, ILL_DEBUG,
5837 _("Processing ISDS response on %s request failed\n"),
5838 service_name_locale);
5839 goto leave;
5842 /* Check for response status */
5843 err = isds_response_status(context, service, *response,
5844 &code, &message, refnumber);
5845 if (err) {
5846 isds_log(ILF_ISDS, ILL_DEBUG,
5847 _("ISDS response on %s request is missing status\n"),
5848 service_name_locale);
5849 goto leave;
5852 err = statuscode2isds_error(context, map, code, message);
5854 /* Request processed, but server failed */
5855 if (xmlStrcmp(code, BAD_CAST "0000")) {
5856 char *code_locale = _isds_utf82locale((char*) code);
5857 char *message_locale = _isds_utf82locale((char*) message);
5858 isds_log(ILF_ISDS, ILL_DEBUG,
5859 _("Server refused %s request (code=%s, message=%s)\n"),
5860 service_name_locale, code_locale, message_locale);
5861 free(code_locale);
5862 free(message_locale);
5863 goto leave;
5867 leave:
5868 free(code);
5869 free(message);
5870 if (err && *response) {
5871 xmlFreeDoc(*response);
5872 *response = NULL;
5874 if (*request) {
5875 xmlFreeNode(*request);
5876 *request = NULL;
5878 free(service_name_locale);
5880 return err;
5884 /* Generic bottom half with request sending.
5885 * It sends prepared request, checks for error code, destroys response and
5886 * request and log success or failure.
5887 * @context is ISDS session context.
5888 * @service is ISDS service handler
5889 * @service_name is name in scope of given @service
5890 * @request is XML tree with request. Will be freed to save memory.
5891 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5892 * NULL, if you don't care. */
5893 static isds_error send_request_check_drop_response(
5894 struct isds_ctx *context,
5895 const isds_service service, const xmlChar *service_name,
5896 xmlNodePtr *request, xmlChar **refnumber) {
5897 isds_error err = IE_SUCCESS;
5898 xmlDocPtr response = NULL;
5901 if (!context) return IE_INVALID_CONTEXT;
5902 if (!service_name || *service_name == '\0' || !request || !*request)
5903 return IE_INVAL;
5905 /* Send request and check response*/
5906 err = send_destroy_request_check_response(context,
5907 service, service_name, request, &response, refnumber, NULL);
5909 xmlFreeDoc(response);
5911 if (*request) {
5912 xmlFreeNode(*request);
5913 *request = NULL;
5916 if (!err) {
5917 char *service_name_locale = _isds_utf82locale((char *) service_name);
5918 isds_log(ILF_ISDS, ILL_DEBUG,
5919 _("%s request processed by server successfully.\n"),
5920 service_name_locale);
5921 free(service_name_locale);
5924 return err;
5928 /* Insert isds_credentials_delivery structure into XML request if not NULL
5929 * @context is session context
5930 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5931 * credentials delivery. The email field is passed.
5932 * @parent is XML element where to insert */
5933 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5934 const struct isds_credentials_delivery *credentials_delivery,
5935 xmlNodePtr parent) {
5936 isds_error err = IE_SUCCESS;
5937 xmlNodePtr node;
5939 if (!context) return IE_INVALID_CONTEXT;
5940 if (!parent) return IE_INVAL;
5942 if (credentials_delivery) {
5943 /* Following elements are valid only for services:
5944 * NewAccessData, AddDataBoxUser, CreateDataBox */
5945 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5946 INSERT_STRING(parent, "email", credentials_delivery->email);
5949 leave:
5950 return err;
5954 /* Extract credentials delivery from ISDS response.
5955 * @context is session context
5956 * @credentials_delivery is pointer to valid structure to fill in returned
5957 * user's password (and new log-in name). If NULL, do not extract the data.
5958 * @response is pointer to XML document with ISDS response
5959 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5960 * @return IE_SUCCESS even if new user name has not been found because it's not
5961 * clear whether it's returned always. */
5962 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5963 struct isds_credentials_delivery *credentials_delivery,
5964 xmlDocPtr response, const char *request_name) {
5965 isds_error err = IE_SUCCESS;
5966 xmlXPathContextPtr xpath_ctx = NULL;
5967 xmlXPathObjectPtr result = NULL;
5968 char *xpath_query = NULL;
5970 if (!context) return IE_INVALID_CONTEXT;
5971 if (credentials_delivery) {
5972 zfree(credentials_delivery->token);
5973 zfree(credentials_delivery->new_user_name);
5975 if (!response || !request_name || !*request_name) return IE_INVAL;
5978 /* Extract optional token */
5979 if (credentials_delivery) {
5980 xpath_ctx = xmlXPathNewContext(response);
5981 if (!xpath_ctx) {
5982 err = IE_ERROR;
5983 goto leave;
5985 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5986 err = IE_ERROR;
5987 goto leave;
5990 /* Verify root element */
5991 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
5992 request_name)) {
5993 err = IE_NOMEM;
5994 goto leave;
5996 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
5997 if (!result) {
5998 err = IE_ERROR;
5999 goto leave;
6001 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6002 char *request_name_locale = _isds_utf82locale(request_name);
6003 isds_log(ILF_ISDS, ILL_WARNING,
6004 _("Wrong element in ISDS response for %s request "
6005 "while extracting credentials delivery details\n"),
6006 request_name_locale);
6007 free(request_name_locale);
6008 err = IE_ERROR;
6009 goto leave;
6011 xpath_ctx->node = result->nodesetval->nodeTab[0];
6014 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6015 * optional. */
6016 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
6018 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
6019 if (!credentials_delivery->token) {
6020 char *request_name_locale = _isds_utf82locale(request_name);
6021 isds_log(ILF_ISDS, ILL_ERR,
6022 _("ISDS did not return token on %s request "
6023 "even if requested\n"), request_name_locale);
6024 free(request_name_locale);
6025 err = IE_ERROR;
6029 leave:
6030 free(xpath_query);
6031 xmlXPathFreeObject(result);
6032 xmlXPathFreeContext(xpath_ctx);
6034 return err;
6038 /* Build XSD:tCreateDBInput request type for box creating.
6039 * @context is session context
6040 * @request outputs built XML tree
6041 * @service_name is request name of SERVICE_DB_MANIPULATION service
6042 * @box is box description to create including single primary user (in case of
6043 * FO box type)
6044 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6045 * box, or contact address of PFO box owner)
6046 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6047 * @upper_box_id is optional ID of supper box if currently created box is
6048 * subordinated.
6049 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6050 * don't care.
6051 * @credentials_delivery is valid pointer if ISDS should return token that box
6052 * owner can use to obtain his new credentials in on-line way. Then valid email
6053 * member value should be supplied.
6054 * @approval is optional external approval of box manipulation */
6055 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
6056 xmlNodePtr *request, const xmlChar *service_name,
6057 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6058 const xmlChar *former_names, const xmlChar *upper_box_id,
6059 const xmlChar *ceo_label,
6060 const struct isds_credentials_delivery *credentials_delivery,
6061 const struct isds_approval *approval) {
6062 isds_error err = IE_SUCCESS;
6063 xmlNsPtr isds_ns = NULL;
6064 xmlNodePtr node, dbPrimaryUsers;
6065 xmlChar *string = NULL;
6066 const struct isds_list *item;
6069 if (!context) return IE_INVALID_CONTEXT;
6070 if (!request || !service_name || service_name[0] == '\0' || !box)
6071 return IE_INVAL;
6074 /* Build CreateDataBox-similar request */
6075 *request = xmlNewNode(NULL, service_name);
6076 if (!*request) {
6077 char *service_name_locale = _isds_utf82locale((char*) service_name);
6078 isds_printf_message(context, _("Could build %s request"),
6079 service_name_locale);
6080 free(service_name_locale);
6081 return IE_ERROR;
6083 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
6084 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
6085 if (!isds_ns) {
6086 isds_log_message(context, _("Could not create ISDS1 name space"));
6087 xmlFreeNode(*request);
6088 return IE_ERROR;
6090 } else {
6091 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6092 if (!isds_ns) {
6093 isds_log_message(context, _("Could not create ISDS name space"));
6094 xmlFreeNode(*request);
6095 return IE_ERROR;
6098 xmlSetNs(*request, isds_ns);
6100 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6101 err = insert_DbOwnerInfo(context, box, node);
6102 if (err) goto leave;
6104 /* Insert users */
6105 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6106 * verbose documentation allows none dbUserInfo */
6107 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6108 for (item = users; item; item = item->next) {
6109 if (item->data) {
6110 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6111 err = insert_DbUserInfo(context,
6112 (struct isds_DbUserInfo *) item->data, node);
6113 if (err) goto leave;
6117 INSERT_STRING(*request, "dbFormerNames", former_names);
6118 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6119 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6121 err = insert_credentials_delivery(context, credentials_delivery, *request);
6122 if (err) goto leave;
6124 err = insert_GExtApproval(context, approval, *request);
6125 if (err) goto leave;
6127 leave:
6128 if (err) {
6129 xmlFreeNode(*request);
6130 *request = NULL;
6132 free(string);
6133 return err;
6135 #endif /* HAVE_LIBCURL */
6138 /* Create new box.
6139 * @context is session context
6140 * @box is box description to create including single primary user (in case of
6141 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6142 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6143 * box, or contact address of PFO box owner)
6144 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6145 * @upper_box_id is optional ID of supper box if currently created box is
6146 * subordinated.
6147 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6148 * @credentials_delivery is NULL if new password should be delivered off-line
6149 * to box owner. It is valid pointer if owner should obtain new password on-line
6150 * on dedicated web server. Then input @credentials_delivery.email value is
6151 * his e-mail address he must provide to dedicated web server together
6152 * with output reallocated @credentials_delivery.token member. Output
6153 * member @credentials_delivery.new_user_name is unused up on this call.
6154 * @approval is optional external approval of box manipulation
6155 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6156 * NULL, if you don't care.*/
6157 isds_error isds_add_box(struct isds_ctx *context,
6158 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6159 const char *former_names, const char *upper_box_id,
6160 const char *ceo_label,
6161 struct isds_credentials_delivery *credentials_delivery,
6162 const struct isds_approval *approval, char **refnumber) {
6163 isds_error err = IE_SUCCESS;
6164 #if HAVE_LIBCURL
6165 xmlNodePtr request = NULL;
6166 xmlDocPtr response = NULL;
6167 xmlXPathContextPtr xpath_ctx = NULL;
6168 xmlXPathObjectPtr result = NULL;
6169 #endif
6172 if (!context) return IE_INVALID_CONTEXT;
6173 zfree(context->long_message);
6174 if (credentials_delivery) {
6175 zfree(credentials_delivery->token);
6176 zfree(credentials_delivery->new_user_name);
6178 if (!box) return IE_INVAL;
6180 #if HAVE_LIBCURL
6181 /* Scratch box ID */
6182 zfree(box->dbID);
6184 /* Build CreateDataBox request */
6185 err = build_CreateDBInput_request(context,
6186 &request, BAD_CAST "CreateDataBox",
6187 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6188 (xmlChar *) ceo_label, credentials_delivery, approval);
6189 if (err) goto leave;
6191 /* Send it to server and process response */
6192 err = send_destroy_request_check_response(context,
6193 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6194 &response, (xmlChar **) refnumber, NULL);
6196 /* Extract box ID */
6197 xpath_ctx = xmlXPathNewContext(response);
6198 if (!xpath_ctx) {
6199 err = IE_ERROR;
6200 goto leave;
6202 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6203 err = IE_ERROR;
6204 goto leave;
6206 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6208 /* Extract optional token */
6209 err = extract_credentials_delivery(context, credentials_delivery, response,
6210 "CreateDataBox");
6212 leave:
6213 xmlXPathFreeObject(result);
6214 xmlXPathFreeContext(xpath_ctx);
6215 xmlFreeDoc(response);
6216 xmlFreeNode(request);
6218 if (!err) {
6219 isds_log(ILF_ISDS, ILL_DEBUG,
6220 _("CreateDataBox request processed by server successfully.\n"));
6222 #else /* not HAVE_LIBCURL */
6223 err = IE_NOTSUP;
6224 #endif
6226 return err;
6230 /* Notify ISDS about new PFO entity.
6231 * This function has no real effect.
6232 * @context is session context
6233 * @box is PFO description including single primary user.
6234 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6235 * @former_names is optional undocumented string. Pass NULL if you don't care.
6236 * @upper_box_id is optional ID of supper box if currently created box is
6237 * subordinated.
6238 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6239 * @approval is optional external approval of box manipulation
6240 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6241 * NULL, if you don't care.*/
6242 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6243 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6244 const char *former_names, const char *upper_box_id,
6245 const char *ceo_label, const struct isds_approval *approval,
6246 char **refnumber) {
6247 isds_error err = IE_SUCCESS;
6248 #if HAVE_LIBCURL
6249 xmlNodePtr request = NULL;
6250 #endif
6252 if (!context) return IE_INVALID_CONTEXT;
6253 zfree(context->long_message);
6254 if (!box) return IE_INVAL;
6256 #if HAVE_LIBCURL
6257 /* Build CreateDataBoxPFOInfo request */
6258 err = build_CreateDBInput_request(context,
6259 &request, BAD_CAST "CreateDataBoxPFOInfo",
6260 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6261 (xmlChar *) ceo_label, NULL, approval);
6262 if (err) goto leave;
6264 /* Send it to server and process response */
6265 err = send_request_check_drop_response(context,
6266 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6267 (xmlChar **) refnumber);
6268 /* XXX: XML Schema names output dbID element but textual documentation
6269 * states no box identifier is returned. */
6270 leave:
6271 xmlFreeNode(request);
6272 #else /* not HAVE_LIBCURL */
6273 err = IE_NOTSUP;
6274 #endif
6275 return err;
6279 /* Common implementation for removing given box.
6280 * @context is session context
6281 * @service_name is UTF-8 encoded name fo ISDS service
6282 * @box is box description to delete
6283 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6284 * carry sane value. If NULL, do not inject this information into request.
6285 * @approval is optional external approval of box manipulation
6286 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6287 * NULL, if you don't care.*/
6288 isds_error _isds_delete_box_common(struct isds_ctx *context,
6289 const xmlChar *service_name,
6290 const struct isds_DbOwnerInfo *box, const struct tm *since,
6291 const struct isds_approval *approval, char **refnumber) {
6292 isds_error err = IE_SUCCESS;
6293 #if HAVE_LIBCURL
6294 xmlNsPtr isds_ns = NULL;
6295 xmlNodePtr request = NULL;
6296 xmlNodePtr node;
6297 xmlChar *string = NULL;
6298 #endif
6301 if (!context) return IE_INVALID_CONTEXT;
6302 zfree(context->long_message);
6303 if (!service_name || !*service_name || !box) return IE_INVAL;
6306 #if HAVE_LIBCURL
6307 /* Build DeleteDataBox(Promptly) request */
6308 request = xmlNewNode(NULL, service_name);
6309 if (!request) {
6310 char *service_name_locale = _isds_utf82locale((char*)service_name);
6311 isds_printf_message(context,
6312 _("Could build %s request"), service_name_locale);
6313 free(service_name_locale);
6314 return IE_ERROR;
6316 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6317 if(!isds_ns) {
6318 isds_log_message(context, _("Could not create ISDS name space"));
6319 xmlFreeNode(request);
6320 return IE_ERROR;
6322 xmlSetNs(request, isds_ns);
6324 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6325 err = insert_DbOwnerInfo(context, box, node);
6326 if (err) goto leave;
6328 if (since) {
6329 err = tm2datestring(since, &string);
6330 if (err) {
6331 isds_log_message(context,
6332 _("Could not convert `since' argument to ISO date string"));
6333 goto leave;
6335 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6336 zfree(string);
6339 err = insert_GExtApproval(context, approval, request);
6340 if (err) goto leave;
6343 /* Send it to server and process response */
6344 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6345 service_name, &request, (xmlChar **) refnumber);
6347 leave:
6348 xmlFreeNode(request);
6349 free(string);
6350 #else /* not HAVE_LIBCURL */
6351 err = IE_NOTSUP;
6352 #endif
6353 return err;
6357 /* Remove given box permanently.
6358 * @context is session context
6359 * @box is box description to delete
6360 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6361 * carry sane value.
6362 * @approval is optional external approval of box manipulation
6363 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6364 * NULL, if you don't care.*/
6365 isds_error isds_delete_box(struct isds_ctx *context,
6366 const struct isds_DbOwnerInfo *box, const struct tm *since,
6367 const struct isds_approval *approval, char **refnumber) {
6368 if (!context) return IE_INVALID_CONTEXT;
6369 zfree(context->long_message);
6370 if (!box || !since) return IE_INVAL;
6372 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6373 box, since, approval, refnumber);
6377 /* Undocumented function.
6378 * @context is session context
6379 * @box is box description to delete
6380 * @approval is optional external approval of box manipulation
6381 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6382 * NULL, if you don't care.*/
6383 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6384 const struct isds_DbOwnerInfo *box,
6385 const struct isds_approval *approval, char **refnumber) {
6386 if (!context) return IE_INVALID_CONTEXT;
6387 zfree(context->long_message);
6388 if (!box) return IE_INVAL;
6390 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6391 box, NULL, approval, refnumber);
6395 /* Update data about given box.
6396 * @context is session context
6397 * @old_box current box description
6398 * @new_box are updated data about @old_box
6399 * @approval is optional external approval of box manipulation
6400 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6401 * NULL, if you don't care.*/
6402 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6403 const struct isds_DbOwnerInfo *old_box,
6404 const struct isds_DbOwnerInfo *new_box,
6405 const struct isds_approval *approval, char **refnumber) {
6406 isds_error err = IE_SUCCESS;
6407 #if HAVE_LIBCURL
6408 xmlNsPtr isds_ns = NULL;
6409 xmlNodePtr request = NULL;
6410 xmlNodePtr node;
6411 #endif
6414 if (!context) return IE_INVALID_CONTEXT;
6415 zfree(context->long_message);
6416 if (!old_box || !new_box) return IE_INVAL;
6419 #if HAVE_LIBCURL
6420 /* Build UpdateDataBoxDescr request */
6421 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6422 if (!request) {
6423 isds_log_message(context,
6424 _("Could build UpdateDataBoxDescr request"));
6425 return IE_ERROR;
6427 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6428 if(!isds_ns) {
6429 isds_log_message(context, _("Could not create ISDS name space"));
6430 xmlFreeNode(request);
6431 return IE_ERROR;
6433 xmlSetNs(request, isds_ns);
6435 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6436 err = insert_DbOwnerInfo(context, old_box, node);
6437 if (err) goto leave;
6439 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6440 err = insert_DbOwnerInfo(context, new_box, node);
6441 if (err) goto leave;
6443 err = insert_GExtApproval(context, approval, request);
6444 if (err) goto leave;
6447 /* Send it to server and process response */
6448 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6449 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6451 leave:
6452 xmlFreeNode(request);
6453 #else /* not HAVE_LIBCURL */
6454 err = IE_NOTSUP;
6455 #endif
6457 return err;
6461 #if HAVE_LIBCURL
6462 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6463 * code
6464 * @context is session context
6465 * @service is SOAP service
6466 * @service_name is name of request in @service
6467 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6468 * @box_id is box ID of interest
6469 * @approval is optional external approval of box manipulation
6470 * @response is server SOAP body response as XML document
6471 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6472 * NULL, if you don't care.
6473 * @return error coded from lower layer, context message will be set up
6474 * appropriately. */
6475 static isds_error build_send_dbid_request_check_response(
6476 struct isds_ctx *context, const isds_service service,
6477 const xmlChar *service_name, const xmlChar *box_id_element,
6478 const xmlChar *box_id, const struct isds_approval *approval,
6479 xmlDocPtr *response, xmlChar **refnumber) {
6481 isds_error err = IE_SUCCESS;
6482 char *service_name_locale = NULL, *box_id_locale = NULL;
6483 xmlNodePtr request = NULL, node;
6484 xmlNsPtr isds_ns = NULL;
6486 if (!context) return IE_INVALID_CONTEXT;
6487 if (!service_name || !box_id) return IE_INVAL;
6488 if (!response) return IE_INVAL;
6490 /* Free output argument */
6491 xmlFreeDoc(*response); *response = NULL;
6493 /* Prepare strings */
6494 service_name_locale = _isds_utf82locale((char*)service_name);
6495 if (!service_name_locale) {
6496 err = IE_NOMEM;
6497 goto leave;
6499 box_id_locale = _isds_utf82locale((char*)box_id);
6500 if (!box_id_locale) {
6501 err = IE_NOMEM;
6502 goto leave;
6505 /* Build request */
6506 request = xmlNewNode(NULL, service_name);
6507 if (!request) {
6508 isds_printf_message(context,
6509 _("Could not build %s request for %s box"), service_name_locale,
6510 box_id_locale);
6511 err = IE_ERROR;
6512 goto leave;
6514 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6515 if(!isds_ns) {
6516 isds_log_message(context, _("Could not create ISDS name space"));
6517 err = IE_ERROR;
6518 goto leave;
6520 xmlSetNs(request, isds_ns);
6522 /* Add XSD:tIdDbInput children */
6523 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6524 INSERT_STRING(request, box_id_element, box_id);
6525 err = insert_GExtApproval(context, approval, request);
6526 if (err) goto leave;
6528 /* Send request and check response*/
6529 err = send_destroy_request_check_response(context,
6530 service, service_name, &request, response, refnumber, NULL);
6532 leave:
6533 free(service_name_locale);
6534 free(box_id_locale);
6535 xmlFreeNode(request);
6536 return err;
6538 #endif /* HAVE_LIBCURL */
6541 /* Get data about all users assigned to given box.
6542 * @context is session context
6543 * @box_id is box ID
6544 * @users is automatically reallocated list of struct isds_DbUserInfo */
6545 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6546 struct isds_list **users) {
6547 isds_error err = IE_SUCCESS;
6548 #if HAVE_LIBCURL
6549 xmlDocPtr response = NULL;
6550 xmlXPathContextPtr xpath_ctx = NULL;
6551 xmlXPathObjectPtr result = NULL;
6552 int i;
6553 struct isds_list *item, *prev_item = NULL;
6554 #endif
6556 if (!context) return IE_INVALID_CONTEXT;
6557 zfree(context->long_message);
6558 if (!users || !box_id) return IE_INVAL;
6559 isds_list_free(users);
6562 #if HAVE_LIBCURL
6563 /* Do request and check for success */
6564 err = build_send_dbid_request_check_response(context,
6565 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6566 BAD_CAST box_id, NULL, &response, NULL);
6567 if (err) goto leave;
6570 /* Extract data */
6571 /* Prepare structure */
6572 xpath_ctx = xmlXPathNewContext(response);
6573 if (!xpath_ctx) {
6574 err = IE_ERROR;
6575 goto leave;
6577 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6578 err = IE_ERROR;
6579 goto leave;
6582 /* Set context node */
6583 result = xmlXPathEvalExpression(BAD_CAST
6584 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6585 xpath_ctx);
6586 if (!result) {
6587 err = IE_ERROR;
6588 goto leave;
6590 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6591 /* Iterate over all users */
6592 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6594 /* Prepare structure */
6595 item = calloc(1, sizeof(*item));
6596 if (!item) {
6597 err = IE_NOMEM;
6598 goto leave;
6600 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6601 if (i == 0) *users = item;
6602 else prev_item->next = item;
6603 prev_item = item;
6605 /* Extract it */
6606 xpath_ctx->node = result->nodesetval->nodeTab[i];
6607 err = extract_DbUserInfo(context,
6608 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6609 if (err) goto leave;
6613 leave:
6614 if (err) {
6615 isds_list_free(users);
6618 xmlXPathFreeObject(result);
6619 xmlXPathFreeContext(xpath_ctx);
6620 xmlFreeDoc(response);
6622 if (!err)
6623 isds_log(ILF_ISDS, ILL_DEBUG,
6624 _("GetDataBoxUsers request processed by server "
6625 "successfully.\n"));
6626 #else /* not HAVE_LIBCURL */
6627 err = IE_NOTSUP;
6628 #endif
6630 return err;
6634 /* Update data about user assigned to given box.
6635 * @context is session context
6636 * @box is box identification
6637 * @old_user identifies user to update
6638 * @new_user are updated data about @old_user
6639 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6640 * NULL, if you don't care.*/
6641 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6642 const struct isds_DbOwnerInfo *box,
6643 const struct isds_DbUserInfo *old_user,
6644 const struct isds_DbUserInfo *new_user,
6645 char **refnumber) {
6646 isds_error err = IE_SUCCESS;
6647 #if HAVE_LIBCURL
6648 xmlNsPtr isds_ns = NULL;
6649 xmlNodePtr request = NULL;
6650 xmlNodePtr node;
6651 #endif
6654 if (!context) return IE_INVALID_CONTEXT;
6655 zfree(context->long_message);
6656 if (!box || !old_user || !new_user) return IE_INVAL;
6659 #if HAVE_LIBCURL
6660 /* Build UpdateDataBoxUser request */
6661 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6662 if (!request) {
6663 isds_log_message(context,
6664 _("Could build UpdateDataBoxUser request"));
6665 return IE_ERROR;
6667 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6668 if(!isds_ns) {
6669 isds_log_message(context, _("Could not create ISDS name space"));
6670 xmlFreeNode(request);
6671 return IE_ERROR;
6673 xmlSetNs(request, isds_ns);
6675 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6676 err = insert_DbOwnerInfo(context, box, node);
6677 if (err) goto leave;
6679 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6680 err = insert_DbUserInfo(context, old_user, node);
6681 if (err) goto leave;
6683 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6684 err = insert_DbUserInfo(context, new_user, node);
6685 if (err) goto leave;
6687 /* Send it to server and process response */
6688 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6689 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6691 leave:
6692 xmlFreeNode(request);
6693 #else /* not HAVE_LIBCURL */
6694 err = IE_NOTSUP;
6695 #endif
6697 return err;
6701 /* Undocumented function.
6702 * @context is session context
6703 * @box_id is UTF-8 encoded box identifier
6704 * @token is UTF-8 encoded temporary password
6705 * @user_id outputs UTF-8 encoded reallocated user identifier
6706 * @password outpus UTF-8 encoded reallocated user password
6707 * Output arguments will be nulled in case of error */
6708 isds_error isds_activate(struct isds_ctx *context,
6709 const char *box_id, const char *token,
6710 char **user_id, char **password) {
6711 isds_error err = IE_SUCCESS;
6712 #if HAVE_LIBCURL
6713 xmlNsPtr isds_ns = NULL;
6714 xmlNodePtr request = NULL, node;
6715 xmlDocPtr response = NULL;
6716 xmlXPathContextPtr xpath_ctx = NULL;
6717 xmlXPathObjectPtr result = NULL;
6718 #endif
6721 if (!context) return IE_INVALID_CONTEXT;
6722 zfree(context->long_message);
6724 if (user_id) zfree(*user_id);
6725 if (password) zfree(*password);
6727 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6730 #if HAVE_LIBCURL
6731 /* Build Activate request */
6732 request = xmlNewNode(NULL, BAD_CAST "Activate");
6733 if (!request) {
6734 isds_log_message(context, _("Could build Activate request"));
6735 return IE_ERROR;
6737 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6738 if(!isds_ns) {
6739 isds_log_message(context, _("Could not create ISDS name space"));
6740 xmlFreeNode(request);
6741 return IE_ERROR;
6743 xmlSetNs(request, isds_ns);
6745 INSERT_STRING(request, "dbAccessDataId", token);
6746 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6747 INSERT_STRING(request, "dbID", box_id);
6750 /* Send request and check response*/
6751 err = send_destroy_request_check_response(context,
6752 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6753 &response, NULL, NULL);
6754 if (err) goto leave;
6757 /* Extract data */
6758 xpath_ctx = xmlXPathNewContext(response);
6759 if (!xpath_ctx) {
6760 err = IE_ERROR;
6761 goto leave;
6763 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6764 err = IE_ERROR;
6765 goto leave;
6767 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6768 xpath_ctx);
6769 if (!result) {
6770 err = IE_ERROR;
6771 goto leave;
6773 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6774 isds_log_message(context, _("Missing ActivateResponse element"));
6775 err = IE_ISDS;
6776 goto leave;
6778 if (result->nodesetval->nodeNr > 1) {
6779 isds_log_message(context, _("Multiple ActivateResponse element"));
6780 err = IE_ISDS;
6781 goto leave;
6783 xpath_ctx->node = result->nodesetval->nodeTab[0];
6784 xmlXPathFreeObject(result); result = NULL;
6786 EXTRACT_STRING("isds:userId", *user_id);
6787 if (!*user_id)
6788 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6789 "but did not return `userId' element.\n"));
6791 EXTRACT_STRING("isds:password", *password);
6792 if (!*password)
6793 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6794 "but did not return `password' element.\n"));
6796 leave:
6797 xmlXPathFreeObject(result);
6798 xmlXPathFreeContext(xpath_ctx);
6799 xmlFreeDoc(response);
6800 xmlFreeNode(request);
6802 if (!err)
6803 isds_log(ILF_ISDS, ILL_DEBUG,
6804 _("Activate request processed by server successfully.\n"));
6805 #else /* not HAVE_LIBCURL */
6806 err = IE_NOTSUP;
6807 #endif
6809 return err;
6813 /* Reset credentials of user assigned to given box.
6814 * @context is session context
6815 * @box is box identification
6816 * @user identifies user to reset password
6817 * @fee_paid is true if fee has been paid, false otherwise
6818 * @approval is optional external approval of box manipulation
6819 * @credentials_delivery is NULL if new password should be delivered off-line
6820 * to the user. It is valid pointer if user should obtain new password on-line
6821 * on dedicated web server. Then input @credentials_delivery.email value is
6822 * user's e-mail address user must provide to dedicated web server together
6823 * with @credentials_delivery.token. The output reallocated token user needs
6824 * to use to authorize on the web server to view his new password. Output
6825 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6826 * ISDS changed up on this call. (No reason why server could change the name
6827 * is known now.)
6828 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6829 * NULL, if you don't care.*/
6830 isds_error isds_reset_password(struct isds_ctx *context,
6831 const struct isds_DbOwnerInfo *box,
6832 const struct isds_DbUserInfo *user,
6833 const _Bool fee_paid, const struct isds_approval *approval,
6834 struct isds_credentials_delivery *credentials_delivery,
6835 char **refnumber) {
6836 isds_error err = IE_SUCCESS;
6837 #if HAVE_LIBCURL
6838 xmlNsPtr isds_ns = NULL;
6839 xmlNodePtr request = NULL, node;
6840 xmlDocPtr response = NULL;
6841 #endif
6844 if (!context) return IE_INVALID_CONTEXT;
6845 zfree(context->long_message);
6847 if (credentials_delivery) {
6848 zfree(credentials_delivery->token);
6849 zfree(credentials_delivery->new_user_name);
6851 if (!box || !user) return IE_INVAL;
6854 #if HAVE_LIBCURL
6855 /* Build NewAccessData request */
6856 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6857 if (!request) {
6858 isds_log_message(context,
6859 _("Could build NewAccessData request"));
6860 return IE_ERROR;
6862 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6863 if(!isds_ns) {
6864 isds_log_message(context, _("Could not create ISDS name space"));
6865 xmlFreeNode(request);
6866 return IE_ERROR;
6868 xmlSetNs(request, isds_ns);
6870 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6871 err = insert_DbOwnerInfo(context, box, node);
6872 if (err) goto leave;
6874 INSERT_ELEMENT(node, request, "dbUserInfo");
6875 err = insert_DbUserInfo(context, user, node);
6876 if (err) goto leave;
6878 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6880 err = insert_credentials_delivery(context, credentials_delivery, request);
6881 if (err) goto leave;
6883 err = insert_GExtApproval(context, approval, request);
6884 if (err) goto leave;
6886 /* Send request and check response*/
6887 err = send_destroy_request_check_response(context,
6888 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6889 &response, (xmlChar **) refnumber, NULL);
6890 if (err) goto leave;
6893 /* Extract optional token */
6894 err = extract_credentials_delivery(context, credentials_delivery,
6895 response, "NewAccessData");
6897 leave:
6898 xmlFreeDoc(response);
6899 xmlFreeNode(request);
6901 if (!err)
6902 isds_log(ILF_ISDS, ILL_DEBUG,
6903 _("NewAccessData request processed by server "
6904 "successfully.\n"));
6905 #else /* not HAVE_LIBCURL */
6906 err = IE_NOTSUP;
6907 #endif
6909 return err;
6913 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6914 * code, destroy response and log success.
6915 * @context is ISDS session context.
6916 * @service_name is name of SERVICE_DB_MANIPULATION service
6917 * @box is box identification
6918 * @user identifies user to remove
6919 * @credentials_delivery is NULL if new user's password should be delivered
6920 * off-line to the user. It is valid pointer if user should obtain new
6921 * password on-line on dedicated web server. Then input
6922 * @credentials_delivery.email value is user's e-mail address user must
6923 * provide to dedicated web server together with @credentials_delivery.token.
6924 * The output reallocated token user needs to use to authorize on the web
6925 * server to view his new password. Output reallocated
6926 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6927 * assingned or changed up on this call.
6928 * @approval is optional external approval of box manipulation
6929 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6930 * NULL, if you don't care. */
6931 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6932 struct isds_ctx *context, const xmlChar *service_name,
6933 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6934 struct isds_credentials_delivery *credentials_delivery,
6935 const struct isds_approval *approval, xmlChar **refnumber) {
6936 isds_error err = IE_SUCCESS;
6937 #if HAVE_LIBCURL
6938 xmlNsPtr isds_ns = NULL;
6939 xmlNodePtr request = NULL, node;
6940 xmlDocPtr response = NULL;
6941 #endif
6944 if (!context) return IE_INVALID_CONTEXT;
6945 zfree(context->long_message);
6946 if (credentials_delivery) {
6947 zfree(credentials_delivery->token);
6948 zfree(credentials_delivery->new_user_name);
6950 if (!service_name || service_name[0] == '\0' || !box || !user)
6951 return IE_INVAL;
6954 #if HAVE_LIBCURL
6955 /* Build NewAccessData or similar request */
6956 request = xmlNewNode(NULL, service_name);
6957 if (!request) {
6958 char *service_name_locale = _isds_utf82locale((char *) service_name);
6959 isds_printf_message(context, _("Could not build %s request"),
6960 service_name_locale);
6961 free(service_name_locale);
6962 return IE_ERROR;
6964 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6965 if(!isds_ns) {
6966 isds_log_message(context, _("Could not create ISDS name space"));
6967 xmlFreeNode(request);
6968 return IE_ERROR;
6970 xmlSetNs(request, isds_ns);
6972 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6973 err = insert_DbOwnerInfo(context, box, node);
6974 if (err) goto leave;
6976 INSERT_ELEMENT(node, request, "dbUserInfo");
6977 err = insert_DbUserInfo(context, user, node);
6978 if (err) goto leave;
6980 err = insert_credentials_delivery(context, credentials_delivery, request);
6981 if (err) goto leave;
6983 err = insert_GExtApproval(context, approval, request);
6984 if (err) goto leave;
6987 /* Send request and check response*/
6988 err = send_destroy_request_check_response(context,
6989 SERVICE_DB_MANIPULATION, service_name, &request, &response,
6990 refnumber, NULL);
6992 xmlFreeNode(request);
6993 request = NULL;
6995 /* Pick up credentials_delivery if requested */
6996 err = extract_credentials_delivery(context, credentials_delivery, response,
6997 (char *)service_name);
6999 leave:
7000 xmlFreeDoc(response);
7001 if (request) xmlFreeNode(request);
7003 if (!err) {
7004 char *service_name_locale = _isds_utf82locale((char *) service_name);
7005 isds_log(ILF_ISDS, ILL_DEBUG,
7006 _("%s request processed by server successfully.\n"),
7007 service_name_locale);
7008 free(service_name_locale);
7010 #else /* not HAVE_LIBCURL */
7011 err = IE_NOTSUP;
7012 #endif
7014 return err;
7018 /* Assign new user to given box.
7019 * @context is session context
7020 * @box is box identification
7021 * @user defines new user to add
7022 * @credentials_delivery is NULL if new user's password should be delivered
7023 * off-line to the user. It is valid pointer if user should obtain new
7024 * password on-line on dedicated web server. Then input
7025 * @credentials_delivery.email value is user's e-mail address user must
7026 * provide to dedicated web server together with @credentials_delivery.token.
7027 * The output reallocated token user needs to use to authorize on the web
7028 * server to view his new password. Output reallocated
7029 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7030 * assingned up on this call.
7031 * @approval is optional external approval of box manipulation
7032 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7033 * NULL, if you don't care.*/
7034 isds_error isds_add_user(struct isds_ctx *context,
7035 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7036 struct isds_credentials_delivery *credentials_delivery,
7037 const struct isds_approval *approval, char **refnumber) {
7038 return build_send_manipulationboxuser_request_check_drop_response(context,
7039 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
7040 approval, (xmlChar **) refnumber);
7044 /* Remove user assigned to given box.
7045 * @context is session context
7046 * @box is box identification
7047 * @user identifies user to remove
7048 * @approval is optional external approval of box manipulation
7049 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7050 * NULL, if you don't care.*/
7051 isds_error isds_delete_user(struct isds_ctx *context,
7052 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7053 const struct isds_approval *approval, char **refnumber) {
7054 return build_send_manipulationboxuser_request_check_drop_response(context,
7055 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
7056 (xmlChar **) refnumber);
7060 /* Get list of boxes in ZIP archive.
7061 * @context is session context
7062 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7063 * System recognizes following values currently: ALL (all boxes), UPG
7064 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
7065 * receiving commercial messages). This argument is a string because
7066 * specification states new values can appear in the future. Not all list
7067 * types are available to all users.
7068 * @buffer is automatically reallocated memory to store the list of boxes. The
7069 * list is zipped CSV file.
7070 * @buffer_length is size of @buffer data in bytes.
7071 * In case of error @buffer will be freed and @buffer_length will be
7072 * undefined.*/
7073 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7074 const char *list_identifier, void **buffer, size_t *buffer_length) {
7075 isds_error err = IE_SUCCESS;
7076 #if HAVE_LIBCURL
7077 xmlNsPtr isds_ns = NULL;
7078 xmlNodePtr request = NULL, node;
7079 xmlDocPtr response = NULL;
7080 xmlXPathContextPtr xpath_ctx = NULL;
7081 xmlXPathObjectPtr result = NULL;
7082 char *string = NULL;
7083 #endif
7086 if (!context) return IE_INVALID_CONTEXT;
7087 zfree(context->long_message);
7088 if (buffer) zfree(*buffer);
7089 if (!buffer || !buffer_length) return IE_INVAL;
7092 #if HAVE_LIBCURL
7093 /* Check if connection is established
7094 * TODO: This check should be done downstairs. */
7095 if (!context->curl) return IE_CONNECTION_CLOSED;
7098 /* Build AuthenticateMessage request */
7099 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7100 if (!request) {
7101 isds_log_message(context,
7102 _("Could not build GetDataBoxList request"));
7103 return IE_ERROR;
7105 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7106 if(!isds_ns) {
7107 isds_log_message(context, _("Could not create ISDS name space"));
7108 xmlFreeNode(request);
7109 return IE_ERROR;
7111 xmlSetNs(request, isds_ns);
7112 INSERT_STRING(request, "dblType", list_identifier);
7114 /* Send request to server and process response */
7115 err = send_destroy_request_check_response(context,
7116 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7117 &response, NULL, NULL);
7118 if (err) goto leave;
7121 /* Extract Base-64 encoded ZIP file */
7122 xpath_ctx = xmlXPathNewContext(response);
7123 if (!xpath_ctx) {
7124 err = IE_ERROR;
7125 goto leave;
7127 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7128 err = IE_ERROR;
7129 goto leave;
7131 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7133 /* Decode non-empty archive */
7134 if (string && string[0] != '\0') {
7135 *buffer_length = _isds_b64decode(string, buffer);
7136 if (*buffer_length == (size_t) -1) {
7137 isds_printf_message(context,
7138 _("Error while Base64-decoding box list archive"));
7139 err = IE_ERROR;
7140 goto leave;
7145 leave:
7146 free(string);
7147 xmlXPathFreeObject(result);
7148 xmlXPathFreeContext(xpath_ctx);
7149 xmlFreeDoc(response);
7150 xmlFreeNode(request);
7152 if (!err) {
7153 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7154 "processed by server successfully.\n"));
7156 #else /* not HAVE_LIBCURL */
7157 err = IE_NOTSUP;
7158 #endif
7160 return err;
7164 /* Find boxes suiting given criteria.
7165 * @criteria is filter. You should fill in at least some members.
7166 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7167 * possibly empty. Input NULL or valid old structure.
7168 * @return:
7169 * IE_SUCCESS if search succeeded, @boxes contains useful data
7170 * IE_NOEXIST if no such box exists, @boxes will be NULL
7171 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7172 * contains still valid data
7173 * other code if something bad happens. @boxes will be NULL. */
7174 isds_error isds_FindDataBox(struct isds_ctx *context,
7175 const struct isds_DbOwnerInfo *criteria,
7176 struct isds_list **boxes) {
7177 isds_error err = IE_SUCCESS;
7178 #if HAVE_LIBCURL
7179 _Bool truncated = 0;
7180 xmlNsPtr isds_ns = NULL;
7181 xmlNodePtr request = NULL;
7182 xmlDocPtr response = NULL;
7183 xmlChar *code = NULL, *message = NULL;
7184 xmlNodePtr db_owner_info;
7185 xmlXPathContextPtr xpath_ctx = NULL;
7186 xmlXPathObjectPtr result = NULL;
7187 xmlChar *string = NULL;
7188 #endif
7191 if (!context) return IE_INVALID_CONTEXT;
7192 zfree(context->long_message);
7193 if (!boxes) return IE_INVAL;
7194 isds_list_free(boxes);
7196 if (!criteria) {
7197 return IE_INVAL;
7200 #if HAVE_LIBCURL
7201 /* Check if connection is established
7202 * TODO: This check should be done downstairs. */
7203 if (!context->curl) return IE_CONNECTION_CLOSED;
7206 /* Build FindDataBox request */
7207 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
7208 if (!request) {
7209 isds_log_message(context,
7210 _("Could build FindDataBox request"));
7211 return IE_ERROR;
7213 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7214 if(!isds_ns) {
7215 isds_log_message(context, _("Could not create ISDS name space"));
7216 xmlFreeNode(request);
7217 return IE_ERROR;
7219 xmlSetNs(request, isds_ns);
7220 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7221 if (!db_owner_info) {
7222 isds_log_message(context, _("Could not add dbOwnerInfo child to "
7223 "FindDataBox element"));
7224 xmlFreeNode(request);
7225 return IE_ERROR;
7228 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
7229 if (err) goto leave;
7232 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
7234 /* Sent request */
7235 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7237 /* Destroy request */
7238 xmlFreeNode(request); request = NULL;
7240 if (err) {
7241 isds_log(ILF_ISDS, ILL_DEBUG,
7242 _("Processing ISDS response on FindDataBox "
7243 "request failed\n"));
7244 goto leave;
7247 /* Check for response status */
7248 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7249 &code, &message, NULL);
7250 if (err) {
7251 isds_log(ILF_ISDS, ILL_DEBUG,
7252 _("ISDS response on FindDataBox request is missing status\n"));
7253 goto leave;
7256 /* Request processed, but nothing found */
7257 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7258 !xmlStrcmp(code, BAD_CAST "5001")) {
7259 char *code_locale = _isds_utf82locale((char*)code);
7260 char *message_locale = _isds_utf82locale((char*)message);
7261 isds_log(ILF_ISDS, ILL_DEBUG,
7262 _("Server did not found any box on FindDataBox request "
7263 "(code=%s, message=%s)\n"), code_locale, message_locale);
7264 isds_log_message(context, message_locale);
7265 free(code_locale);
7266 free(message_locale);
7267 err = IE_NOEXIST;
7268 goto leave;
7271 /* Warning, not a error */
7272 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7273 char *code_locale = _isds_utf82locale((char*)code);
7274 char *message_locale = _isds_utf82locale((char*)message);
7275 isds_log(ILF_ISDS, ILL_DEBUG,
7276 _("Server truncated response on FindDataBox request "
7277 "(code=%s, message=%s)\n"), code_locale, message_locale);
7278 isds_log_message(context, message_locale);
7279 free(code_locale);
7280 free(message_locale);
7281 truncated = 1;
7284 /* Other error */
7285 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7286 char *code_locale = _isds_utf82locale((char*)code);
7287 char *message_locale = _isds_utf82locale((char*)message);
7288 isds_log(ILF_ISDS, ILL_DEBUG,
7289 _("Server refused FindDataBox request "
7290 "(code=%s, message=%s)\n"), code_locale, message_locale);
7291 isds_log_message(context, message_locale);
7292 free(code_locale);
7293 free(message_locale);
7294 err = IE_ISDS;
7295 goto leave;
7298 xpath_ctx = xmlXPathNewContext(response);
7299 if (!xpath_ctx) {
7300 err = IE_ERROR;
7301 goto leave;
7303 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7304 err = IE_ERROR;
7305 goto leave;
7308 /* Extract boxes if they present */
7309 result = xmlXPathEvalExpression(BAD_CAST
7310 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7311 xpath_ctx);
7312 if (!result) {
7313 err = IE_ERROR;
7314 goto leave;
7316 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7317 struct isds_list *item, *prev_item = NULL;
7318 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7319 item = calloc(1, sizeof(*item));
7320 if (!item) {
7321 err = IE_NOMEM;
7322 goto leave;
7325 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7326 if (i == 0) *boxes = item;
7327 else prev_item->next = item;
7328 prev_item = item;
7330 xpath_ctx->node = result->nodesetval->nodeTab[i];
7331 err = extract_DbOwnerInfo(context,
7332 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7333 if (err) goto leave;
7337 leave:
7338 if (err) {
7339 isds_list_free(boxes);
7340 } else {
7341 if (truncated) err = IE_2BIG;
7344 free(string);
7345 xmlFreeNode(request);
7346 xmlXPathFreeObject(result);
7347 xmlXPathFreeContext(xpath_ctx);
7349 free(code);
7350 free(message);
7351 xmlFreeDoc(response);
7353 if (!err)
7354 isds_log(ILF_ISDS, ILL_DEBUG,
7355 _("FindDataBox request processed by server successfully.\n"));
7356 #else /* not HAVE_LIBCURL */
7357 err = IE_NOTSUP;
7358 #endif
7360 return err;
7364 #if HAVE_LIBCURL
7365 /* Convert a string with match markers into a plain string with list of
7366 * pointers to the matches
7367 * @string is an UTF-8 encoded non-constant string with match markers
7368 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7369 * The markers will be removed from the string.
7370 * @starts is a reallocated list of static pointers into the @string pointing
7371 * to places where match start markers occured.
7372 * @ends is a reallocated list of static pointers into the @string pointing
7373 * to places where match end markers occured.
7374 * @return IE_SUCCESS in case of no failure. */
7375 static isds_error interpret_matches(xmlChar *string,
7376 struct isds_list **starts, struct isds_list **ends) {
7377 isds_error err = IE_SUCCESS;
7378 xmlChar *pointer, *destination, *source;
7379 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7381 isds_list_free(starts);
7382 isds_list_free(ends);
7383 if (NULL == starts || NULL == ends) return IE_INVAL;
7384 if (NULL == string) return IE_SUCCESS;
7386 for (pointer = string; *pointer != '\0';) {
7387 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7388 /* Remove the start marker */
7389 for (source = pointer + 14, destination = pointer;
7390 *source != '\0'; source++, destination++) {
7391 *destination = *source;
7393 *destination = '\0';
7394 /* Append the pointer into the list */
7395 item = calloc(1, sizeof(*item));
7396 if (!item) {
7397 err = IE_NOMEM;
7398 goto leave;
7400 item->destructor = (void (*)(void **))NULL;
7401 item->data = pointer;
7402 if (NULL == prev_start) *starts = item;
7403 else prev_start->next = item;
7404 prev_start = item;
7405 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7406 /* Remove the end marker */
7407 for (source = pointer + 12, destination = pointer;
7408 *source != '\0'; source++, destination++) {
7409 *destination = *source;
7411 *destination = '\0';
7412 /* Append the pointer into the list */
7413 item = calloc(1, sizeof(*item));
7414 if (!item) {
7415 err = IE_NOMEM;
7416 goto leave;
7418 item->destructor = (void (*)(void **))NULL;
7419 item->data = pointer;
7420 if (NULL == prev_end) *ends = item;
7421 else prev_end->next = item;
7422 prev_end = item;
7423 } else {
7424 pointer++;
7428 leave:
7429 if (err) {
7430 isds_list_free(starts);
7431 isds_list_free(ends);
7433 return err;
7437 /* Convert isds:dbResult XML tree into structure
7438 * @context is ISDS context.
7439 * @fulltext_result is automatically reallocated found box structure.
7440 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7441 * @collect_matches is true to interpret match markers.
7442 * In case of error @result will be freed. */
7443 static isds_error extract_dbResult(struct isds_ctx *context,
7444 struct isds_fulltext_result **fulltext_result,
7445 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7446 isds_error err = IE_SUCCESS;
7447 xmlXPathObjectPtr result = NULL;
7448 char *string = NULL;
7450 if (NULL == context) return IE_INVALID_CONTEXT;
7451 if (NULL == fulltext_result) return IE_INVAL;
7452 isds_fulltext_result_free(fulltext_result);
7453 if (!xpath_ctx) return IE_INVAL;
7456 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7457 if (NULL == *fulltext_result) {
7458 err = IE_NOMEM;
7459 goto leave;
7462 /* Extract data */
7463 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7465 EXTRACT_STRING("isds:dbType", string);
7466 if (NULL == string) {
7467 err = IE_ISDS;
7468 isds_log_message(context, _("Empty isds:dbType element"));
7469 goto leave;
7471 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7472 if (err) {
7473 if (err == IE_ENUM) {
7474 err = IE_ISDS;
7475 char *string_locale = _isds_utf82locale(string);
7476 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7477 string_locale);
7478 free(string_locale);
7480 goto leave;
7482 zfree(string);
7484 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7485 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7487 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7488 if (err) goto leave;
7490 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7491 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7492 (*fulltext_result)->dbEffectiveOVM);
7494 EXTRACT_STRING("isds:dbSendOptions", string);
7495 if (NULL == string) {
7496 err = IE_ISDS;
7497 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7498 goto leave;
7500 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7501 (*fulltext_result)->active = 1;
7502 (*fulltext_result)->public_sending = 1;
7503 (*fulltext_result)->commercial_sending = 0;
7504 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7505 (*fulltext_result)->active = 1;
7506 (*fulltext_result)->public_sending = 1;
7507 (*fulltext_result)->commercial_sending = 1;
7508 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7509 (*fulltext_result)->active = 1;
7510 (*fulltext_result)->public_sending = 0;
7511 (*fulltext_result)->commercial_sending = 1;
7512 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7513 (*fulltext_result)->active = 1;
7514 (*fulltext_result)->public_sending = 0;
7515 (*fulltext_result)->commercial_sending = 0;
7516 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7517 (*fulltext_result)->active = 0;
7518 (*fulltext_result)->public_sending = 0;
7519 (*fulltext_result)->commercial_sending = 0;
7520 } else {
7521 err = IE_ISDS;
7522 char *string_locale = _isds_utf82locale(string);
7523 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7524 string_locale);
7525 free(string_locale);
7526 goto leave;
7528 zfree(string);
7530 /* Interpret match marks */
7531 if (collect_matches) {
7532 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7533 &((*fulltext_result)->name_match_start),
7534 &((*fulltext_result)->name_match_end));
7535 if (err) goto leave;
7536 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7537 &((*fulltext_result)->address_match_start),
7538 &((*fulltext_result)->address_match_end));
7539 if (err) goto leave;
7542 leave:
7543 if (err) isds_fulltext_result_free(fulltext_result);
7544 free(string);
7545 xmlXPathFreeObject(result);
7546 return err;
7548 #endif /* HAVE_LIBCURL */
7551 /* Find boxes matching a given full-text criteria.
7552 * @context is a session context
7553 * @query is a non-empty string which consists of words to search
7554 * @target selects box attributes to search for @query words. Pass NULL if you
7555 * don't care.
7556 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7557 * to search in all box types. Pass NULL to let server to use default value
7558 * which is DBTYPE_SYSTEM.
7559 * @page_size defines count of boxes to constitute a response page. It counts
7560 * from zero. Pass NULL to let server to use a default value (50 now).
7561 * @page_number defines ordinar number of the response page to return. It
7562 * counts from zero. Pass NULL to let server to use a default value (0 now).
7563 * @track_matches points to true for marking @query words found in the box
7564 * attributes. It points to false for not marking. Pass NULL to let the server
7565 * to use default value (false now).
7566 * @total_matching_boxes outputs reallocated number of all boxes matching the
7567 * query. Will be pointer to NULL if server did not provide the value.
7568 * Pass NULL if you don't care.
7569 * @current_page_beginning outputs reallocated ordinar number of the first box
7570 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7571 * server did not provide the value. Pass NULL if you don't care.
7572 * @current_page_size outputs reallocated count of boxes in the this @boxes
7573 * page. It will be pointer to NULL if the server did not provide the value.
7574 * Pass NULL if you don't care.
7575 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7576 * is the last one, false if more boxes match, NULL if the server did not
7577 * provude the value. Pass NULL if you don't care.
7578 * @boxes outputs reallocated list of isds_fulltext_result structures,
7579 * possibly empty.
7580 * @return:
7581 * IE_SUCCESS if search succeeded
7582 * IE_2BIG if @page_size is too large
7583 * other code if something bad happens; output arguments will be NULL. */
7584 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7585 const char *query,
7586 const isds_fulltext_target *target,
7587 const isds_DbType *box_type,
7588 const unsigned long int *page_size,
7589 const unsigned long int *page_number,
7590 const _Bool *track_matches,
7591 unsigned long int **total_matching_boxes,
7592 unsigned long int **current_page_beginning,
7593 unsigned long int **current_page_size,
7594 _Bool **last_page,
7595 struct isds_list **boxes) {
7596 isds_error err = IE_SUCCESS;
7597 #if HAVE_LIBCURL
7598 xmlNsPtr isds_ns = NULL;
7599 xmlNodePtr request = NULL;
7600 xmlDocPtr response = NULL;
7601 xmlNodePtr node;
7602 xmlXPathContextPtr xpath_ctx = NULL;
7603 xmlXPathObjectPtr result = NULL;
7604 const xmlChar *static_string = NULL;
7605 xmlChar *string = NULL;
7607 const xmlChar *codes[] = {
7608 BAD_CAST "1004",
7609 BAD_CAST "1152",
7610 BAD_CAST "1153",
7611 BAD_CAST "1154",
7612 BAD_CAST "1155",
7613 BAD_CAST "1156",
7614 BAD_CAST "9002",
7615 NULL
7617 const char *meanings[] = {
7618 N_("You are not allowed to perform the search"),
7619 N_("The query string is empty"),
7620 N_("Searched box ID is malformed"),
7621 N_("Searched organization ID is malformed"),
7622 N_("Invalid input"),
7623 N_("Requested page size is too large"),
7624 N_("Search engine internal error")
7626 const isds_error errors[] = {
7627 IE_ISDS,
7628 IE_INVAL,
7629 IE_INVAL,
7630 IE_INVAL,
7631 IE_INVAL,
7632 IE_2BIG,
7633 IE_ISDS
7635 struct code_map_isds_error map = {
7636 .codes = codes,
7637 .meanings = meanings,
7638 .errors = errors
7640 #endif
7643 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7644 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7645 if (NULL != current_page_size) zfree(*current_page_size);
7646 if (NULL != last_page) zfree(*last_page);
7647 isds_list_free(boxes);
7649 if (NULL == context) return IE_INVALID_CONTEXT;
7650 zfree(context->long_message);
7652 if (NULL == boxes) return IE_INVAL;
7654 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
7655 isds_log_message(context, _("Query string must be non-empty"));
7656 return IE_INVAL;
7659 #if HAVE_LIBCURL
7660 /* Check if connection is established
7661 * TODO: This check should be done downstairs. */
7662 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7664 /* Build FindDataBox request */
7665 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
7666 if (NULL == request) {
7667 isds_log_message(context,
7668 _("Could not build ISDSSearch2 request"));
7669 return IE_ERROR;
7671 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7672 if(NULL == isds_ns) {
7673 isds_log_message(context, _("Could not create ISDS name space"));
7674 xmlFreeNode(request);
7675 return IE_ERROR;
7677 xmlSetNs(request, isds_ns);
7679 INSERT_STRING(request, "searchText", query);
7681 if (NULL != target) {
7682 static_string = isds_fulltext_target2string(*(target));
7683 if (NULL == static_string) {
7684 isds_printf_message(context, _("Invalid target value: %d"),
7685 *(target));
7686 err = IE_ENUM;
7687 goto leave;
7690 INSERT_STRING(request, "searchType", static_string);
7691 static_string = NULL;
7693 if (NULL != box_type) {
7694 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7695 if (DBTYPE_SYSTEM == *box_type) {
7696 static_string = BAD_CAST "ALL";
7697 } else {
7698 static_string = isds_DbType2string(*(box_type));
7699 if (NULL == static_string) {
7700 isds_printf_message(context, _("Invalid box type value: %d"),
7701 *(box_type));
7702 err = IE_ENUM;
7703 goto leave;
7707 INSERT_STRING(request, "searchScope", static_string);
7708 static_string = NULL;
7710 INSERT_ULONGINT(request, "page", page_number, string);
7711 INSERT_ULONGINT(request, "pageSize", page_size, string);
7712 INSERT_BOOLEAN(request, "highlighting", track_matches);
7714 /* Send request and check response */
7715 err = send_destroy_request_check_response(context,
7716 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
7717 &request, &response, NULL, &map);
7718 if (err) goto leave;
7720 /* Parse response */
7721 xpath_ctx = xmlXPathNewContext(response);
7722 if (NULL == xpath_ctx) {
7723 err = IE_ERROR;
7724 goto leave;
7726 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7727 err = IE_ERROR;
7728 goto leave;
7730 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
7731 xpath_ctx);
7732 if (!result) {
7733 err = IE_ERROR;
7734 goto leave;
7736 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7737 isds_log_message(context, _("Missing ISDSSearch2 element"));
7738 err = IE_ISDS;
7739 goto leave;
7741 if (result->nodesetval->nodeNr > 1) {
7742 isds_log_message(context, _("Multiple ISDSSearch2 element"));
7743 err = IE_ISDS;
7744 goto leave;
7746 xpath_ctx->node = result->nodesetval->nodeTab[0];
7747 xmlXPathFreeObject(result); result = NULL;
7750 /* Extract counters */
7751 if (NULL != total_matching_boxes) {
7752 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
7754 if (NULL != current_page_size) {
7755 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
7757 if (NULL != current_page_beginning) {
7758 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
7760 if (NULL != last_page) {
7761 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
7763 xmlXPathFreeObject(result); result = NULL;
7765 /* Extract boxes if they present */
7766 result = xmlXPathEvalExpression(BAD_CAST
7767 "isds:dbResults/isds:dbResult", xpath_ctx);
7768 if (NULL == result) {
7769 err = IE_ERROR;
7770 goto leave;
7772 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7773 struct isds_list *item, *prev_item = NULL;
7774 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7775 item = calloc(1, sizeof(*item));
7776 if (!item) {
7777 err = IE_NOMEM;
7778 goto leave;
7781 item->destructor = (void (*)(void **))isds_fulltext_result_free;
7782 if (i == 0) *boxes = item;
7783 else prev_item->next = item;
7784 prev_item = item;
7786 xpath_ctx->node = result->nodesetval->nodeTab[i];
7787 err = extract_dbResult(context,
7788 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
7789 (NULL == track_matches) ? 0 : *track_matches);
7790 if (err) goto leave;
7794 leave:
7795 if (err) {
7796 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7797 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7798 if (NULL != current_page_size) zfree(*current_page_size);
7799 if (NULL != last_page) zfree(*last_page);
7800 isds_list_free(boxes);
7803 free(string);
7804 xmlFreeNode(request);
7805 xmlXPathFreeObject(result);
7806 xmlXPathFreeContext(xpath_ctx);
7807 xmlFreeDoc(response);
7809 if (!err)
7810 isds_log(ILF_ISDS, ILL_DEBUG,
7811 _("ISDSSearch2 request processed by server successfully.\n"));
7812 #else /* not HAVE_LIBCURL */
7813 err = IE_NOTSUP;
7814 #endif
7816 return err;
7820 /* Get status of a box.
7821 * @context is ISDS session context.
7822 * @box_id is UTF-8 encoded box identifier as zero terminated string
7823 * @box_status is return value of box status.
7824 * @return:
7825 * IE_SUCCESS if box has been found and its status retrieved
7826 * IE_NOEXIST if box is not known to ISDS server
7827 * or other appropriate error.
7828 * You can use isds_DbState to enumerate box status. However out of enum
7829 * range value can be returned too. This is feature because ISDS
7830 * specification leaves the set of values open.
7831 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7832 * the box has been deleted, but ISDS still lists its former existence. */
7833 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7834 long int *box_status) {
7835 isds_error err = IE_SUCCESS;
7836 #if HAVE_LIBCURL
7837 xmlNsPtr isds_ns = NULL;
7838 xmlNodePtr request = NULL, db_id;
7839 xmlDocPtr response = NULL;
7840 xmlXPathContextPtr xpath_ctx = NULL;
7841 xmlXPathObjectPtr result = NULL;
7842 xmlChar *string = NULL;
7844 const xmlChar *codes[] = {
7845 BAD_CAST "5001",
7846 BAD_CAST "1007",
7847 BAD_CAST "2011",
7848 NULL
7850 const char *meanings[] = {
7851 "The box does not exist",
7852 "Box ID is malformed",
7853 "Box ID malformed",
7855 const isds_error errors[] = {
7856 IE_NOEXIST,
7857 IE_INVAL,
7858 IE_INVAL,
7860 struct code_map_isds_error map = {
7861 .codes = codes,
7862 .meanings = meanings,
7863 .errors = errors
7865 #endif
7867 if (!context) return IE_INVALID_CONTEXT;
7868 zfree(context->long_message);
7869 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7871 #if HAVE_LIBCURL
7872 /* Check if connection is established
7873 * TODO: This check should be done downstairs. */
7874 if (!context->curl) return IE_CONNECTION_CLOSED;
7877 /* Build CheckDataBox request */
7878 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7879 if (!request) {
7880 isds_log_message(context,
7881 _("Could build CheckDataBox request"));
7882 return IE_ERROR;
7884 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7885 if(!isds_ns) {
7886 isds_log_message(context, _("Could not create ISDS name space"));
7887 xmlFreeNode(request);
7888 return IE_ERROR;
7890 xmlSetNs(request, isds_ns);
7891 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7892 if (!db_id) {
7893 isds_log_message(context, _("Could not add dbID child to "
7894 "CheckDataBox element"));
7895 xmlFreeNode(request);
7896 return IE_ERROR;
7900 /* Send request and check response*/
7901 err = send_destroy_request_check_response(context,
7902 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7903 &request, &response, NULL, &map);
7904 if (err) goto leave;
7907 /* Extract data */
7908 xpath_ctx = xmlXPathNewContext(response);
7909 if (!xpath_ctx) {
7910 err = IE_ERROR;
7911 goto leave;
7913 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7914 err = IE_ERROR;
7915 goto leave;
7917 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7918 xpath_ctx);
7919 if (!result) {
7920 err = IE_ERROR;
7921 goto leave;
7923 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7924 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7925 err = IE_ISDS;
7926 goto leave;
7928 if (result->nodesetval->nodeNr > 1) {
7929 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7930 err = IE_ISDS;
7931 goto leave;
7933 xpath_ctx->node = result->nodesetval->nodeTab[0];
7934 xmlXPathFreeObject(result); result = NULL;
7936 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7939 leave:
7940 free(string);
7941 xmlXPathFreeObject(result);
7942 xmlXPathFreeContext(xpath_ctx);
7944 xmlFreeDoc(response);
7946 if (!err)
7947 isds_log(ILF_ISDS, ILL_DEBUG,
7948 _("CheckDataBox request processed by server successfully.\n"));
7949 #else /* not HAVE_LIBCURL */
7950 err = IE_NOTSUP;
7951 #endif
7953 return err;
7957 /* Get list of permissions to send commercial messages.
7958 * @context is ISDS session context.
7959 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7960 * @permissions is a reallocated list of permissions (struct
7961 * isds_commercial_permission*) to send commercial messages from @box_id. The
7962 * order of permissions is significant as the server applies the permissions
7963 * and associated pre-paid credits in the order. Empty list means no
7964 * permission.
7965 * @return:
7966 * IE_SUCCESS if the list has been obtained correctly,
7967 * or other appropriate error. */
7968 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7969 const char *box_id, struct isds_list **permissions) {
7970 isds_error err = IE_SUCCESS;
7971 #if HAVE_LIBCURL
7972 xmlDocPtr response = NULL;
7973 xmlXPathContextPtr xpath_ctx = NULL;
7974 xmlXPathObjectPtr result = NULL;
7975 #endif
7977 if (!context) return IE_INVALID_CONTEXT;
7978 zfree(context->long_message);
7979 if (NULL == permissions) return IE_INVAL;
7980 isds_list_free(permissions);
7981 if (NULL == box_id) return IE_INVAL;
7983 #if HAVE_LIBCURL
7984 /* Check if connection is established */
7985 if (!context->curl) return IE_CONNECTION_CLOSED;
7987 /* Do request and check for success */
7988 err = build_send_dbid_request_check_response(context,
7989 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7990 BAD_CAST box_id, NULL, &response, NULL);
7991 if (!err) {
7992 isds_log(ILF_ISDS, ILL_DEBUG,
7993 _("PDZInfo request processed by server successfully.\n"));
7996 /* Extract data */
7997 /* Prepare structure */
7998 xpath_ctx = xmlXPathNewContext(response);
7999 if (!xpath_ctx) {
8000 err = IE_ERROR;
8001 goto leave;
8003 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8004 err = IE_ERROR;
8005 goto leave;
8008 /* Set context node */
8009 result = xmlXPathEvalExpression(BAD_CAST
8010 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8011 xpath_ctx);
8012 if (!result) {
8013 err = IE_ERROR;
8014 goto leave;
8016 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8017 struct isds_list *prev_item = NULL;
8019 /* Iterate over all permission records */
8020 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8021 struct isds_list *item;
8023 /* Prepare structure */
8024 item = calloc(1, sizeof(*item));
8025 if (!item) {
8026 err = IE_NOMEM;
8027 goto leave;
8029 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8030 if (i == 0) *permissions = item;
8031 else prev_item->next = item;
8032 prev_item = item;
8034 /* Extract it */
8035 xpath_ctx->node = result->nodesetval->nodeTab[i];
8036 err = extract_DbPDZRecord(context,
8037 (struct isds_commercial_permission **) (&item->data),
8038 xpath_ctx);
8039 if (err) goto leave;
8043 leave:
8044 if (err) {
8045 isds_list_free(permissions);
8048 xmlXPathFreeObject(result);
8049 xmlXPathFreeContext(xpath_ctx);
8050 xmlFreeDoc(response);
8052 #else /* not HAVE_LIBCURL */
8053 err = IE_NOTSUP;
8054 #endif
8056 return err;
8060 /* Get details about credit for sending pre-paid commercial messages.
8061 * @context is ISDS session context.
8062 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8063 * @from_date is first day of credit history to return in @history. Only
8064 * tm_year, tm_mon and tm_mday carry sane value.
8065 * @to_date is last day of credit history to return in @history. Only
8066 * tm_year, tm_mon and tm_mday carry sane value.
8067 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8068 * if you don't care. This and all other credit values are integers in
8069 * hundredths of Czech Crowns.
8070 * @email outputs notification e-mail address where notifications about credit
8071 * are sent. This is automatically reallocated string. Pass NULL if you don't
8072 * care. It can return NULL if no address is defined.
8073 * @history outputs auto-reallocated list of pointers to struct
8074 * isds_credit_event. Events in closed interval @from_time to @to_time are
8075 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8076 * are sorted by time.
8077 * @return:
8078 * IE_SUCCESS if the credit details have been obtained correctly,
8079 * or other appropriate error. Please note that server allows to retrieve
8080 * only limited history of events. */
8081 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8082 const char *box_id,
8083 const struct tm *from_date, const struct tm *to_date,
8084 long int *credit, char **email, struct isds_list **history) {
8085 isds_error err = IE_SUCCESS;
8086 #if HAVE_LIBCURL
8087 char *box_id_locale = NULL;
8088 xmlNodePtr request = NULL, node;
8089 xmlNsPtr isds_ns = NULL;
8090 xmlChar *string = NULL;
8092 xmlDocPtr response = NULL;
8093 xmlXPathContextPtr xpath_ctx = NULL;
8094 xmlXPathObjectPtr result = NULL;
8096 const xmlChar *codes[] = {
8097 BAD_CAST "1004",
8098 BAD_CAST "2011",
8099 BAD_CAST "1093",
8100 BAD_CAST "1137",
8101 BAD_CAST "1058",
8102 NULL
8104 const char *meanings[] = {
8105 "Insufficient priviledges for the box",
8106 "The box does not exist",
8107 "Date is too long (history is not available after 15 months)",
8108 "Interval is too long (limit is 3 months)",
8109 "Invalid date"
8111 const isds_error errors[] = {
8112 IE_ISDS,
8113 IE_NOEXIST,
8114 IE_DATE,
8115 IE_DATE,
8116 IE_DATE,
8118 struct code_map_isds_error map = {
8119 .codes = codes,
8120 .meanings = meanings,
8121 .errors = errors
8123 #endif
8125 if (!context) return IE_INVALID_CONTEXT;
8126 zfree(context->long_message);
8128 /* Free output argument */
8129 if (NULL != credit) *credit = 0;
8130 if (NULL != email) zfree(*email);
8131 isds_list_free(history);
8133 if (NULL == box_id) return IE_INVAL;
8135 #if HAVE_LIBCURL
8136 /* Check if connection is established */
8137 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8139 box_id_locale = _isds_utf82locale((char*)box_id);
8140 if (NULL == box_id_locale) {
8141 err = IE_NOMEM;
8142 goto leave;
8145 /* Build request */
8146 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8147 if (NULL == request) {
8148 isds_printf_message(context,
8149 _("Could not build DataBoxCreditInfo request for %s box"),
8150 box_id_locale);
8151 err = IE_ERROR;
8152 goto leave;
8154 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8155 if(!isds_ns) {
8156 isds_log_message(context, _("Could not create ISDS name space"));
8157 err = IE_ERROR;
8158 goto leave;
8160 xmlSetNs(request, isds_ns);
8162 /* Add mandatory XSD:tIdDbInput child */
8163 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8164 /* Add mandatory dates elements with optional values */
8165 if (from_date) {
8166 err = tm2datestring(from_date, &string);
8167 if (err) {
8168 isds_log_message(context,
8169 _("Could not convert `from_date' argument to ISO date "
8170 "string"));
8171 goto leave;
8173 INSERT_STRING(request, "ciFromDate", string);
8174 zfree(string);
8175 } else {
8176 INSERT_STRING(request, "ciFromDate", NULL);
8178 if (to_date) {
8179 err = tm2datestring(to_date, &string);
8180 if (err) {
8181 isds_log_message(context,
8182 _("Could not convert `to_date' argument to ISO date "
8183 "string"));
8184 goto leave;
8186 INSERT_STRING(request, "ciTodate", string);
8187 zfree(string);
8188 } else {
8189 INSERT_STRING(request, "ciTodate", NULL);
8192 /* Send request and check response*/
8193 err = send_destroy_request_check_response(context,
8194 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8195 &request, &response, NULL, &map);
8196 if (err) goto leave;
8199 /* Extract data */
8200 /* Set context to the root */
8201 xpath_ctx = xmlXPathNewContext(response);
8202 if (!xpath_ctx) {
8203 err = IE_ERROR;
8204 goto leave;
8206 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8207 err = IE_ERROR;
8208 goto leave;
8210 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8211 xpath_ctx);
8212 if (!result) {
8213 err = IE_ERROR;
8214 goto leave;
8216 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8217 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8218 err = IE_ISDS;
8219 goto leave;
8221 if (result->nodesetval->nodeNr > 1) {
8222 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8223 err = IE_ISDS;
8224 goto leave;
8226 xpath_ctx->node = result->nodesetval->nodeTab[0];
8227 xmlXPathFreeObject(result); result = NULL;
8229 /* Extract common data */
8230 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8231 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8233 /* Extract records */
8234 if (NULL == history) goto leave;
8235 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8236 xpath_ctx);
8237 if (!result) {
8238 err = IE_ERROR;
8239 goto leave;
8241 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8242 struct isds_list *prev_item = NULL;
8244 /* Iterate over all records */
8245 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8246 struct isds_list *item;
8248 /* Prepare structure */
8249 item = calloc(1, sizeof(*item));
8250 if (!item) {
8251 err = IE_NOMEM;
8252 goto leave;
8254 item->destructor = (void(*)(void**))isds_credit_event_free;
8255 if (i == 0) *history = item;
8256 else prev_item->next = item;
8257 prev_item = item;
8259 /* Extract it */
8260 xpath_ctx->node = result->nodesetval->nodeTab[i];
8261 err = extract_CiRecord(context,
8262 (struct isds_credit_event **) (&item->data),
8263 xpath_ctx);
8264 if (err) goto leave;
8268 leave:
8269 if (!err) {
8270 isds_log(ILF_ISDS, ILL_DEBUG,
8271 _("DataBoxCreditInfo request processed by server successfully.\n"));
8273 if (err) {
8274 isds_list_free(history);
8275 if (NULL != email) zfree(*email)
8278 free(box_id_locale);
8279 xmlXPathFreeObject(result);
8280 xmlXPathFreeContext(xpath_ctx);
8281 xmlFreeDoc(response);
8283 #else /* not HAVE_LIBCURL */
8284 err = IE_NOTSUP;
8285 #endif
8287 return err;
8291 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8292 * code, destroy response and log success.
8293 * @context is ISDS session context.
8294 * @service_name is name of SERVICE_DB_MANIPULATION service
8295 * @box_id is UTF-8 encoded box identifier as zero terminated string
8296 * @approval is optional external approval of box manipulation
8297 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8298 * NULL, if you don't care. */
8299 static isds_error build_send_manipulationdbid_request_check_drop_response(
8300 struct isds_ctx *context, const xmlChar *service_name,
8301 const xmlChar *box_id, const struct isds_approval *approval,
8302 xmlChar **refnumber) {
8303 isds_error err = IE_SUCCESS;
8304 #if HAVE_LIBCURL
8305 xmlDocPtr response = NULL;
8306 #endif
8308 if (!context) return IE_INVALID_CONTEXT;
8309 zfree(context->long_message);
8310 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8312 #if HAVE_LIBCURL
8313 /* Check if connection is established */
8314 if (!context->curl) return IE_CONNECTION_CLOSED;
8316 /* Do request and check for success */
8317 err = build_send_dbid_request_check_response(context,
8318 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8319 &response, refnumber);
8320 xmlFreeDoc(response);
8322 if (!err) {
8323 char *service_name_locale = _isds_utf82locale((char *) service_name);
8324 isds_log(ILF_ISDS, ILL_DEBUG,
8325 _("%s request processed by server successfully.\n"),
8326 service_name_locale);
8327 free(service_name_locale);
8329 #else /* not HAVE_LIBCURL */
8330 err = IE_NOTSUP;
8331 #endif
8333 return err;
8337 /* Switch box into state where box can receive commercial messages (off by
8338 * default)
8339 * @context is ISDS session context.
8340 * @box_id is UTF-8 encoded box identifier as zero terminated string
8341 * @allow is true for enable, false for disable commercial messages income
8342 * @approval is optional external approval of box manipulation
8343 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8344 * NULL, if you don't care. */
8345 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8346 const char *box_id, const _Bool allow,
8347 const struct isds_approval *approval, char **refnumber) {
8348 return build_send_manipulationdbid_request_check_drop_response(context,
8349 (allow) ? BAD_CAST "SetOpenAddressing" :
8350 BAD_CAST "ClearOpenAddressing",
8351 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8355 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8356 * message acceptance). This is just a box permission. Sender must apply
8357 * such role by sending each message.
8358 * @context is ISDS session context.
8359 * @box_id is UTF-8 encoded box identifier as zero terminated string
8360 * @allow is true for enable, false for disable OVM role permission
8361 * @approval is optional external approval of box manipulation
8362 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8363 * NULL, if you don't care. */
8364 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
8365 const char *box_id, const _Bool allow,
8366 const struct isds_approval *approval, char **refnumber) {
8367 return build_send_manipulationdbid_request_check_drop_response(context,
8368 (allow) ? BAD_CAST "SetEffectiveOVM" :
8369 BAD_CAST "ClearEffectiveOVM",
8370 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8374 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8375 * code, destroy response and log success.
8376 * @context is ISDS session context.
8377 * @service_name is name of SERVICE_DB_MANIPULATION service
8378 * @owner is structure describing box
8379 * @approval is optional external approval of box manipulation
8380 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8381 * NULL, if you don't care. */
8382 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8383 struct isds_ctx *context, const xmlChar *service_name,
8384 const struct isds_DbOwnerInfo *owner,
8385 const struct isds_approval *approval, xmlChar **refnumber) {
8386 isds_error err = IE_SUCCESS;
8387 #if HAVE_LIBCURL
8388 char *service_name_locale = NULL;
8389 xmlNodePtr request = NULL, db_owner_info;
8390 xmlNsPtr isds_ns = NULL;
8391 #endif
8394 if (!context) return IE_INVALID_CONTEXT;
8395 zfree(context->long_message);
8396 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8398 #if HAVE_LIBCURL
8399 service_name_locale = _isds_utf82locale((char*)service_name);
8400 if (!service_name_locale) {
8401 err = IE_NOMEM;
8402 goto leave;
8405 /* Build request */
8406 request = xmlNewNode(NULL, service_name);
8407 if (!request) {
8408 isds_printf_message(context,
8409 _("Could not build %s request"), service_name_locale);
8410 err = IE_ERROR;
8411 goto leave;
8413 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8414 if(!isds_ns) {
8415 isds_log_message(context, _("Could not create ISDS name space"));
8416 err = IE_ERROR;
8417 goto leave;
8419 xmlSetNs(request, isds_ns);
8422 /* Add XSD:tOwnerInfoInput child*/
8423 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8424 err = insert_DbOwnerInfo(context, owner, db_owner_info);
8425 if (err) goto leave;
8427 /* Add XSD:gExtApproval*/
8428 err = insert_GExtApproval(context, approval, request);
8429 if (err) goto leave;
8431 /* Send it to server and process response */
8432 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8433 service_name, &request, refnumber);
8435 leave:
8436 xmlFreeNode(request);
8437 free(service_name_locale);
8438 #else /* not HAVE_LIBCURL */
8439 err = IE_NOTSUP;
8440 #endif
8442 return err;
8446 /* Switch box accessibility state on request of box owner.
8447 * Despite the name, owner must do the request off-line. This function is
8448 * designed for such off-line meeting points (e.g. Czech POINT).
8449 * @context is ISDS session context.
8450 * @box identifies box to switch accessibility state.
8451 * @allow is true for making accessible, false to disallow access.
8452 * @approval is optional external approval of box manipulation
8453 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8454 * NULL, if you don't care. */
8455 isds_error isds_switch_box_accessibility_on_owner_request(
8456 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8457 const _Bool allow, const struct isds_approval *approval,
8458 char **refnumber) {
8459 return build_send_manipulationdbowner_request_check_drop_response(context,
8460 (allow) ? BAD_CAST "EnableOwnDataBox" :
8461 BAD_CAST "DisableOwnDataBox",
8462 box, approval, (xmlChar **) refnumber);
8466 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8467 * date.
8468 * @context is ISDS session context.
8469 * @box identifies box to switch accessibility state.
8470 * @since is date since accessibility has been denied. This can be past too.
8471 * Only tm_year, tm_mon and tm_mday carry sane value.
8472 * @approval is optional external approval of box manipulation
8473 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8474 * NULL, if you don't care. */
8475 isds_error isds_disable_box_accessibility_externaly(
8476 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8477 const struct tm *since, const struct isds_approval *approval,
8478 char **refnumber) {
8479 isds_error err = IE_SUCCESS;
8480 #if HAVE_LIBCURL
8481 char *service_name_locale = NULL;
8482 xmlNodePtr request = NULL, node;
8483 xmlNsPtr isds_ns = NULL;
8484 xmlChar *string = NULL;
8485 #endif
8488 if (!context) return IE_INVALID_CONTEXT;
8489 zfree(context->long_message);
8490 if (!box || !since) return IE_INVAL;
8492 #if HAVE_LIBCURL
8493 /* Build request */
8494 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
8495 if (!request) {
8496 isds_printf_message(context,
8497 _("Could not build %s request"), "DisableDataBoxExternally");
8498 err = IE_ERROR;
8499 goto leave;
8501 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8502 if(!isds_ns) {
8503 isds_log_message(context, _("Could not create ISDS name space"));
8504 err = IE_ERROR;
8505 goto leave;
8507 xmlSetNs(request, isds_ns);
8510 /* Add @box identification */
8511 INSERT_ELEMENT(node, request, "dbOwnerInfo");
8512 err = insert_DbOwnerInfo(context, box, node);
8513 if (err) goto leave;
8515 /* Add @since date */
8516 err = tm2datestring(since, &string);
8517 if(err) {
8518 isds_log_message(context,
8519 _("Could not convert `since' argument to ISO date string"));
8520 goto leave;
8522 INSERT_STRING(request, "dbOwnerDisableDate", string);
8523 zfree(string);
8525 /* Add @approval */
8526 err = insert_GExtApproval(context, approval, request);
8527 if (err) goto leave;
8529 /* Send it to server and process response */
8530 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8531 BAD_CAST "DisableDataBoxExternally", &request,
8532 (xmlChar **) refnumber);
8534 leave:
8535 free(string);
8536 xmlFreeNode(request);
8537 free(service_name_locale);
8538 #else /* not HAVE_LIBCURL */
8539 err = IE_NOTSUP;
8540 #endif
8542 return err;
8546 #if HAVE_LIBCURL
8547 /* Insert struct isds_message data (envelope (recipient data optional) and
8548 * documents into XML tree
8549 * @context is session context
8550 * @outgoing_message is libisds structure with message data
8551 * @create_message is XML CreateMessage or CreateMultipleMessage element
8552 * @process_recipient true for recipient data serialization, false for no
8553 * serialization */
8554 static isds_error insert_envelope_files(struct isds_ctx *context,
8555 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8556 const _Bool process_recipient) {
8558 isds_error err = IE_SUCCESS;
8559 xmlNodePtr envelope, dm_files, node;
8560 xmlChar *string = NULL;
8562 if (!context) return IE_INVALID_CONTEXT;
8563 if (!outgoing_message || !create_message) return IE_INVAL;
8566 /* Build envelope */
8567 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8568 if (!envelope) {
8569 isds_printf_message(context, _("Could not add dmEnvelope child to "
8570 "%s element"), create_message->name);
8571 return IE_ERROR;
8574 if (!outgoing_message->envelope) {
8575 isds_log_message(context, _("Outgoing message is missing envelope"));
8576 err = IE_INVAL;
8577 goto leave;
8580 /* Insert optional message type */
8581 err = insert_message_type(context, outgoing_message->envelope->dmType,
8582 envelope);
8583 if (err) goto leave;
8585 INSERT_STRING(envelope, "dmSenderOrgUnit",
8586 outgoing_message->envelope->dmSenderOrgUnit);
8587 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8588 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8590 if (process_recipient) {
8591 if (!outgoing_message->envelope->dbIDRecipient) {
8592 isds_log_message(context,
8593 _("Outgoing message is missing recipient box identifier"));
8594 err = IE_INVAL;
8595 goto leave;
8597 INSERT_STRING(envelope, "dbIDRecipient",
8598 outgoing_message->envelope->dbIDRecipient);
8600 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8601 outgoing_message->envelope->dmRecipientOrgUnit);
8602 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8603 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8604 INSERT_STRING(envelope, "dmToHands",
8605 outgoing_message->envelope->dmToHands);
8608 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8609 "dmAnnotation");
8610 INSERT_STRING(envelope, "dmAnnotation",
8611 outgoing_message->envelope->dmAnnotation);
8613 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8614 0, 50, "dmRecipientRefNumber");
8615 INSERT_STRING(envelope, "dmRecipientRefNumber",
8616 outgoing_message->envelope->dmRecipientRefNumber);
8618 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8619 0, 50, "dmSenderRefNumber");
8620 INSERT_STRING(envelope, "dmSenderRefNumber",
8621 outgoing_message->envelope->dmSenderRefNumber);
8623 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8624 0, 50, "dmRecipientIdent");
8625 INSERT_STRING(envelope, "dmRecipientIdent",
8626 outgoing_message->envelope->dmRecipientIdent);
8628 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8629 0, 50, "dmSenderIdent");
8630 INSERT_STRING(envelope, "dmSenderIdent",
8631 outgoing_message->envelope->dmSenderIdent);
8633 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8634 outgoing_message->envelope->dmLegalTitleLaw, string);
8635 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8636 outgoing_message->envelope->dmLegalTitleYear, string);
8637 INSERT_STRING(envelope, "dmLegalTitleSect",
8638 outgoing_message->envelope->dmLegalTitleSect);
8639 INSERT_STRING(envelope, "dmLegalTitlePar",
8640 outgoing_message->envelope->dmLegalTitlePar);
8641 INSERT_STRING(envelope, "dmLegalTitlePoint",
8642 outgoing_message->envelope->dmLegalTitlePoint);
8644 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8645 outgoing_message->envelope->dmPersonalDelivery);
8646 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8647 outgoing_message->envelope->dmAllowSubstDelivery);
8649 /* ???: Should we require value for dbEffectiveOVM sender?
8650 * ISDS has default as true */
8651 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8652 INSERT_BOOLEAN(envelope, "dmPublishOwnID",
8653 outgoing_message->envelope->dmPublishOwnID);
8656 /* Append dmFiles */
8657 if (!outgoing_message->documents) {
8658 isds_log_message(context,
8659 _("Outgoing message is missing list of documents"));
8660 err = IE_INVAL;
8661 goto leave;
8663 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8664 if (!dm_files) {
8665 isds_printf_message(context, _("Could not add dmFiles child to "
8666 "%s element"), create_message->name);
8667 err = IE_ERROR;
8668 goto leave;
8671 /* Check for document hierarchy */
8672 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8673 if (err) goto leave;
8675 /* Process each document */
8676 for (struct isds_list *item =
8677 (struct isds_list *) outgoing_message->documents;
8678 item; item = item->next) {
8679 if (!item->data) {
8680 isds_log_message(context,
8681 _("List of documents contains empty item"));
8682 err = IE_INVAL;
8683 goto leave;
8685 /* FIXME: Check for dmFileMetaType and for document references.
8686 * Only first document can be of MAIN type */
8687 err = insert_document(context, (struct isds_document*) item->data,
8688 dm_files);
8690 if (err) goto leave;
8693 leave:
8694 free(string);
8695 return err;
8697 #endif /* HAVE_LIBCURL */
8700 /* Send a message via ISDS to a recipient
8701 * @context is session context
8702 * @outgoing_message is message to send; Some members are mandatory (like
8703 * dbIDRecipient), some are optional and some are irrelevant (especially data
8704 * about sender). Included pointer to isds_list documents must contain at
8705 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8706 * members will be filled with valid data from ISDS. Exact list of write
8707 * members is subject to change. Currently dmID is changed.
8708 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8709 isds_error isds_send_message(struct isds_ctx *context,
8710 struct isds_message *outgoing_message) {
8712 isds_error err = IE_SUCCESS;
8713 #if HAVE_LIBCURL
8714 xmlNsPtr isds_ns = NULL;
8715 xmlNodePtr request = NULL;
8716 xmlDocPtr response = NULL;
8717 xmlChar *code = NULL, *message = NULL;
8718 xmlXPathContextPtr xpath_ctx = NULL;
8719 xmlXPathObjectPtr result = NULL;
8720 /*_Bool message_is_complete = 0;*/
8721 #endif
8723 if (!context) return IE_INVALID_CONTEXT;
8724 zfree(context->long_message);
8725 if (!outgoing_message) return IE_INVAL;
8727 #if HAVE_LIBCURL
8728 /* Check if connection is established
8729 * TODO: This check should be done downstairs. */
8730 if (!context->curl) return IE_CONNECTION_CLOSED;
8733 /* Build CreateMessage request */
8734 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8735 if (!request) {
8736 isds_log_message(context,
8737 _("Could not build CreateMessage request"));
8738 return IE_ERROR;
8740 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8741 if(!isds_ns) {
8742 isds_log_message(context, _("Could not create ISDS name space"));
8743 xmlFreeNode(request);
8744 return IE_ERROR;
8746 xmlSetNs(request, isds_ns);
8748 /* Append envelope and files */
8749 err = insert_envelope_files(context, outgoing_message, request, 1);
8750 if (err) goto leave;
8753 /* Signal we can serialize message since now */
8754 /*message_is_complete = 1;*/
8757 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8759 /* Sent request */
8760 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8762 /* Don't' destroy request, we want to provide it to application later */
8764 if (err) {
8765 isds_log(ILF_ISDS, ILL_DEBUG,
8766 _("Processing ISDS response on CreateMessage "
8767 "request failed\n"));
8768 goto leave;
8771 /* Check for response status */
8772 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8773 &code, &message, NULL);
8774 if (err) {
8775 isds_log(ILF_ISDS, ILL_DEBUG,
8776 _("ISDS response on CreateMessage request "
8777 "is missing status\n"));
8778 goto leave;
8781 /* Request processed, but refused by server or server failed */
8782 if (xmlStrcmp(code, BAD_CAST "0000")) {
8783 char *box_id_locale =
8784 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8785 char *code_locale = _isds_utf82locale((char*)code);
8786 char *message_locale = _isds_utf82locale((char*)message);
8787 isds_log(ILF_ISDS, ILL_DEBUG,
8788 _("Server did not accept message for %s on CreateMessage "
8789 "request (code=%s, message=%s)\n"),
8790 box_id_locale, code_locale, message_locale);
8791 isds_log_message(context, message_locale);
8792 free(box_id_locale);
8793 free(code_locale);
8794 free(message_locale);
8795 err = IE_ISDS;
8796 goto leave;
8800 /* Extract data */
8801 xpath_ctx = xmlXPathNewContext(response);
8802 if (!xpath_ctx) {
8803 err = IE_ERROR;
8804 goto leave;
8806 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8807 err = IE_ERROR;
8808 goto leave;
8810 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8811 xpath_ctx);
8812 if (!result) {
8813 err = IE_ERROR;
8814 goto leave;
8816 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8817 isds_log_message(context, _("Missing CreateMessageResponse element"));
8818 err = IE_ISDS;
8819 goto leave;
8821 if (result->nodesetval->nodeNr > 1) {
8822 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8823 err = IE_ISDS;
8824 goto leave;
8826 xpath_ctx->node = result->nodesetval->nodeTab[0];
8827 xmlXPathFreeObject(result); result = NULL;
8829 if (outgoing_message->envelope->dmID) {
8830 free(outgoing_message->envelope->dmID);
8831 outgoing_message->envelope->dmID = NULL;
8833 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8834 if (!outgoing_message->envelope->dmID) {
8835 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8836 "but did not return assigned message ID\n"));
8839 leave:
8840 /* TODO: Serialize message into structure member raw */
8841 /* XXX: Each web service transport message in different format.
8842 * Therefore it's not possible to save them directly.
8843 * To save them, one must figure out common format.
8844 * We can leave it on application, or we can implement the ESS format. */
8845 /*if (message_is_complete) {
8846 if (outgoing_message->envelope->dmID) {
8848 /* Add assigned message ID as first child*/
8849 /*xmlNodePtr dmid_text = xmlNewText(
8850 (xmlChar *) outgoing_message->envelope->dmID);
8851 if (!dmid_text) goto serialization_failed;
8853 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8854 BAD_CAST "dmID");
8855 if (!dmid_element) {
8856 xmlFreeNode(dmid_text);
8857 goto serialization_failed;
8860 xmlNodePtr dmid_element_with_text =
8861 xmlAddChild(dmid_element, dmid_text);
8862 if (!dmid_element_with_text) {
8863 xmlFreeNode(dmid_element);
8864 xmlFreeNode(dmid_text);
8865 goto serialization_failed;
8868 node = xmlAddPrevSibling(envelope->childern,
8869 dmid_element_with_text);
8870 if (!node) {
8871 xmlFreeNodeList(dmid_element_with_text);
8872 goto serialization_failed;
8876 /* Serialize message with ID into raw */
8877 /*buffer = serialize_element(envelope)*/
8878 /* }
8880 serialization_failed:
8884 /* Clean up */
8885 xmlXPathFreeObject(result);
8886 xmlXPathFreeContext(xpath_ctx);
8888 free(code);
8889 free(message);
8890 xmlFreeDoc(response);
8891 xmlFreeNode(request);
8893 if (!err)
8894 isds_log(ILF_ISDS, ILL_DEBUG,
8895 _("CreateMessage request processed by server "
8896 "successfully.\n"));
8897 #else /* not HAVE_LIBCURL */
8898 err = IE_NOTSUP;
8899 #endif
8901 return err;
8905 /* Send a message via ISDS to a multiple recipients
8906 * @context is session context
8907 * @outgoing_message is message to send; Some members are mandatory,
8908 * some are optional and some are irrelevant (especially data
8909 * about sender). Data about recipient will be substituted by ISDS from
8910 * @copies. Included pointer to isds_list documents must
8911 * contain at least one document of FILEMETATYPE_MAIN.
8912 * @copies is list of isds_message_copy structures addressing all desired
8913 * recipients. This is read-write structure, some members will be filled with
8914 * valid data from ISDS (message IDs, error codes, error descriptions).
8915 * @return
8916 * ISDS_SUCCESS if all messages have been sent
8917 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8918 * succeeded messages can be identified by copies->data->error),
8919 * or other error code if something other goes wrong. */
8920 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8921 const struct isds_message *outgoing_message,
8922 struct isds_list *copies) {
8924 isds_error err = IE_SUCCESS;
8925 #if HAVE_LIBCURL
8926 isds_error append_err;
8927 xmlNsPtr isds_ns = NULL;
8928 xmlNodePtr request = NULL, recipients, recipient, node;
8929 struct isds_list *item;
8930 struct isds_message_copy *copy;
8931 xmlDocPtr response = NULL;
8932 xmlChar *code = NULL, *message = NULL;
8933 xmlXPathContextPtr xpath_ctx = NULL;
8934 xmlXPathObjectPtr result = NULL;
8935 xmlChar *string = NULL;
8936 int i;
8937 #endif
8939 if (!context) return IE_INVALID_CONTEXT;
8940 zfree(context->long_message);
8941 if (!outgoing_message || !copies) return IE_INVAL;
8943 #if HAVE_LIBCURL
8944 /* Check if connection is established
8945 * TODO: This check should be done downstairs. */
8946 if (!context->curl) return IE_CONNECTION_CLOSED;
8949 /* Build CreateMultipleMessage request */
8950 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
8951 if (!request) {
8952 isds_log_message(context,
8953 _("Could not build CreateMultipleMessage request"));
8954 return IE_ERROR;
8956 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8957 if(!isds_ns) {
8958 isds_log_message(context, _("Could not create ISDS name space"));
8959 xmlFreeNode(request);
8960 return IE_ERROR;
8962 xmlSetNs(request, isds_ns);
8965 /* Build recipients */
8966 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8967 if (!recipients) {
8968 isds_log_message(context, _("Could not add dmRecipients child to "
8969 "CreateMultipleMessage element"));
8970 xmlFreeNode(request);
8971 return IE_ERROR;
8974 /* Insert each recipient */
8975 for (item = copies; item; item = item->next) {
8976 copy = (struct isds_message_copy *) item->data;
8977 if (!copy) {
8978 isds_log_message(context,
8979 _("`copies' list item contains empty data"));
8980 err = IE_INVAL;
8981 goto leave;
8984 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
8985 if (!recipient) {
8986 isds_log_message(context, _("Could not add dmRecipient child to "
8987 "dmRecipients element"));
8988 err = IE_ERROR;
8989 goto leave;
8992 if (!copy->dbIDRecipient) {
8993 isds_log_message(context,
8994 _("Message copy is missing recipient box identifier"));
8995 err = IE_INVAL;
8996 goto leave;
8998 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
8999 INSERT_STRING(recipient, "dmRecipientOrgUnit",
9000 copy->dmRecipientOrgUnit);
9001 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
9002 copy->dmRecipientOrgUnitNum, string);
9003 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
9006 /* Append envelope and files */
9007 err = insert_envelope_files(context, outgoing_message, request, 0);
9008 if (err) goto leave;
9011 isds_log(ILF_ISDS, ILL_DEBUG,
9012 _("Sending CreateMultipleMessage request to ISDS\n"));
9014 /* Sent request */
9015 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9016 if (err) {
9017 isds_log(ILF_ISDS, ILL_DEBUG,
9018 _("Processing ISDS response on CreateMultipleMessage "
9019 "request failed\n"));
9020 goto leave;
9023 /* Check for response status */
9024 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9025 &code, &message, NULL);
9026 if (err) {
9027 isds_log(ILF_ISDS, ILL_DEBUG,
9028 _("ISDS response on CreateMultipleMessage request "
9029 "is missing status\n"));
9030 goto leave;
9033 /* Request processed, but some copies failed */
9034 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9035 char *box_id_locale =
9036 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9037 char *code_locale = _isds_utf82locale((char*)code);
9038 char *message_locale = _isds_utf82locale((char*)message);
9039 isds_log(ILF_ISDS, ILL_DEBUG,
9040 _("Server did accept message for multiple recipients "
9041 "on CreateMultipleMessage request but delivery to "
9042 "some of them failed (code=%s, message=%s)\n"),
9043 box_id_locale, code_locale, message_locale);
9044 isds_log_message(context, message_locale);
9045 free(box_id_locale);
9046 free(code_locale);
9047 free(message_locale);
9048 err = IE_PARTIAL_SUCCESS;
9051 /* Request refused by server as whole */
9052 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9053 char *box_id_locale =
9054 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9055 char *code_locale = _isds_utf82locale((char*)code);
9056 char *message_locale = _isds_utf82locale((char*)message);
9057 isds_log(ILF_ISDS, ILL_DEBUG,
9058 _("Server did not accept message for multiple recipients "
9059 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9060 box_id_locale, code_locale, message_locale);
9061 isds_log_message(context, message_locale);
9062 free(box_id_locale);
9063 free(code_locale);
9064 free(message_locale);
9065 err = IE_ISDS;
9066 goto leave;
9070 /* Extract data */
9071 xpath_ctx = xmlXPathNewContext(response);
9072 if (!xpath_ctx) {
9073 err = IE_ERROR;
9074 goto leave;
9076 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9077 err = IE_ERROR;
9078 goto leave;
9080 result = xmlXPathEvalExpression(
9081 BAD_CAST "/isds:CreateMultipleMessageResponse"
9082 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9083 xpath_ctx);
9084 if (!result) {
9085 err = IE_ERROR;
9086 goto leave;
9088 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9089 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9090 err = IE_ISDS;
9091 goto leave;
9094 /* Extract message ID and delivery status for each copy */
9095 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9096 item = item->next, i++) {
9097 copy = (struct isds_message_copy *) item->data;
9098 xpath_ctx->node = result->nodesetval->nodeTab[i];
9100 append_err = append_TMStatus(context, copy, xpath_ctx);
9101 if (append_err) {
9102 err = append_err;
9103 goto leave;
9106 if (item || i < result->nodesetval->nodeNr) {
9107 isds_printf_message(context, _("ISDS returned unexpected number of "
9108 "message copy delivery states: %d"),
9109 result->nodesetval->nodeNr);
9110 err = IE_ISDS;
9111 goto leave;
9115 leave:
9116 /* Clean up */
9117 free(string);
9118 xmlXPathFreeObject(result);
9119 xmlXPathFreeContext(xpath_ctx);
9121 free(code);
9122 free(message);
9123 xmlFreeDoc(response);
9124 xmlFreeNode(request);
9126 if (!err)
9127 isds_log(ILF_ISDS, ILL_DEBUG,
9128 _("CreateMultipleMessageResponse request processed by server "
9129 "successfully.\n"));
9130 #else /* not HAVE_LIBCURL */
9131 err = IE_NOTSUP;
9132 #endif
9134 return err;
9138 /* Get list of messages. This is common core for getting sent or received
9139 * messages.
9140 * Any criterion argument can be NULL, if you don't care about it.
9141 * @context is session context. Must not be NULL.
9142 * @outgoing_direction is true if you want list of outgoing messages,
9143 * it's false if you want incoming messages.
9144 * @from_time is minimal time and date of message sending inclusive.
9145 * @to_time is maximal time and date of message sending inclusive
9146 * @organization_unit_number is number of sender/recipient respectively.
9147 * @status_filter is bit field of isds_message_status values. Use special
9148 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9149 * all values, you can use bit-wise arithmetic if you want.)
9150 * @offset is index of first message we are interested in. First message is 1.
9151 * Set to 0 (or 1) if you don't care.
9152 * @number is maximal length of list you want to get as input value, outputs
9153 * number of messages matching these criteria. Can be NULL if you don't care
9154 * (applies to output value either).
9155 * @messages is automatically reallocated list of isds_message's. Be ware that
9156 * it returns only brief overview (envelope and some other fields) about each
9157 * message, not the complete message. FIXME: Specify exact fields.
9158 * The list is sorted by delivery time in ascending order.
9159 * Use NULL if you don't care about don't need the data (useful if you want to
9160 * know only the @number). If you provide &NULL, list will be allocated on
9161 * heap, if you provide pointer to non-NULL, list will be freed automatically
9162 * at first. Also in case of error the list will be NULLed.
9163 * @return IE_SUCCESS or appropriate error code. */
9164 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9165 _Bool outgoing_direction,
9166 const struct timeval *from_time, const struct timeval *to_time,
9167 const long int *organization_unit_number,
9168 const unsigned int status_filter,
9169 const unsigned long int offset, unsigned long int *number,
9170 struct isds_list **messages) {
9172 isds_error err = IE_SUCCESS;
9173 #if HAVE_LIBCURL
9174 xmlNsPtr isds_ns = NULL;
9175 xmlNodePtr request = NULL, node;
9176 xmlDocPtr response = NULL;
9177 xmlChar *code = NULL, *message = NULL;
9178 xmlXPathContextPtr xpath_ctx = NULL;
9179 xmlXPathObjectPtr result = NULL;
9180 xmlChar *string = NULL;
9181 int count = 0;
9182 #endif
9184 if (!context) return IE_INVALID_CONTEXT;
9185 zfree(context->long_message);
9187 /* Free former message list if any */
9188 if (messages) isds_list_free(messages);
9190 #if HAVE_LIBCURL
9191 /* Check if connection is established
9192 * TODO: This check should be done downstairs. */
9193 if (!context->curl) return IE_CONNECTION_CLOSED;
9195 /* Build GetListOf*Messages request */
9196 request = xmlNewNode(NULL,
9197 (outgoing_direction) ?
9198 BAD_CAST "GetListOfSentMessages" :
9199 BAD_CAST "GetListOfReceivedMessages"
9201 if (!request) {
9202 isds_log_message(context,
9203 (outgoing_direction) ?
9204 _("Could not build GetListOfSentMessages request") :
9205 _("Could not build GetListOfReceivedMessages request")
9207 return IE_ERROR;
9209 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9210 if(!isds_ns) {
9211 isds_log_message(context, _("Could not create ISDS name space"));
9212 xmlFreeNode(request);
9213 return IE_ERROR;
9215 xmlSetNs(request, isds_ns);
9218 if (from_time) {
9219 err = timeval2timestring(from_time, &string);
9220 if (err) goto leave;
9222 INSERT_STRING(request, "dmFromTime", string);
9223 free(string); string = NULL;
9225 if (to_time) {
9226 err = timeval2timestring(to_time, &string);
9227 if (err) goto leave;
9229 INSERT_STRING(request, "dmToTime", string);
9230 free(string); string = NULL;
9232 if (outgoing_direction) {
9233 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9234 organization_unit_number, string);
9235 } else {
9236 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9237 organization_unit_number, string);
9240 if (status_filter > MESSAGESTATE_ANY) {
9241 isds_printf_message(context,
9242 _("Invalid message state filter value: %ld"), status_filter);
9243 err = IE_INVAL;
9244 goto leave;
9246 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9248 if (offset > 0 ) {
9249 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9250 } else {
9251 INSERT_STRING(request, "dmOffset", "1");
9254 /* number 0 means no limit */
9255 if (number && *number == 0) {
9256 INSERT_STRING(request, "dmLimit", NULL);
9257 } else {
9258 INSERT_ULONGINT(request, "dmLimit", number, string);
9262 isds_log(ILF_ISDS, ILL_DEBUG,
9263 (outgoing_direction) ?
9264 _("Sending GetListOfSentMessages request to ISDS\n") :
9265 _("Sending GetListOfReceivedMessages request to ISDS\n")
9268 /* Sent request */
9269 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9270 xmlFreeNode(request); request = NULL;
9272 if (err) {
9273 isds_log(ILF_ISDS, ILL_DEBUG,
9274 (outgoing_direction) ?
9275 _("Processing ISDS response on GetListOfSentMessages "
9276 "request failed\n") :
9277 _("Processing ISDS response on GetListOfReceivedMessages "
9278 "request failed\n")
9280 goto leave;
9283 /* Check for response status */
9284 err = isds_response_status(context, SERVICE_DM_INFO, response,
9285 &code, &message, NULL);
9286 if (err) {
9287 isds_log(ILF_ISDS, ILL_DEBUG,
9288 (outgoing_direction) ?
9289 _("ISDS response on GetListOfSentMessages request "
9290 "is missing status\n") :
9291 _("ISDS response on GetListOfReceivedMessages request "
9292 "is missing status\n")
9294 goto leave;
9297 /* Request processed, but nothing found */
9298 if (xmlStrcmp(code, BAD_CAST "0000")) {
9299 char *code_locale = _isds_utf82locale((char*)code);
9300 char *message_locale = _isds_utf82locale((char*)message);
9301 isds_log(ILF_ISDS, ILL_DEBUG,
9302 (outgoing_direction) ?
9303 _("Server refused GetListOfSentMessages request "
9304 "(code=%s, message=%s)\n") :
9305 _("Server refused GetListOfReceivedMessages request "
9306 "(code=%s, message=%s)\n"),
9307 code_locale, message_locale);
9308 isds_log_message(context, message_locale);
9309 free(code_locale);
9310 free(message_locale);
9311 err = IE_ISDS;
9312 goto leave;
9316 /* Extract data */
9317 xpath_ctx = xmlXPathNewContext(response);
9318 if (!xpath_ctx) {
9319 err = IE_ERROR;
9320 goto leave;
9322 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9323 err = IE_ERROR;
9324 goto leave;
9326 result = xmlXPathEvalExpression(
9327 (outgoing_direction) ?
9328 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9329 "isds:dmRecords/isds:dmRecord" :
9330 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9331 "isds:dmRecords/isds:dmRecord",
9332 xpath_ctx);
9333 if (!result) {
9334 err = IE_ERROR;
9335 goto leave;
9338 /* Fill output arguments in */
9339 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9340 struct isds_envelope *envelope;
9341 struct isds_list *item = NULL, *last_item = NULL;
9343 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9344 /* Create new message */
9345 item = calloc(1, sizeof(*item));
9346 if (!item) {
9347 err = IE_NOMEM;
9348 goto leave;
9350 item->destructor = (void(*)(void**)) &isds_message_free;
9351 item->data = calloc(1, sizeof(struct isds_message));
9352 if (!item->data) {
9353 isds_list_free(&item);
9354 err = IE_NOMEM;
9355 goto leave;
9358 /* Extract envelope data */
9359 xpath_ctx->node = result->nodesetval->nodeTab[count];
9360 envelope = NULL;
9361 err = extract_DmRecord(context, &envelope, xpath_ctx);
9362 if (err) {
9363 isds_list_free(&item);
9364 goto leave;
9367 /* Attach extracted envelope */
9368 ((struct isds_message *) item->data)->envelope = envelope;
9370 /* Append new message into the list */
9371 if (!*messages) {
9372 *messages = last_item = item;
9373 } else {
9374 last_item->next = item;
9375 last_item = item;
9379 if (number) *number = count;
9381 leave:
9382 if (err) {
9383 isds_list_free(messages);
9386 free(string);
9387 xmlXPathFreeObject(result);
9388 xmlXPathFreeContext(xpath_ctx);
9390 free(code);
9391 free(message);
9392 xmlFreeDoc(response);
9393 xmlFreeNode(request);
9395 if (!err)
9396 isds_log(ILF_ISDS, ILL_DEBUG,
9397 (outgoing_direction) ?
9398 _("GetListOfSentMessages request processed by server "
9399 "successfully.\n") :
9400 _("GetListOfReceivedMessages request processed by server "
9401 "successfully.\n")
9403 #else /* not HAVE_LIBCURL */
9404 err = IE_NOTSUP;
9405 #endif
9406 return err;
9410 /* Get list of outgoing (already sent) messages.
9411 * Any criterion argument can be NULL, if you don't care about it.
9412 * @context is session context. Must not be NULL.
9413 * @from_time is minimal time and date of message sending inclusive.
9414 * @to_time is maximal time and date of message sending inclusive
9415 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9416 * @status_filter is bit field of isds_message_status values. Use special
9417 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9418 * all values, you can use bit-wise arithmetic if you want.)
9419 * @offset is index of first message we are interested in. First message is 1.
9420 * Set to 0 (or 1) if you don't care.
9421 * @number is maximal length of list you want to get as input value, outputs
9422 * number of messages matching these criteria. Can be NULL if you don't care
9423 * (applies to output value either).
9424 * @messages is automatically reallocated list of isds_message's. Be ware that
9425 * it returns only brief overview (envelope and some other fields) about each
9426 * message, not the complete message. FIXME: Specify exact fields.
9427 * The list is sorted by delivery time in ascending order.
9428 * Use NULL if you don't care about the meta data (useful if you want to know
9429 * only the @number). If you provide &NULL, list will be allocated on heap,
9430 * if you provide pointer to non-NULL, list will be freed automatically at
9431 * first. Also in case of error the list will be NULLed.
9432 * @return IE_SUCCESS or appropriate error code. */
9433 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9434 const struct timeval *from_time, const struct timeval *to_time,
9435 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9436 const unsigned long int offset, unsigned long int *number,
9437 struct isds_list **messages) {
9439 return isds_get_list_of_messages(
9440 context, 1,
9441 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9442 offset, number,
9443 messages);
9447 /* Get list of incoming (addressed to you) messages.
9448 * Any criterion argument can be NULL, if you don't care about it.
9449 * @context is session context. Must not be NULL.
9450 * @from_time is minimal time and date of message sending inclusive.
9451 * @to_time is maximal time and date of message sending inclusive
9452 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9453 * @status_filter is bit field of isds_message_status values. Use special
9454 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9455 * all values, you can use bit-wise arithmetic if you want.)
9456 * @offset is index of first message we are interested in. First message is 1.
9457 * Set to 0 (or 1) if you don't care.
9458 * @number is maximal length of list you want to get as input value, outputs
9459 * number of messages matching these criteria. Can be NULL if you don't care
9460 * (applies to output value either).
9461 * @messages is automatically reallocated list of isds_message's. Be ware that
9462 * it returns only brief overview (envelope and some other fields) about each
9463 * message, not the complete message. FIXME: Specify exact fields.
9464 * Use NULL if you don't care about the meta data (useful if you want to know
9465 * only the @number). If you provide &NULL, list will be allocated on heap,
9466 * if you provide pointer to non-NULL, list will be freed automatically at
9467 * first. Also in case of error the list will be NULLed.
9468 * @return IE_SUCCESS or appropriate error code. */
9469 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9470 const struct timeval *from_time, const struct timeval *to_time,
9471 const long int *dmRecipientOrgUnitNum,
9472 const unsigned int status_filter,
9473 const unsigned long int offset, unsigned long int *number,
9474 struct isds_list **messages) {
9476 return isds_get_list_of_messages(
9477 context, 0,
9478 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9479 offset, number,
9480 messages);
9484 /* Get list of sent message state changes.
9485 * Any criterion argument can be NULL, if you don't care about it.
9486 * @context is session context. Must not be NULL.
9487 * @from_time is minimal time and date of status changes inclusive
9488 * @to_time is maximal time and date of status changes inclusive
9489 * @changed_states is automatically reallocated list of
9490 * isds_message_status_change's. If you provide &NULL, list will be allocated
9491 * on heap, if you provide pointer to non-NULL, list will be freed
9492 * automatically at first. Also in case of error the list will be NULLed.
9493 * XXX: The list item ordering is not specified.
9494 * XXX: Server provides only `recent' changes.
9495 * @return IE_SUCCESS or appropriate error code. */
9496 isds_error isds_get_list_of_sent_message_state_changes(
9497 struct isds_ctx *context,
9498 const struct timeval *from_time, const struct timeval *to_time,
9499 struct isds_list **changed_states) {
9501 isds_error err = IE_SUCCESS;
9502 #if HAVE_LIBCURL
9503 xmlNsPtr isds_ns = NULL;
9504 xmlNodePtr request = NULL, node;
9505 xmlDocPtr response = NULL;
9506 xmlXPathContextPtr xpath_ctx = NULL;
9507 xmlXPathObjectPtr result = NULL;
9508 xmlChar *string = NULL;
9509 int count = 0;
9510 #endif
9512 if (!context) return IE_INVALID_CONTEXT;
9513 zfree(context->long_message);
9515 /* Free former message list if any */
9516 isds_list_free(changed_states);
9518 #if HAVE_LIBCURL
9519 /* Check if connection is established
9520 * TODO: This check should be done downstairs. */
9521 if (!context->curl) return IE_CONNECTION_CLOSED;
9523 /* Build GetMessageStateChanges request */
9524 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
9525 if (!request) {
9526 isds_log_message(context,
9527 _("Could not build GetMessageStateChanges request"));
9528 return IE_ERROR;
9530 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9531 if(!isds_ns) {
9532 isds_log_message(context, _("Could not create ISDS name space"));
9533 xmlFreeNode(request);
9534 return IE_ERROR;
9536 xmlSetNs(request, isds_ns);
9539 if (from_time) {
9540 err = timeval2timestring(from_time, &string);
9541 if (err) goto leave;
9543 INSERT_STRING(request, "dmFromTime", string);
9544 zfree(string);
9546 if (to_time) {
9547 err = timeval2timestring(to_time, &string);
9548 if (err) goto leave;
9550 INSERT_STRING(request, "dmToTime", string);
9551 zfree(string);
9554 /* Sent request */
9555 err = send_destroy_request_check_response(context,
9556 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9557 &response, NULL, NULL);
9558 if (err) goto leave;
9561 /* Extract data */
9562 xpath_ctx = xmlXPathNewContext(response);
9563 if (!xpath_ctx) {
9564 err = IE_ERROR;
9565 goto leave;
9567 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9568 err = IE_ERROR;
9569 goto leave;
9571 result = xmlXPathEvalExpression(
9572 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9573 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9574 if (!result) {
9575 err = IE_ERROR;
9576 goto leave;
9579 /* Fill output arguments in */
9580 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9581 struct isds_list *item = NULL, *last_item = NULL;
9583 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9584 /* Create new status change */
9585 item = calloc(1, sizeof(*item));
9586 if (!item) {
9587 err = IE_NOMEM;
9588 goto leave;
9590 item->destructor =
9591 (void(*)(void**)) &isds_message_status_change_free;
9593 /* Extract message status change */
9594 xpath_ctx->node = result->nodesetval->nodeTab[count];
9595 err = extract_StateChangesRecord(context,
9596 (struct isds_message_status_change **) &item->data,
9597 xpath_ctx);
9598 if (err) {
9599 isds_list_free(&item);
9600 goto leave;
9603 /* Append new message status change into the list */
9604 if (!*changed_states) {
9605 *changed_states = last_item = item;
9606 } else {
9607 last_item->next = item;
9608 last_item = item;
9613 leave:
9614 if (err) {
9615 isds_list_free(changed_states);
9618 free(string);
9619 xmlXPathFreeObject(result);
9620 xmlXPathFreeContext(xpath_ctx);
9621 xmlFreeDoc(response);
9622 xmlFreeNode(request);
9624 if (!err)
9625 isds_log(ILF_ISDS, ILL_DEBUG,
9626 _("GetMessageStateChanges request processed by server "
9627 "successfully.\n"));
9628 #else /* not HAVE_LIBCURL */
9629 err = IE_NOTSUP;
9630 #endif
9631 return err;
9635 #if HAVE_LIBCURL
9636 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9637 * code
9638 * @context is session context
9639 * @service is ISDS WS service handler
9640 * @service_name is name of SERVICE_DM_OPERATIONS
9641 * @message_id is message ID to send as service argument to ISDS
9642 * @response is reallocated server SOAP body response as XML document
9643 * @raw_response is reallocated bit stream with response body. Use
9644 * NULL if you don't care
9645 * @raw_response_length is size of @raw_response in bytes
9646 * @code is reallocated ISDS status code
9647 * @status_message is reallocated ISDS status message
9648 * @return error coded from lower layer, context message will be set up
9649 * appropriately. */
9650 static isds_error build_send_check_message_request(struct isds_ctx *context,
9651 const isds_service service, const xmlChar *service_name,
9652 const char *message_id,
9653 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9654 xmlChar **code, xmlChar **status_message) {
9656 isds_error err = IE_SUCCESS;
9657 char *service_name_locale = NULL, *message_id_locale = NULL;
9658 xmlNodePtr request = NULL, node;
9659 xmlNsPtr isds_ns = NULL;
9661 if (!context) return IE_INVALID_CONTEXT;
9662 if (!service_name || !message_id) return IE_INVAL;
9663 if (!response || !code || !status_message) return IE_INVAL;
9664 if (!raw_response_length && raw_response) return IE_INVAL;
9666 /* Free output argument */
9667 xmlFreeDoc(*response); *response = NULL;
9668 if (raw_response) zfree(*raw_response);
9669 zfree(*code);
9670 zfree(*status_message);
9673 /* Check if connection is established
9674 * TODO: This check should be done downstairs. */
9675 if (!context->curl) return IE_CONNECTION_CLOSED;
9677 service_name_locale = _isds_utf82locale((char*)service_name);
9678 message_id_locale = _isds_utf82locale(message_id);
9679 if (!service_name_locale || !message_id_locale) {
9680 err = IE_NOMEM;
9681 goto leave;
9684 /* Build request */
9685 request = xmlNewNode(NULL, service_name);
9686 if (!request) {
9687 isds_printf_message(context,
9688 _("Could not build %s request for %s message ID"),
9689 service_name_locale, message_id_locale);
9690 err = IE_ERROR;
9691 goto leave;
9693 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9694 if(!isds_ns) {
9695 isds_log_message(context, _("Could not create ISDS name space"));
9696 err = IE_ERROR;
9697 goto leave;
9699 xmlSetNs(request, isds_ns);
9702 /* Add requested ID */
9703 err = validate_message_id_length(context, (xmlChar *) message_id);
9704 if (err) goto leave;
9705 INSERT_STRING(request, "dmID", message_id);
9708 isds_log(ILF_ISDS, ILL_DEBUG,
9709 _("Sending %s request for %s message ID to ISDS\n"),
9710 service_name_locale, message_id_locale);
9712 /* Send request */
9713 err = _isds(context, service, request, response,
9714 raw_response, raw_response_length);
9715 xmlFreeNode(request); request = NULL;
9717 if (err) {
9718 isds_log(ILF_ISDS, ILL_DEBUG,
9719 _("Processing ISDS response on %s request failed\n"),
9720 service_name_locale);
9721 goto leave;
9724 /* Check for response status */
9725 err = isds_response_status(context, service, *response,
9726 code, status_message, NULL);
9727 if (err) {
9728 isds_log(ILF_ISDS, ILL_DEBUG,
9729 _("ISDS response on %s request is missing status\n"),
9730 service_name_locale);
9731 goto leave;
9734 /* Request processed, but nothing found */
9735 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9736 char *code_locale = _isds_utf82locale((char*) *code);
9737 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9738 isds_log(ILF_ISDS, ILL_DEBUG,
9739 _("Server refused %s request for %s message ID "
9740 "(code=%s, message=%s)\n"),
9741 service_name_locale, message_id_locale,
9742 code_locale, status_message_locale);
9743 isds_log_message(context, status_message_locale);
9744 free(code_locale);
9745 free(status_message_locale);
9746 err = IE_ISDS;
9747 goto leave;
9750 leave:
9751 free(message_id_locale);
9752 free(service_name_locale);
9753 xmlFreeNode(request);
9754 return err;
9758 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9759 * signed data and free ISDS response.
9760 * @context is session context
9761 * @message_id is UTF-8 encoded message ID for logging purpose
9762 * @response is parsed XML document. It will be freed and NULLed in the middle
9763 * of function run to save memory. This is not guaranteed in case of error.
9764 * @request_name is name of ISDS request used to construct response root
9765 * element name and for logging purpose.
9766 * @raw is reallocated output buffer with DER encoded CMS data
9767 * @raw_length is size of @raw buffer in bytes
9768 * @returns standard error codes, in case of error, @raw will be freed and
9769 * NULLed, @response sometimes. */
9770 static isds_error find_extract_signed_data_free_response(
9771 struct isds_ctx *context, const xmlChar *message_id,
9772 xmlDocPtr *response, const xmlChar *request_name,
9773 void **raw, size_t *raw_length) {
9775 isds_error err = IE_SUCCESS;
9776 char *xpath_expression = NULL;
9777 xmlXPathContextPtr xpath_ctx = NULL;
9778 xmlXPathObjectPtr result = NULL;
9779 char *encoded_structure = NULL;
9781 if (!context) return IE_INVALID_CONTEXT;
9782 if (!raw) return IE_INVAL;
9783 zfree(*raw);
9784 if (!message_id || !response || !*response || !request_name || !raw_length)
9785 return IE_INVAL;
9787 /* Build XPath expression */
9788 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9789 "Response/isds:dmSignature");
9790 if (!xpath_expression) return IE_NOMEM;
9792 /* Extract data */
9793 xpath_ctx = xmlXPathNewContext(*response);
9794 if (!xpath_ctx) {
9795 err = IE_ERROR;
9796 goto leave;
9798 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9799 err = IE_ERROR;
9800 goto leave;
9802 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9803 if (!result) {
9804 err = IE_ERROR;
9805 goto leave;
9807 /* Empty response */
9808 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9809 char *message_id_locale = _isds_utf82locale((char*) message_id);
9810 isds_printf_message(context,
9811 _("Server did not return any signed data for message ID `%s' "
9812 "on %s request"),
9813 message_id_locale, request_name);
9814 free(message_id_locale);
9815 err = IE_ISDS;
9816 goto leave;
9818 /* More responses */
9819 if (result->nodesetval->nodeNr > 1) {
9820 char *message_id_locale = _isds_utf82locale((char*) message_id);
9821 isds_printf_message(context,
9822 _("Server did return more signed data for message ID `%s' "
9823 "on %s request"),
9824 message_id_locale, request_name);
9825 free(message_id_locale);
9826 err = IE_ISDS;
9827 goto leave;
9829 /* One response */
9830 xpath_ctx->node = result->nodesetval->nodeTab[0];
9832 /* Extract PKCS#7 structure */
9833 EXTRACT_STRING(".", encoded_structure);
9834 if (!encoded_structure) {
9835 isds_log_message(context, _("dmSignature element is empty"));
9838 /* Here we have delivery info as standalone CMS in encoded_structure.
9839 * We don't need any other data, free them: */
9840 xmlXPathFreeObject(result); result = NULL;
9841 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9842 xmlFreeDoc(*response); *response = NULL;
9845 /* Decode PKCS#7 to DER format */
9846 *raw_length = _isds_b64decode(encoded_structure, raw);
9847 if (*raw_length == (size_t) -1) {
9848 isds_log_message(context,
9849 _("Error while Base64-decoding PKCS#7 structure"));
9850 err = IE_ERROR;
9851 goto leave;
9854 leave:
9855 if (err) {
9856 zfree(*raw);
9857 raw_length = 0;
9860 free(encoded_structure);
9861 xmlXPathFreeObject(result);
9862 xmlXPathFreeContext(xpath_ctx);
9863 free(xpath_expression);
9865 return err;
9867 #endif /* HAVE_LIBCURL */
9870 /* Download incoming message envelope identified by ID.
9871 * @context is session context
9872 * @message_id is message identifier (you can get them from
9873 * isds_get_list_of_received_messages())
9874 * @message is automatically reallocated message retrieved from ISDS.
9875 * It will miss documents per se. Use isds_get_received_message(), if you are
9876 * interested in documents (content) too.
9877 * Returned hash and timestamp require documents to be verifiable. */
9878 isds_error isds_get_received_envelope(struct isds_ctx *context,
9879 const char *message_id, struct isds_message **message) {
9881 isds_error err = IE_SUCCESS;
9882 #if HAVE_LIBCURL
9883 xmlDocPtr response = NULL;
9884 xmlChar *code = NULL, *status_message = NULL;
9885 xmlXPathContextPtr xpath_ctx = NULL;
9886 xmlXPathObjectPtr result = NULL;
9887 #endif
9889 if (!context) return IE_INVALID_CONTEXT;
9890 zfree(context->long_message);
9892 /* Free former message if any */
9893 if (!message) return IE_INVAL;
9894 isds_message_free(message);
9896 #if HAVE_LIBCURL
9897 /* Do request and check for success */
9898 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9899 BAD_CAST "MessageEnvelopeDownload", message_id,
9900 &response, NULL, NULL, &code, &status_message);
9901 if (err) goto leave;
9903 /* Extract data */
9904 xpath_ctx = xmlXPathNewContext(response);
9905 if (!xpath_ctx) {
9906 err = IE_ERROR;
9907 goto leave;
9909 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9910 err = IE_ERROR;
9911 goto leave;
9913 result = xmlXPathEvalExpression(
9914 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9915 "isds:dmReturnedMessageEnvelope",
9916 xpath_ctx);
9917 if (!result) {
9918 err = IE_ERROR;
9919 goto leave;
9921 /* Empty response */
9922 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9923 char *message_id_locale = _isds_utf82locale((char*) message_id);
9924 isds_printf_message(context,
9925 _("Server did not return any envelope for ID `%s' "
9926 "on MessageEnvelopeDownload request"), message_id_locale);
9927 free(message_id_locale);
9928 err = IE_ISDS;
9929 goto leave;
9931 /* More envelops */
9932 if (result->nodesetval->nodeNr > 1) {
9933 char *message_id_locale = _isds_utf82locale((char*) message_id);
9934 isds_printf_message(context,
9935 _("Server did return more envelopes for ID `%s' "
9936 "on MessageEnvelopeDownload request"), message_id_locale);
9937 free(message_id_locale);
9938 err = IE_ISDS;
9939 goto leave;
9941 /* One message */
9942 xpath_ctx->node = result->nodesetval->nodeTab[0];
9944 /* Extract the envelope (= message without documents, hence 0) */
9945 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9946 if (err) goto leave;
9948 /* Save XML blob */
9949 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9950 &(*message)->raw_length);
9952 leave:
9953 if (err) {
9954 isds_message_free(message);
9957 xmlXPathFreeObject(result);
9958 xmlXPathFreeContext(xpath_ctx);
9960 free(code);
9961 free(status_message);
9962 if (!*message || !(*message)->xml) {
9963 xmlFreeDoc(response);
9966 if (!err)
9967 isds_log(ILF_ISDS, ILL_DEBUG,
9968 _("MessageEnvelopeDownload request processed by server "
9969 "successfully.\n")
9971 #else /* not HAVE_LIBCURL */
9972 err = IE_NOTSUP;
9973 #endif
9974 return err;
9978 /* Load delivery info of any format from buffer.
9979 * @context is session context
9980 * @raw_type advertises format of @buffer content. Only delivery info types
9981 * are accepted.
9982 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9983 * retrieve such data from message->raw after calling
9984 * isds_get_signed_delivery_info().
9985 * @length is length of buffer in bytes.
9986 * @message is automatically reallocated message parsed from @buffer.
9987 * @strategy selects how buffer will be attached into raw isds_message member.
9988 * */
9989 isds_error isds_load_delivery_info(struct isds_ctx *context,
9990 const isds_raw_type raw_type,
9991 const void *buffer, const size_t length,
9992 struct isds_message **message, const isds_buffer_strategy strategy) {
9994 isds_error err = IE_SUCCESS;
9995 message_ns_type message_ns;
9996 xmlDocPtr message_doc = NULL;
9997 xmlXPathContextPtr xpath_ctx = NULL;
9998 xmlXPathObjectPtr result = NULL;
9999 void *xml_stream = NULL;
10000 size_t xml_stream_length = 0;
10002 if (!context) return IE_INVALID_CONTEXT;
10003 zfree(context->long_message);
10004 if (!message) return IE_INVAL;
10005 isds_message_free(message);
10006 if (!buffer) return IE_INVAL;
10009 /* Select buffer format and extract XML from CMS*/
10010 switch (raw_type) {
10011 case RAWTYPE_DELIVERYINFO:
10012 message_ns = MESSAGE_NS_UNSIGNED;
10013 xml_stream = (void *) buffer;
10014 xml_stream_length = length;
10015 break;
10017 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
10018 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10019 xml_stream = (void *) buffer;
10020 xml_stream_length = length;
10021 break;
10023 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10024 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10025 err = _isds_extract_cms_data(context, buffer, length,
10026 &xml_stream, &xml_stream_length);
10027 if (err) goto leave;
10028 break;
10030 default:
10031 isds_log_message(context, _("Bad raw delivery representation type"));
10032 return IE_INVAL;
10033 break;
10036 if (_isds_sizet2int(xml_stream_length) >= 0) {
10037 isds_log(ILF_ISDS, ILL_DEBUG,
10038 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10039 _isds_sizet2int(xml_stream_length), xml_stream);
10042 /* Convert delivery info XML stream into XPath context */
10043 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10044 if (!message_doc) {
10045 err = IE_XML;
10046 goto leave;
10048 xpath_ctx = xmlXPathNewContext(message_doc);
10049 if (!xpath_ctx) {
10050 err = IE_ERROR;
10051 goto leave;
10053 /* XXX: Name spaces mangled for signed delivery info:
10054 * http://isds.czechpoint.cz/v20/delivery:
10056 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10057 * <q:dmDelivery>
10058 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10059 * <p:dmID>170272</p:dmID>
10060 * ...
10061 * </p:dmDm>
10062 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10063 * ...
10064 * </q:dmEvents>...</q:dmEvents>
10065 * </q:dmDelivery>
10066 * </q:GetDeliveryInfoResponse>
10067 * */
10068 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10069 err = IE_ERROR;
10070 goto leave;
10072 result = xmlXPathEvalExpression(
10073 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10074 xpath_ctx);
10075 if (!result) {
10076 err = IE_ERROR;
10077 goto leave;
10079 /* Empty delivery info */
10080 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10081 isds_printf_message(context,
10082 _("XML document is not sisds:dmDelivery document"));
10083 err = IE_ISDS;
10084 goto leave;
10086 /* More delivery info's */
10087 if (result->nodesetval->nodeNr > 1) {
10088 isds_printf_message(context,
10089 _("XML document has more sisds:dmDelivery elements"));
10090 err = IE_ISDS;
10091 goto leave;
10093 /* One delivery info */
10094 xpath_ctx->node = result->nodesetval->nodeTab[0];
10096 /* Extract the envelope (= message without documents, hence 0).
10097 * XXX: extract_TReturnedMessage() can obtain attachments size,
10098 * but delivery info carries none. It's coded as option elements,
10099 * so it should work. */
10100 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10101 if (err) goto leave;
10103 /* Extract events */
10104 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10105 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10106 if (err) { err = IE_ERROR; goto leave; }
10107 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10108 if (err) goto leave;
10110 /* Append raw CMS structure into message */
10111 (*message)->raw_type = raw_type;
10112 switch (strategy) {
10113 case BUFFER_DONT_STORE:
10114 break;
10115 case BUFFER_COPY:
10116 (*message)->raw = malloc(length);
10117 if (!(*message)->raw) {
10118 err = IE_NOMEM;
10119 goto leave;
10121 memcpy((*message)->raw, buffer, length);
10122 (*message)->raw_length = length;
10123 break;
10124 case BUFFER_MOVE:
10125 (*message)->raw = (void *) buffer;
10126 (*message)->raw_length = length;
10127 break;
10128 default:
10129 err = IE_ENUM;
10130 goto leave;
10133 leave:
10134 if (err) {
10135 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10136 isds_message_free(message);
10139 xmlXPathFreeObject(result);
10140 xmlXPathFreeContext(xpath_ctx);
10141 if (!*message || !(*message)->xml) {
10142 xmlFreeDoc(message_doc);
10144 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10146 if (!err)
10147 isds_log(ILF_ISDS, ILL_DEBUG,
10148 _("Delivery info loaded successfully.\n"));
10149 return err;
10153 /* Download signed delivery info-sheet of given message identified by ID.
10154 * @context is session context
10155 * @message_id is message identifier (you can get them from
10156 * isds_get_list_of_{sent,received}_messages())
10157 * @message is automatically reallocated message retrieved from ISDS.
10158 * It will miss documents per se. Use isds_get_signed_received_message(),
10159 * if you are interested in documents (content). OTOH, only this function
10160 * can get list events message has gone through. */
10161 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10162 const char *message_id, struct isds_message **message) {
10164 isds_error err = IE_SUCCESS;
10165 #if HAVE_LIBCURL
10166 xmlDocPtr response = NULL;
10167 xmlChar *code = NULL, *status_message = NULL;
10168 void *raw = NULL;
10169 size_t raw_length = 0;
10170 #endif
10172 if (!context) return IE_INVALID_CONTEXT;
10173 zfree(context->long_message);
10175 /* Free former message if any */
10176 if (!message) return IE_INVAL;
10177 isds_message_free(message);
10179 #if HAVE_LIBCURL
10180 /* Do request and check for success */
10181 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10182 BAD_CAST "GetSignedDeliveryInfo", message_id,
10183 &response, NULL, NULL, &code, &status_message);
10184 if (err) goto leave;
10186 /* Find signed delivery info, extract it into raw and maybe free
10187 * response */
10188 err = find_extract_signed_data_free_response(context,
10189 (xmlChar *)message_id, &response,
10190 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10191 if (err) goto leave;
10193 /* Parse delivery info */
10194 err = isds_load_delivery_info(context,
10195 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10196 message, BUFFER_MOVE);
10197 if (err) goto leave;
10199 raw = NULL;
10201 leave:
10202 if (err) {
10203 isds_message_free(message);
10206 free(raw);
10207 free(code);
10208 free(status_message);
10209 xmlFreeDoc(response);
10211 if (!err)
10212 isds_log(ILF_ISDS, ILL_DEBUG,
10213 _("GetSignedDeliveryInfo request processed by server "
10214 "successfully.\n")
10216 #else /* not HAVE_LIBCURL */
10217 err = IE_NOTSUP;
10218 #endif
10219 return err;
10223 /* Download delivery info-sheet of given message identified by ID.
10224 * @context is session context
10225 * @message_id is message identifier (you can get them from
10226 * isds_get_list_of_{sent,received}_messages())
10227 * @message is automatically reallocated message retrieved from ISDS.
10228 * It will miss documents per se. Use isds_get_received_message(), if you are
10229 * interested in documents (content). OTOH, only this function can get list
10230 * of events message has gone through. */
10231 isds_error isds_get_delivery_info(struct isds_ctx *context,
10232 const char *message_id, struct isds_message **message) {
10234 isds_error err = IE_SUCCESS;
10235 #if HAVE_LIBCURL
10236 xmlDocPtr response = NULL;
10237 xmlChar *code = NULL, *status_message = NULL;
10238 xmlNodePtr delivery_node = NULL;
10239 void *raw = NULL;
10240 size_t raw_length = 0;
10241 #endif
10243 if (!context) return IE_INVALID_CONTEXT;
10244 zfree(context->long_message);
10246 /* Free former message if any */
10247 if (!message) return IE_INVAL;
10248 isds_message_free(message);
10250 #if HAVE_LIBCURL
10251 /* Do request and check for success */
10252 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10253 BAD_CAST "GetDeliveryInfo", message_id,
10254 &response, NULL, NULL, &code, &status_message);
10255 if (err) goto leave;
10258 /* Serialize delivery info */
10259 delivery_node = xmlDocGetRootElement(response);
10260 if (!delivery_node) {
10261 char *message_id_locale = _isds_utf82locale((char*) message_id);
10262 isds_printf_message(context,
10263 _("Server did not return any delivery info for ID `%s' "
10264 "on GetDeliveryInfo request"), message_id_locale);
10265 free(message_id_locale);
10266 err = IE_ISDS;
10267 goto leave;
10269 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10270 if (err) goto leave;
10272 /* Parse delivery info */
10273 /* TODO: Here we parse the response second time. We could single delivery
10274 * parser from isds_load_delivery_info() to make things faster. */
10275 err = isds_load_delivery_info(context,
10276 RAWTYPE_DELIVERYINFO, raw, raw_length,
10277 message, BUFFER_MOVE);
10278 if (err) goto leave;
10280 raw = NULL;
10283 leave:
10284 if (err) {
10285 isds_message_free(message);
10288 free(raw);
10289 free(code);
10290 free(status_message);
10291 xmlFreeDoc(response);
10293 if (!err)
10294 isds_log(ILF_ISDS, ILL_DEBUG,
10295 _("GetDeliveryInfo request processed by server "
10296 "successfully.\n")
10298 #else /* not HAVE_LIBCURL */
10299 err = IE_NOTSUP;
10300 #endif
10301 return err;
10305 /* Download incoming message identified by ID.
10306 * @context is session context
10307 * @message_id is message identifier (you can get them from
10308 * isds_get_list_of_received_messages())
10309 * @message is automatically reallocated message retrieved from ISDS */
10310 isds_error isds_get_received_message(struct isds_ctx *context,
10311 const char *message_id, struct isds_message **message) {
10313 isds_error err = IE_SUCCESS;
10314 #if HAVE_LIBCURL
10315 xmlDocPtr response = NULL;
10316 void *xml_stream = NULL;
10317 size_t xml_stream_length;
10318 xmlChar *code = NULL, *status_message = NULL;
10319 xmlXPathContextPtr xpath_ctx = NULL;
10320 xmlXPathObjectPtr result = NULL;
10321 char *phys_path = NULL;
10322 size_t phys_start, phys_end;
10323 #endif
10325 if (!context) return IE_INVALID_CONTEXT;
10326 zfree(context->long_message);
10328 /* Free former message if any */
10329 if (NULL == message) return IE_INVAL;
10330 if (message) isds_message_free(message);
10332 #if HAVE_LIBCURL
10333 /* Do request and check for success */
10334 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10335 BAD_CAST "MessageDownload", message_id,
10336 &response, &xml_stream, &xml_stream_length,
10337 &code, &status_message);
10338 if (err) goto leave;
10340 /* Extract data */
10341 xpath_ctx = xmlXPathNewContext(response);
10342 if (!xpath_ctx) {
10343 err = IE_ERROR;
10344 goto leave;
10346 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10347 err = IE_ERROR;
10348 goto leave;
10350 result = xmlXPathEvalExpression(
10351 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10352 xpath_ctx);
10353 if (!result) {
10354 err = IE_ERROR;
10355 goto leave;
10357 /* Empty response */
10358 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10359 char *message_id_locale = _isds_utf82locale((char*) message_id);
10360 isds_printf_message(context,
10361 _("Server did not return any message for ID `%s' "
10362 "on MessageDownload request"), message_id_locale);
10363 free(message_id_locale);
10364 err = IE_ISDS;
10365 goto leave;
10367 /* More messages */
10368 if (result->nodesetval->nodeNr > 1) {
10369 char *message_id_locale = _isds_utf82locale((char*) message_id);
10370 isds_printf_message(context,
10371 _("Server did return more messages for ID `%s' "
10372 "on MessageDownload request"), message_id_locale);
10373 free(message_id_locale);
10374 err = IE_ISDS;
10375 goto leave;
10377 /* One message */
10378 xpath_ctx->node = result->nodesetval->nodeTab[0];
10380 /* Extract the message */
10381 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10382 if (err) goto leave;
10384 /* Locate raw XML blob */
10385 phys_path = strdup(
10386 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10387 PHYSXML_ELEMENT_SEPARATOR
10388 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10389 PHYSXML_ELEMENT_SEPARATOR
10390 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10392 if (!phys_path) {
10393 err = IE_NOMEM;
10394 goto leave;
10396 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10397 phys_path, &phys_start, &phys_end);
10398 zfree(phys_path);
10399 if (err) {
10400 isds_log_message(context,
10401 _("Substring with isds:MessageDownloadResponse element "
10402 "could not be located in raw SOAP message"));
10403 goto leave;
10405 /* Save XML blob */
10406 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10407 &(*message)->raw_length);*/
10408 /* TODO: Store name space declarations from ancestors */
10409 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10410 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10411 (*message)->raw_length = phys_end - phys_start + 1;
10412 (*message)->raw = malloc((*message)->raw_length);
10413 if (!(*message)->raw) {
10414 err = IE_NOMEM;
10415 goto leave;
10417 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10420 leave:
10421 if (err) {
10422 isds_message_free(message);
10425 free(phys_path);
10427 xmlXPathFreeObject(result);
10428 xmlXPathFreeContext(xpath_ctx);
10430 free(code);
10431 free(status_message);
10432 free(xml_stream);
10433 if (!*message || !(*message)->xml) {
10434 xmlFreeDoc(response);
10437 if (!err)
10438 isds_log(ILF_ISDS, ILL_DEBUG,
10439 _("MessageDownload request processed by server "
10440 "successfully.\n")
10442 #else /* not HAVE_LIBCURL */
10443 err = IE_NOTSUP;
10444 #endif
10445 return err;
10449 /* Load message of any type from buffer.
10450 * @context is session context
10451 * @raw_type defines content type of @buffer. Only message types are allowed.
10452 * @buffer is message raw representation. Format (CMS, plain signed,
10453 * message direction) is defined in @raw_type. You can retrieve such data
10454 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10455 * @length is length of buffer in bytes.
10456 * @message is automatically reallocated message parsed from @buffer.
10457 * @strategy selects how buffer will be attached into raw isds_message member.
10458 * */
10459 isds_error isds_load_message(struct isds_ctx *context,
10460 const isds_raw_type raw_type, const void *buffer, const size_t length,
10461 struct isds_message **message, const isds_buffer_strategy strategy) {
10463 isds_error err = IE_SUCCESS;
10464 void *xml_stream = NULL;
10465 size_t xml_stream_length = 0;
10466 message_ns_type message_ns;
10467 xmlDocPtr message_doc = NULL;
10468 xmlXPathContextPtr xpath_ctx = NULL;
10469 xmlXPathObjectPtr result = NULL;
10471 if (!context) return IE_INVALID_CONTEXT;
10472 zfree(context->long_message);
10473 if (!message) return IE_INVAL;
10474 isds_message_free(message);
10475 if (!buffer) return IE_INVAL;
10478 /* Select buffer format and extract XML from CMS*/
10479 switch (raw_type) {
10480 case RAWTYPE_INCOMING_MESSAGE:
10481 message_ns = MESSAGE_NS_UNSIGNED;
10482 xml_stream = (void *) buffer;
10483 xml_stream_length = length;
10484 break;
10486 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10487 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10488 xml_stream = (void *) buffer;
10489 xml_stream_length = length;
10490 break;
10492 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10493 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10494 err = _isds_extract_cms_data(context, buffer, length,
10495 &xml_stream, &xml_stream_length);
10496 if (err) goto leave;
10497 break;
10499 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10500 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10501 xml_stream = (void *) buffer;
10502 xml_stream_length = length;
10503 break;
10505 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10506 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10507 err = _isds_extract_cms_data(context, buffer, length,
10508 &xml_stream, &xml_stream_length);
10509 if (err) goto leave;
10510 break;
10512 default:
10513 isds_log_message(context, _("Bad raw message representation type"));
10514 return IE_INVAL;
10515 break;
10518 if (_isds_sizet2int(xml_stream_length) >= 0) {
10519 isds_log(ILF_ISDS, ILL_DEBUG,
10520 _("Loading message:\n%.*s\nEnd of message\n"),
10521 _isds_sizet2int(xml_stream_length), xml_stream);
10524 /* Convert messages XML stream into XPath context */
10525 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10526 if (!message_doc) {
10527 err = IE_XML;
10528 goto leave;
10530 xpath_ctx = xmlXPathNewContext(message_doc);
10531 if (!xpath_ctx) {
10532 err = IE_ERROR;
10533 goto leave;
10535 /* XXX: Standard name space for unsigned incoming direction:
10536 * http://isds.czechpoint.cz/v20/
10538 * XXX: Name spaces mangled for signed outgoing direction:
10539 * http://isds.czechpoint.cz/v20/SentMessage:
10541 * <q:MessageDownloadResponse
10542 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10543 * <q:dmReturnedMessage>
10544 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10545 * <p:dmID>151916</p:dmID>
10546 * ...
10547 * </p:dmDm>
10548 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10549 * ...
10550 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10551 * </q:dmReturnedMessage>
10552 * </q:MessageDownloadResponse>
10554 * XXX: Name spaces mangled for signed incoming direction:
10555 * http://isds.czechpoint.cz/v20/message:
10557 * <q:MessageDownloadResponse
10558 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10559 * <q:dmReturnedMessage>
10560 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10561 * <p:dmID>151916</p:dmID>
10562 * ...
10563 * </p:dmDm>
10564 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10565 * ...
10566 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10567 * </q:dmReturnedMessage>
10568 * </q:MessageDownloadResponse>
10570 * Stupidity of ISDS developers is unlimited */
10571 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10572 err = IE_ERROR;
10573 goto leave;
10575 result = xmlXPathEvalExpression(
10576 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10577 xpath_ctx);
10578 if (!result) {
10579 err = IE_ERROR;
10580 goto leave;
10582 /* Empty message */
10583 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10584 isds_printf_message(context,
10585 _("XML document does not contain "
10586 "sisds:dmReturnedMessage element"));
10587 err = IE_ISDS;
10588 goto leave;
10590 /* More messages */
10591 if (result->nodesetval->nodeNr > 1) {
10592 isds_printf_message(context,
10593 _("XML document has more sisds:dmReturnedMessage elements"));
10594 err = IE_ISDS;
10595 goto leave;
10597 /* One message */
10598 xpath_ctx->node = result->nodesetval->nodeTab[0];
10600 /* Extract the message */
10601 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10602 if (err) goto leave;
10604 /* Append raw buffer into message */
10605 (*message)->raw_type = raw_type;
10606 switch (strategy) {
10607 case BUFFER_DONT_STORE:
10608 break;
10609 case BUFFER_COPY:
10610 (*message)->raw = malloc(length);
10611 if (!(*message)->raw) {
10612 err = IE_NOMEM;
10613 goto leave;
10615 memcpy((*message)->raw, buffer, length);
10616 (*message)->raw_length = length;
10617 break;
10618 case BUFFER_MOVE:
10619 (*message)->raw = (void *) buffer;
10620 (*message)->raw_length = length;
10621 break;
10622 default:
10623 err = IE_ENUM;
10624 goto leave;
10628 leave:
10629 if (err) {
10630 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10631 isds_message_free(message);
10634 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10635 xmlXPathFreeObject(result);
10636 xmlXPathFreeContext(xpath_ctx);
10637 if (!*message || !(*message)->xml) {
10638 xmlFreeDoc(message_doc);
10641 if (!err)
10642 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10643 return err;
10647 /* Determine type of raw message or delivery info according some heuristics.
10648 * It does not validate the raw blob.
10649 * @context is session context
10650 * @raw_type returns content type of @buffer. Valid only if exit code of this
10651 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10652 * reallocated memory.
10653 * @buffer is message raw representation.
10654 * @length is length of buffer in bytes. */
10655 isds_error isds_guess_raw_type(struct isds_ctx *context,
10656 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10657 isds_error err;
10658 void *xml_stream = NULL;
10659 size_t xml_stream_length = 0;
10660 xmlDocPtr document = NULL;
10661 xmlNodePtr root = NULL;
10663 if (!context) return IE_INVALID_CONTEXT;
10664 zfree(context->long_message);
10665 if (length == 0 || !buffer) return IE_INVAL;
10666 if (!raw_type) return IE_INVAL;
10668 /* Try CMS */
10669 err = _isds_extract_cms_data(context, buffer, length,
10670 &xml_stream, &xml_stream_length);
10671 if (err) {
10672 xml_stream = (void *) buffer;
10673 xml_stream_length = (size_t) length;
10674 err = IE_SUCCESS;
10677 /* Try XML */
10678 document = xmlParseMemory(xml_stream, xml_stream_length);
10679 if (!document) {
10680 isds_printf_message(context,
10681 _("Could not parse data as XML document"));
10682 err = IE_NOTSUP;
10683 goto leave;
10686 /* Get root element */
10687 root = xmlDocGetRootElement(document);
10688 if (!root) {
10689 isds_printf_message(context,
10690 _("XML document is missing root element"));
10691 err = IE_XML;
10692 goto leave;
10695 if (!root->ns || !root->ns->href) {
10696 isds_printf_message(context,
10697 _("Root element does not belong to any name space"));
10698 err = IE_NOTSUP;
10699 goto leave;
10702 /* Test name space */
10703 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10704 if (xml_stream == buffer)
10705 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10706 else
10707 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10708 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10709 if (xml_stream == buffer)
10710 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10711 else
10712 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10713 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10714 if (xml_stream == buffer)
10715 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10716 else
10717 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10718 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10719 if (xml_stream != buffer) {
10720 isds_printf_message(context,
10721 _("Document in ISDS name space is encapsulated into CMS" ));
10722 err = IE_NOTSUP;
10723 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10724 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10725 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10726 *raw_type = RAWTYPE_DELIVERYINFO;
10727 else {
10728 isds_printf_message(context,
10729 _("Unknown root element in ISDS name space"));
10730 err = IE_NOTSUP;
10732 } else {
10733 isds_printf_message(context,
10734 _("Unknown name space"));
10735 err = IE_NOTSUP;
10738 leave:
10739 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10740 xmlFreeDoc(document);
10741 return err;
10745 /* Download signed incoming/outgoing message identified by ID.
10746 * @context is session context
10747 * @output is true for outgoing message, false for incoming message
10748 * @message_id is message identifier (you can get them from
10749 * isds_get_list_of_{sent,received}_messages())
10750 * @message is automatically reallocated message retrieved from ISDS. The raw
10751 * member will be filled with PKCS#7 structure in DER format. */
10752 static isds_error isds_get_signed_message(struct isds_ctx *context,
10753 const _Bool outgoing, const char *message_id,
10754 struct isds_message **message) {
10756 isds_error err = IE_SUCCESS;
10757 #if HAVE_LIBCURL
10758 xmlDocPtr response = NULL;
10759 xmlChar *code = NULL, *status_message = NULL;
10760 xmlXPathContextPtr xpath_ctx = NULL;
10761 xmlXPathObjectPtr result = NULL;
10762 char *encoded_structure = NULL;
10763 void *raw = NULL;
10764 size_t raw_length = 0;
10765 #endif
10767 if (!context) return IE_INVALID_CONTEXT;
10768 zfree(context->long_message);
10769 if (!message) return IE_INVAL;
10770 isds_message_free(message);
10772 #if HAVE_LIBCURL
10773 /* Do request and check for success */
10774 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10775 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10776 BAD_CAST "SignedMessageDownload",
10777 message_id, &response, NULL, NULL, &code, &status_message);
10778 if (err) goto leave;
10780 /* Find signed message, extract it into raw and maybe free
10781 * response */
10782 err = find_extract_signed_data_free_response(context,
10783 (xmlChar *)message_id, &response,
10784 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10785 BAD_CAST "SignedMessageDownload",
10786 &raw, &raw_length);
10787 if (err) goto leave;
10789 /* Parse message */
10790 err = isds_load_message(context,
10791 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10792 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10793 raw, raw_length, message, BUFFER_MOVE);
10794 if (err) goto leave;
10796 raw = NULL;
10798 leave:
10799 if (err) {
10800 isds_message_free(message);
10803 free(encoded_structure);
10804 xmlXPathFreeObject(result);
10805 xmlXPathFreeContext(xpath_ctx);
10806 free(raw);
10808 free(code);
10809 free(status_message);
10810 xmlFreeDoc(response);
10812 if (!err)
10813 isds_log(ILF_ISDS, ILL_DEBUG,
10814 (outgoing) ?
10815 _("SignedSentMessageDownload request processed by server "
10816 "successfully.\n") :
10817 _("SignedMessageDownload request processed by server "
10818 "successfully.\n")
10820 #else /* not HAVE_LIBCURL */
10821 err = IE_NOTSUP;
10822 #endif
10823 return err;
10827 /* Download signed incoming message identified by ID.
10828 * @context is session context
10829 * @message_id is message identifier (you can get them from
10830 * isds_get_list_of_received_messages())
10831 * @message is automatically reallocated message retrieved from ISDS. The raw
10832 * member will be filled with PKCS#7 structure in DER format. */
10833 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10834 const char *message_id, struct isds_message **message) {
10835 return isds_get_signed_message(context, 0, message_id, message);
10839 /* Download signed outgoing message identified by ID.
10840 * @context is session context
10841 * @message_id is message identifier (you can get them from
10842 * isds_get_list_of_sent_messages())
10843 * @message is automatically reallocated message retrieved from ISDS. The raw
10844 * member will be filled with PKCS#7 structure in DER format. */
10845 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10846 const char *message_id, struct isds_message **message) {
10847 return isds_get_signed_message(context, 1, message_id, message);
10851 /* Get type and name of user who sent a message identified by ID.
10852 * @context is session context
10853 * @message_id is message identifier
10854 * @sender_type is pointer to automatically allocated type of sender detected
10855 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10856 * library or to the server, NULL will be returned. Pass NULL if you don't
10857 * care about it.
10858 * @raw_sender_type is automatically reallocated UTF-8 string describing
10859 * sender type or NULL if not known to server. Pass NULL if you don't care.
10860 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10861 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10862 isds_error isds_get_message_sender(struct isds_ctx *context,
10863 const char *message_id, isds_sender_type **sender_type,
10864 char **raw_sender_type, char **sender_name) {
10865 isds_error err = IE_SUCCESS;
10866 #if HAVE_LIBCURL
10867 xmlDocPtr response = NULL;
10868 xmlChar *code = NULL, *status_message = NULL;
10869 xmlXPathContextPtr xpath_ctx = NULL;
10870 xmlXPathObjectPtr result = NULL;
10871 char *type_string = NULL;
10872 #endif
10874 if (!context) return IE_INVALID_CONTEXT;
10875 zfree(context->long_message);
10876 if (sender_type) zfree(*sender_type);
10877 if (raw_sender_type) zfree(*raw_sender_type);
10878 if (sender_name) zfree(*sender_name);
10879 if (!message_id) return IE_INVAL;
10881 #if HAVE_LIBCURL
10882 /* Do request and check for success */
10883 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10884 BAD_CAST "GetMessageAuthor",
10885 message_id, &response, NULL, NULL, &code, &status_message);
10886 if (err) goto leave;
10888 /* Extract data */
10889 xpath_ctx = xmlXPathNewContext(response);
10890 if (!xpath_ctx) {
10891 err = IE_ERROR;
10892 goto leave;
10894 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10895 err = IE_ERROR;
10896 goto leave;
10898 result = xmlXPathEvalExpression(
10899 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10900 if (!result) {
10901 err = IE_ERROR;
10902 goto leave;
10904 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10905 isds_log_message(context,
10906 _("Missing GetMessageAuthorResponse element"));
10907 err = IE_ISDS;
10908 goto leave;
10910 if (result->nodesetval->nodeNr > 1) {
10911 isds_log_message(context,
10912 _("Multiple GetMessageAuthorResponse element"));
10913 err = IE_ISDS;
10914 goto leave;
10916 xpath_ctx->node = result->nodesetval->nodeTab[0];
10917 xmlXPathFreeObject(result); result = NULL;
10919 /* Fill output arguments in */
10920 EXTRACT_STRING("isds:userType", type_string);
10921 if (NULL != type_string) {
10922 if (NULL != sender_type) {
10923 *sender_type = calloc(1, sizeof(**sender_type));
10924 if (NULL == *sender_type) {
10925 err = IE_NOMEM;
10926 goto leave;
10929 err = string2isds_sender_type((xmlChar *)type_string,
10930 *sender_type);
10931 if (err) {
10932 zfree(*sender_type);
10933 if (err == IE_ENUM) {
10934 err = IE_SUCCESS;
10935 char *type_string_locale = _isds_utf82locale(type_string);
10936 isds_log(ILF_ISDS, ILL_WARNING,
10937 _("Unknown isds:userType value: %s"),
10938 type_string_locale);
10939 free(type_string_locale);
10944 if (NULL != sender_name)
10945 EXTRACT_STRING("isds:authorName", *sender_name);
10947 leave:
10948 if (err) {
10949 if (NULL != sender_type) zfree(*sender_type);
10950 zfree(type_string);
10951 if (NULL != sender_name) zfree(*sender_name);
10953 if (NULL != raw_sender_type) *raw_sender_type = type_string;
10955 xmlXPathFreeObject(result);
10956 xmlXPathFreeContext(xpath_ctx);
10958 free(code);
10959 free(status_message);
10960 xmlFreeDoc(response);
10962 if (!err)
10963 isds_log(ILF_ISDS, ILL_DEBUG,
10964 _("GetMessageAuthor request processed by server "
10965 "successfully.\n"));
10966 #else /* not HAVE_LIBCURL */
10967 err = IE_NOTSUP;
10968 #endif
10969 return err;
10973 /* Retrieve hash of message identified by ID stored in ISDS.
10974 * @context is session context
10975 * @message_id is message identifier
10976 * @hash is automatically reallocated message hash downloaded from ISDS.
10977 * Message must exist in system and must not be deleted. */
10978 isds_error isds_download_message_hash(struct isds_ctx *context,
10979 const char *message_id, struct isds_hash **hash) {
10981 isds_error err = IE_SUCCESS;
10982 #if HAVE_LIBCURL
10983 xmlDocPtr response = NULL;
10984 xmlChar *code = NULL, *status_message = NULL;
10985 xmlXPathContextPtr xpath_ctx = NULL;
10986 xmlXPathObjectPtr result = NULL;
10987 #endif
10989 if (!context) return IE_INVALID_CONTEXT;
10990 zfree(context->long_message);
10992 isds_hash_free(hash);
10994 #if HAVE_LIBCURL
10995 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10996 BAD_CAST "VerifyMessage", message_id,
10997 &response, NULL, NULL, &code, &status_message);
10998 if (err) goto leave;
11001 /* Extract data */
11002 xpath_ctx = xmlXPathNewContext(response);
11003 if (!xpath_ctx) {
11004 err = IE_ERROR;
11005 goto leave;
11007 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11008 err = IE_ERROR;
11009 goto leave;
11011 result = xmlXPathEvalExpression(
11012 BAD_CAST "/isds:VerifyMessageResponse",
11013 xpath_ctx);
11014 if (!result) {
11015 err = IE_ERROR;
11016 goto leave;
11018 /* Empty response */
11019 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11020 char *message_id_locale = _isds_utf82locale((char*) message_id);
11021 isds_printf_message(context,
11022 _("Server did not return any response for ID `%s' "
11023 "on VerifyMessage request"), message_id_locale);
11024 free(message_id_locale);
11025 err = IE_ISDS;
11026 goto leave;
11028 /* More responses */
11029 if (result->nodesetval->nodeNr > 1) {
11030 char *message_id_locale = _isds_utf82locale((char*) message_id);
11031 isds_printf_message(context,
11032 _("Server did return more responses for ID `%s' "
11033 "on VerifyMessage request"), message_id_locale);
11034 free(message_id_locale);
11035 err = IE_ISDS;
11036 goto leave;
11038 /* One response */
11039 xpath_ctx->node = result->nodesetval->nodeTab[0];
11041 /* Extract the hash */
11042 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11044 leave:
11045 if (err) {
11046 isds_hash_free(hash);
11049 xmlXPathFreeObject(result);
11050 xmlXPathFreeContext(xpath_ctx);
11052 free(code);
11053 free(status_message);
11054 xmlFreeDoc(response);
11056 if (!err)
11057 isds_log(ILF_ISDS, ILL_DEBUG,
11058 _("VerifyMessage request processed by server "
11059 "successfully.\n")
11061 #else /* not HAVE_LIBCURL */
11062 err = IE_NOTSUP;
11063 #endif
11064 return err;
11068 /* Erase message specified by @message_id from long term storage. Other
11069 * message cannot be erased on user request.
11070 * @context is session context
11071 * @message_id is message identifier.
11072 * @incoming is true for incoming message, false for outgoing message.
11073 * @return
11074 * IE_SUCCESS if message has ben removed
11075 * IE_INVAL if message does not exist in long term storage or message
11076 * belongs to different box
11077 * TODO: IE_NOEPRM if user has no permission to erase a message */
11078 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11079 const char *message_id, _Bool incoming) {
11080 isds_error err = IE_SUCCESS;
11081 #if HAVE_LIBCURL
11082 xmlNodePtr request = NULL, node;
11083 xmlNsPtr isds_ns = NULL;
11084 xmlDocPtr response = NULL;
11085 xmlChar *code = NULL, *status_message = NULL;
11086 #endif
11088 if (!context) return IE_INVALID_CONTEXT;
11089 zfree(context->long_message);
11090 if (NULL == message_id) return IE_INVAL;
11092 #if HAVE_LIBCURL
11093 /* Check if connection is established
11094 * TODO: This check should be done downstairs. */
11095 if (!context->curl) return IE_CONNECTION_CLOSED;
11097 /* Build request */
11098 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11099 if (!request) {
11100 isds_log_message(context,
11101 _("Could build EraseMessage request"));
11102 return IE_ERROR;
11104 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11105 if(!isds_ns) {
11106 isds_log_message(context, _("Could not create ISDS name space"));
11107 xmlFreeNode(request);
11108 return IE_ERROR;
11110 xmlSetNs(request, isds_ns);
11112 err = validate_message_id_length(context, (xmlChar *) message_id);
11113 if (err) goto leave;
11114 INSERT_STRING(request, "dmID", message_id);
11116 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11119 /* Send request */
11120 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11121 "message ID %s to ISDS\n"), message_id);
11122 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11123 xmlFreeNode(request); request = NULL;
11125 if (err) {
11126 isds_log(ILF_ISDS, ILL_DEBUG,
11127 _("Processing ISDS response on EraseMessage request "
11128 "failed\n"));
11129 goto leave;
11132 /* Check for response status */
11133 err = isds_response_status(context, SERVICE_DM_INFO, response,
11134 &code, &status_message, NULL);
11135 if (err) {
11136 isds_log(ILF_ISDS, ILL_DEBUG,
11137 _("ISDS response on EraseMessage request is missing "
11138 "status\n"));
11139 goto leave;
11142 /* Check server status code */
11143 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11144 isds_log_message(context, _("Message to erase belongs to other box"));
11145 err = IE_INVAL;
11146 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11147 isds_log_message(context, _("Message to erase is not saved in "
11148 "long term storage or the direction does not match"));
11149 err = IE_INVAL;
11150 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11151 char *code_locale = _isds_utf82locale((char*) code);
11152 char *message_locale = _isds_utf82locale((char*) status_message);
11153 isds_log(ILF_ISDS, ILL_DEBUG,
11154 _("Server refused EraseMessage request "
11155 "(code=%s, message=%s)\n"),
11156 code_locale, message_locale);
11157 isds_log_message(context, message_locale);
11158 free(code_locale);
11159 free(message_locale);
11160 err = IE_ISDS;
11161 goto leave;
11164 leave:
11165 free(code);
11166 free(status_message);
11167 xmlFreeDoc(response);
11168 xmlFreeNode(request);
11170 if (!err)
11171 isds_log(ILF_ISDS, ILL_DEBUG,
11172 _("EraseMessage request processed by server "
11173 "successfully.\n")
11175 #else /* not HAVE_LIBCURL */
11176 err = IE_NOTSUP;
11177 #endif
11178 return err;
11182 /* Mark message as read. This is a transactional commit function to acknowledge
11183 * to ISDS the message has been downloaded and processed by client properly.
11184 * @context is session context
11185 * @message_id is message identifier. */
11186 isds_error isds_mark_message_read(struct isds_ctx *context,
11187 const char *message_id) {
11189 isds_error err = IE_SUCCESS;
11190 #if HAVE_LIBCURL
11191 xmlDocPtr response = NULL;
11192 xmlChar *code = NULL, *status_message = NULL;
11193 #endif
11195 if (!context) return IE_INVALID_CONTEXT;
11196 zfree(context->long_message);
11198 #if HAVE_LIBCURL
11199 /* Do request and check for success */
11200 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11201 BAD_CAST "MarkMessageAsDownloaded", message_id,
11202 &response, NULL, NULL, &code, &status_message);
11204 free(code);
11205 free(status_message);
11206 xmlFreeDoc(response);
11208 if (!err)
11209 isds_log(ILF_ISDS, ILL_DEBUG,
11210 _("MarkMessageAsDownloaded request processed by server "
11211 "successfully.\n")
11213 #else /* not HAVE_LIBCURL */
11214 err = IE_NOTSUP;
11215 #endif
11216 return err;
11220 /* Mark message as received by recipient. This is applicable only to
11221 * commercial message. Use envelope->dmType message member to distinguish
11222 * commercial message from government message. Government message is
11223 * received automatically (by law), commercial message on recipient request.
11224 * @context is session context
11225 * @message_id is message identifier. */
11226 isds_error isds_mark_message_received(struct isds_ctx *context,
11227 const char *message_id) {
11229 isds_error err = IE_SUCCESS;
11230 #if HAVE_LIBCURL
11231 xmlDocPtr response = NULL;
11232 xmlChar *code = NULL, *status_message = NULL;
11233 #endif
11235 if (!context) return IE_INVALID_CONTEXT;
11236 zfree(context->long_message);
11238 #if HAVE_LIBCURL
11239 /* Do request and check for success */
11240 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11241 BAD_CAST "ConfirmDelivery", message_id,
11242 &response, NULL, NULL, &code, &status_message);
11244 free(code);
11245 free(status_message);
11246 xmlFreeDoc(response);
11248 if (!err)
11249 isds_log(ILF_ISDS, ILL_DEBUG,
11250 _("ConfirmDelivery request processed by server "
11251 "successfully.\n")
11253 #else /* not HAVE_LIBCURL */
11254 err = IE_NOTSUP;
11255 #endif
11256 return err;
11260 /* Send document for authorized conversion into Czech POINT system.
11261 * This is public anonymous service, no log-in necessary. Special context is
11262 * used to reuse keep-a-live HTTPS connection.
11263 * @context is Czech POINT session context. DO NOT use context connected to
11264 * ISDS server. Use new context or context used by this function previously.
11265 * @document is document to convert. Only data, data_length, dmFileDescr and
11266 * is_xml members are significant. Be ware that not all document formats can be
11267 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11268 * @id is reallocated identifier assigned by Czech POINT system to
11269 * your document on submit. Use is to tell it to Czech POINT officer.
11270 * @date is reallocated document submit date (submitted documents
11271 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11272 * value. */
11273 isds_error czp_convert_document(struct isds_ctx *context,
11274 const struct isds_document *document,
11275 char **id, struct tm **date) {
11276 isds_error err = IE_SUCCESS;
11277 #if HAVE_LIBCURL
11278 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11279 xmlNodePtr request = NULL, node;
11280 xmlDocPtr response = NULL;
11282 xmlXPathContextPtr xpath_ctx = NULL;
11283 xmlXPathObjectPtr result = NULL;
11284 long int status = -1;
11285 long int *status_ptr = &status;
11286 char *string = NULL;
11287 #endif
11290 if (!context) return IE_INVALID_CONTEXT;
11291 zfree(context->long_message);
11292 if (!document || !id || !date) return IE_INVAL;
11294 if (document->is_xml) {
11295 isds_log_message(context,
11296 _("XML documents cannot be submitted to conversion"));
11297 return IE_NOTSUP;
11300 /* Free output arguments */
11301 zfree(*id);
11302 zfree(*date);
11304 #if HAVE_LIBCURL
11305 /* Store configuration */
11306 context->type = CTX_TYPE_CZP;
11307 free(context->url);
11308 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11309 if (!(context->url))
11310 return IE_NOMEM;
11312 /* Prepare CURL handle if not yet connected */
11313 if (!context->curl) {
11314 context->curl = curl_easy_init();
11315 if (!(context->curl))
11316 return IE_ERROR;
11319 /* Build conversion request */
11320 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11321 if (!request) {
11322 isds_log_message(context,
11323 _("Could not build Czech POINT conversion request"));
11324 return IE_ERROR;
11326 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11327 if(!deposit_ns) {
11328 isds_log_message(context,
11329 _("Could not create Czech POINT deposit name space"));
11330 xmlFreeNode(request);
11331 return IE_ERROR;
11333 xmlSetNs(request, deposit_ns);
11335 /* Insert children. They are in empty namespace! */
11336 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11337 if(!empty_ns) {
11338 isds_log_message(context, _("Could not create empty name space"));
11339 err = IE_ERROR;
11340 goto leave;
11342 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11343 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11344 document->dmFileDescr);
11346 /* Document encoded in Base64 */
11347 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11348 document->data, document->data_length);
11349 if (err) goto leave;
11351 isds_log(ILF_ISDS, ILL_DEBUG,
11352 _("Submitting document for conversion into Czech POINT deposit"));
11354 /* Send conversion request */
11355 err = _czp_czpdeposit(context, request, &response);
11356 xmlFreeNode(request); request = NULL;
11358 if (err) {
11359 czp_do_close_connection(context);
11360 goto leave;
11364 /* Extract response */
11365 xpath_ctx = xmlXPathNewContext(response);
11366 if (!xpath_ctx) {
11367 err = IE_ERROR;
11368 goto leave;
11370 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11371 err = IE_ERROR;
11372 goto leave;
11374 result = xmlXPathEvalExpression(
11375 BAD_CAST "/deposit:saveDocumentResponse/return",
11376 xpath_ctx);
11377 if (!result) {
11378 err = IE_ERROR;
11379 goto leave;
11381 /* Empty response */
11382 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11383 isds_printf_message(context,
11384 _("Missing `return' element in Czech POINT deposit response"));
11385 err = IE_ISDS;
11386 goto leave;
11388 /* More responses */
11389 if (result->nodesetval->nodeNr > 1) {
11390 isds_printf_message(context,
11391 _("Multiple `return' element in Czech POINT deposit response"));
11392 err = IE_ISDS;
11393 goto leave;
11395 /* One response */
11396 xpath_ctx->node = result->nodesetval->nodeTab[0];
11398 /* Get status */
11399 EXTRACT_LONGINT("status", status_ptr, 1);
11400 if (status) {
11401 EXTRACT_STRING("statusMsg", string);
11402 char *string_locale = _isds_utf82locale(string);
11403 isds_printf_message(context,
11404 _("Czech POINT deposit refused document for conversion "
11405 "(code=%ld, message=%s)"),
11406 status, string_locale);
11407 free(string_locale);
11408 err = IE_ISDS;
11409 goto leave;
11412 /* Get document ID */
11413 EXTRACT_STRING("documentID", *id);
11415 /* Get submit date */
11416 EXTRACT_STRING("dateInserted", string);
11417 if (string) {
11418 *date = calloc(1, sizeof(**date));
11419 if (!*date) {
11420 err = IE_NOMEM;
11421 goto leave;
11423 err = _isds_datestring2tm((xmlChar *)string, *date);
11424 if (err) {
11425 if (err == IE_NOTSUP) {
11426 err = IE_ISDS;
11427 char *string_locale = _isds_utf82locale(string);
11428 isds_printf_message(context,
11429 _("Invalid dateInserted value: %s"), string_locale);
11430 free(string_locale);
11432 goto leave;
11436 leave:
11437 free(string);
11438 xmlXPathFreeObject(result);
11439 xmlXPathFreeContext(xpath_ctx);
11441 xmlFreeDoc(response);
11442 xmlFreeNode(request);
11444 if (!err) {
11445 char *id_locale = _isds_utf82locale((char *) *id);
11446 isds_log(ILF_ISDS, ILL_DEBUG,
11447 _("Document %s has been submitted for conversion "
11448 "to server successfully\n"), id_locale);
11449 free(id_locale);
11451 #else /* not HAVE_LIBCURL */
11452 err = IE_NOTSUP;
11453 #endif
11454 return err;
11458 /* Close possibly opened connection to Czech POINT document deposit.
11459 * @context is Czech POINT session context. */
11460 isds_error czp_close_connection(struct isds_ctx *context) {
11461 if (!context) return IE_INVALID_CONTEXT;
11462 zfree(context->long_message);
11463 #if HAVE_LIBCURL
11464 return czp_do_close_connection(context);
11465 #else
11466 return IE_NOTSUP;
11467 #endif
11471 /* Send request for new box creation in testing ISDS instance.
11472 * It's not possible to request for a production box currently, as it
11473 * communicates via e-mail.
11474 * XXX: This function does not work either. Server complains about invalid
11475 * e-mail address.
11476 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11477 * this function
11478 * @context is special session context for box creation request. DO NOT use
11479 * standard context as it could reveal your password. Use fresh new context or
11480 * context previously used by this function.
11481 * @box is box description to create including single primary user (in case of
11482 * FO box type). It outputs box ID assigned by ISDS in dbID element.
11483 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11484 * box, or contact address of PFO box owner). The email member is mandatory as
11485 * it will be used to deliver credentials.
11486 * @former_names is former name of box owner. Pass NULL if you don't care.
11487 * @approval is optional external approval of box manipulation
11488 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11489 * NULL, if you don't care.*/
11490 isds_error isds_request_new_testing_box(struct isds_ctx *context,
11491 struct isds_DbOwnerInfo *box, const struct isds_list *users,
11492 const char *former_names, const struct isds_approval *approval,
11493 char **refnumber) {
11494 isds_error err = IE_SUCCESS;
11495 #if HAVE_LIBCURL
11496 xmlNodePtr request = NULL;
11497 xmlDocPtr response = NULL;
11498 xmlXPathContextPtr xpath_ctx = NULL;
11499 xmlXPathObjectPtr result = NULL;
11500 #endif
11503 if (!context) return IE_INVALID_CONTEXT;
11504 zfree(context->long_message);
11505 if (!box) return IE_INVAL;
11507 #if HAVE_LIBCURL
11508 if (!box->email || box->email[0] == '\0') {
11509 isds_log_message(context, _("E-mail field is mandatory"));
11510 return IE_INVAL;
11513 /* Scratch box ID */
11514 zfree(box->dbID);
11516 /* Store configuration */
11517 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
11518 free(context->url);
11519 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
11520 if (!(context->url))
11521 return IE_NOMEM;
11523 /* Prepare CURL handle if not yet connected */
11524 if (!context->curl) {
11525 context->curl = curl_easy_init();
11526 if (!(context->curl))
11527 return IE_ERROR;
11530 /* Build CreateDataBox request */
11531 err = build_CreateDBInput_request(context,
11532 &request, BAD_CAST "CreateDataBox",
11533 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
11534 if (err) goto leave;
11536 /* Send it to server and process response */
11537 err = send_destroy_request_check_response(context,
11538 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
11539 &response, (xmlChar **) refnumber, NULL);
11540 if (err) goto leave;
11542 /* Extract box ID */
11543 xpath_ctx = xmlXPathNewContext(response);
11544 if (!xpath_ctx) {
11545 err = IE_ERROR;
11546 goto leave;
11548 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11549 err = IE_ERROR;
11550 goto leave;
11552 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
11554 leave:
11555 xmlXPathFreeObject(result);
11556 xmlXPathFreeContext(xpath_ctx);
11557 xmlFreeDoc(response);
11558 xmlFreeNode(request);
11560 if (!err) {
11561 isds_log(ILF_ISDS, ILL_DEBUG,
11562 _("CreateDataBox request processed by server successfully.\n"));
11564 #else /* not HAVE_LIBCURL */
11565 err = IE_NOTSUP;
11566 #endif
11568 return err;
11572 /* Submit CMS signed message to ISDS to verify its originality. This is
11573 * stronger form of isds_verify_message_hash() because ISDS does more checks
11574 * than simple one (potentialy old weak) hash comparison.
11575 * @context is session context
11576 * @message is memory with raw CMS signed message bit stream
11577 * @length is @message size in bytes
11578 * @return
11579 * IE_SUCCESS if message originates in ISDS
11580 * IE_NOTEQUAL if message is unknown to ISDS
11581 * other code for other errors */
11582 isds_error isds_authenticate_message(struct isds_ctx *context,
11583 const void *message, size_t length) {
11584 isds_error err = IE_SUCCESS;
11585 #if HAVE_LIBCURL
11586 xmlNsPtr isds_ns = NULL;
11587 xmlNodePtr request = NULL;
11588 xmlDocPtr response = NULL;
11589 xmlXPathContextPtr xpath_ctx = NULL;
11590 xmlXPathObjectPtr result = NULL;
11591 _Bool *authentic = NULL;
11592 #endif
11594 if (!context) return IE_INVALID_CONTEXT;
11595 zfree(context->long_message);
11596 if (!message || length == 0) return IE_INVAL;
11598 #if HAVE_LIBCURL
11599 /* Check if connection is established
11600 * TODO: This check should be done downstairs. */
11601 if (!context->curl) return IE_CONNECTION_CLOSED;
11604 /* Build AuthenticateMessage request */
11605 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11606 if (!request) {
11607 isds_log_message(context,
11608 _("Could not build AuthenticateMessage request"));
11609 return IE_ERROR;
11611 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11612 if(!isds_ns) {
11613 isds_log_message(context, _("Could not create ISDS name space"));
11614 xmlFreeNode(request);
11615 return IE_ERROR;
11617 xmlSetNs(request, isds_ns);
11619 /* Insert Base64 encoded message */
11620 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11621 message, length);
11622 if (err) goto leave;
11624 /* Send request to server and process response */
11625 err = send_destroy_request_check_response(context,
11626 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11627 &response, NULL, NULL);
11628 if (err) goto leave;
11631 /* ISDS has decided */
11632 xpath_ctx = xmlXPathNewContext(response);
11633 if (!xpath_ctx) {
11634 err = IE_ERROR;
11635 goto leave;
11637 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11638 err = IE_ERROR;
11639 goto leave;
11642 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11644 if (!authentic) {
11645 isds_log_message(context,
11646 _("Server did not return any response on "
11647 "AuthenticateMessage request"));
11648 err = IE_ISDS;
11649 goto leave;
11651 if (*authentic) {
11652 isds_log(ILF_ISDS, ILL_DEBUG,
11653 _("ISDS authenticated the message successfully\n"));
11654 } else {
11655 isds_log_message(context, _("ISDS does not know the message"));
11656 err = IE_NOTEQUAL;
11660 leave:
11661 free(authentic);
11662 xmlXPathFreeObject(result);
11663 xmlXPathFreeContext(xpath_ctx);
11665 xmlFreeDoc(response);
11666 xmlFreeNode(request);
11667 #else /* not HAVE_LIBCURL */
11668 err = IE_NOTSUP;
11669 #endif
11671 return err;
11675 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11676 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11677 * be re-signed.
11678 * @context is session context
11679 * @input_data is memory with raw CMS signed message or delivery info bit
11680 * stream to re-sign
11681 * @input_length is @input_data size in bytes
11682 * @output_data is pointer to auto-allocated memory where to store re-signed
11683 * input data blob. Caller must free it.
11684 * @output_data is pointer where to store @output_data size in bytes
11685 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11686 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11687 * @return
11688 * IE_SUCCESS if CMS blob has been re-signed successfully
11689 * other code for other errors */
11690 isds_error isds_resign_message(struct isds_ctx *context,
11691 const void *input_data, size_t input_length,
11692 void **output_data, size_t *output_length, struct tm **valid_to) {
11693 isds_error err = IE_SUCCESS;
11694 #if HAVE_LIBCURL
11695 xmlNsPtr isds_ns = NULL;
11696 xmlNodePtr request = NULL;
11697 xmlDocPtr response = NULL;
11698 xmlXPathContextPtr xpath_ctx = NULL;
11699 xmlXPathObjectPtr result = NULL;
11700 char *string = NULL;
11701 const xmlChar *codes[] = {
11702 BAD_CAST "2200",
11703 BAD_CAST "2201",
11704 BAD_CAST "2204",
11705 BAD_CAST "2207",
11706 NULL
11708 const char *meanings[] = {
11709 "Message is bad",
11710 "Message is not original",
11711 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11712 "Time stamp could not been generated in time"
11714 const isds_error errors[] = {
11715 IE_INVAL,
11716 IE_NOTUNIQ,
11717 IE_INVAL,
11718 IE_ISDS,
11720 struct code_map_isds_error map = {
11721 .codes = codes,
11722 .meanings = meanings,
11723 .errors = errors
11725 #endif
11727 if (NULL != output_data) *output_data = NULL;
11728 if (NULL != output_length) *output_length = 0;
11729 if (NULL != valid_to) *valid_to = NULL;
11731 if (NULL == context) return IE_INVALID_CONTEXT;
11732 zfree(context->long_message);
11733 if (NULL == input_data || 0 == input_length) {
11734 isds_log_message(context, _("Empty CMS blob on input"));
11735 return IE_INVAL;
11737 if (NULL == output_data || NULL == output_length) {
11738 isds_log_message(context,
11739 _("NULL pointer provided for output CMS blob"));
11740 return IE_INVAL;
11743 #if HAVE_LIBCURL
11744 /* Check if connection is established
11745 * TODO: This check should be done downstairs. */
11746 if (!context->curl) return IE_CONNECTION_CLOSED;
11749 /* Build Re-signISDSDocument request */
11750 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11751 if (!request) {
11752 isds_log_message(context,
11753 _("Could not build Re-signISDSDocument request"));
11754 return IE_ERROR;
11756 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11757 if(!isds_ns) {
11758 isds_log_message(context, _("Could not create ISDS name space"));
11759 xmlFreeNode(request);
11760 return IE_ERROR;
11762 xmlSetNs(request, isds_ns);
11764 /* Insert Base64 encoded CMS blob */
11765 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11766 input_data, input_length);
11767 if (err) goto leave;
11769 /* Send request to server and process response */
11770 err = send_destroy_request_check_response(context,
11771 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11772 &response, NULL, &map);
11773 if (err) goto leave;
11776 /* Extract re-signed data */
11777 xpath_ctx = xmlXPathNewContext(response);
11778 if (!xpath_ctx) {
11779 err = IE_ERROR;
11780 goto leave;
11782 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11783 err = IE_ERROR;
11784 goto leave;
11786 result = xmlXPathEvalExpression(
11787 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11788 if (!result) {
11789 err = IE_ERROR;
11790 goto leave;
11792 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11793 isds_log_message(context,
11794 _("Missing Re-signISDSDocumentResponse element"));
11795 err = IE_ISDS;
11796 goto leave;
11798 if (result->nodesetval->nodeNr > 1) {
11799 isds_log_message(context,
11800 _("Multiple Re-signISDSDocumentResponse element"));
11801 err = IE_ISDS;
11802 goto leave;
11804 xpath_ctx->node = result->nodesetval->nodeTab[0];
11805 xmlXPathFreeObject(result); result = NULL;
11807 EXTRACT_STRING("isds:dmResultDoc", string);
11808 /* Decode non-empty data */
11809 if (NULL != string && string[0] != '\0') {
11810 *output_length = _isds_b64decode(string, output_data);
11811 if (*output_length == (size_t) -1) {
11812 isds_log_message(context,
11813 _("Error while Base64-decoding re-signed data"));
11814 err = IE_ERROR;
11815 goto leave;
11817 } else {
11818 isds_log_message(context, _("Server did not send re-signed data"));
11819 err = IE_ISDS;
11820 goto leave;
11822 zfree(string);
11824 if (NULL != valid_to) {
11825 /* Get time stamp expiration date */
11826 EXTRACT_STRING("isds:dmValidTo", string);
11827 if (NULL != string) {
11828 *valid_to = calloc(1, sizeof(**valid_to));
11829 if (!*valid_to) {
11830 err = IE_NOMEM;
11831 goto leave;
11833 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11834 if (err) {
11835 if (err == IE_NOTSUP) {
11836 err = IE_ISDS;
11837 char *string_locale = _isds_utf82locale(string);
11838 isds_printf_message(context,
11839 _("Invalid dmValidTo value: %s"), string_locale);
11840 free(string_locale);
11842 goto leave;
11847 leave:
11848 free(string);
11850 xmlXPathFreeObject(result);
11851 xmlXPathFreeContext(xpath_ctx);
11853 xmlFreeDoc(response);
11854 xmlFreeNode(request);
11855 #else /* not HAVE_LIBCURL */
11856 err = IE_NOTSUP;
11857 #endif
11859 return err;
11862 #undef INSERT_ELEMENT
11863 #undef CHECK_FOR_STRING_LENGTH
11864 #undef INSERT_STRING_ATTRIBUTE
11865 #undef INSERT_ULONGINTNOPTR
11866 #undef INSERT_ULONGINT
11867 #undef INSERT_LONGINT
11868 #undef INSERT_BOOLEAN
11869 #undef INSERT_SCALAR_BOOLEAN
11870 #undef INSERT_STRING
11871 #undef INSERT_STRING_WITH_NS
11872 #undef EXTRACT_STRING_ATTRIBUTE
11873 #undef EXTRACT_ULONGINT
11874 #undef EXTRACT_LONGINT
11875 #undef EXTRACT_BOOLEAN
11876 #undef EXTRACT_STRING
11879 /* Compute hash of message from raw representation and store it into envelope.
11880 * Original hash structure will be destroyed in envelope.
11881 * @context is session context
11882 * @message is message carrying raw XML message blob
11883 * @algorithm is desired hash algorithm to use */
11884 isds_error isds_compute_message_hash(struct isds_ctx *context,
11885 struct isds_message *message, const isds_hash_algorithm algorithm) {
11886 isds_error err = IE_SUCCESS;
11887 const char *nsuri;
11888 void *xml_stream = NULL;
11889 size_t xml_stream_length;
11890 size_t phys_start, phys_end;
11891 char *phys_path = NULL;
11892 struct isds_hash *new_hash = NULL;
11895 if (!context) return IE_INVALID_CONTEXT;
11896 zfree(context->long_message);
11897 if (!message) return IE_INVAL;
11899 if (!message->raw) {
11900 isds_log_message(context,
11901 _("Message does not carry raw representation"));
11902 return IE_INVAL;
11905 switch (message->raw_type) {
11906 case RAWTYPE_INCOMING_MESSAGE:
11907 nsuri = ISDS_NS;
11908 xml_stream = message->raw;
11909 xml_stream_length = message->raw_length;
11910 break;
11912 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11913 nsuri = SISDS_INCOMING_NS;
11914 xml_stream = message->raw;
11915 xml_stream_length = message->raw_length;
11916 break;
11918 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11919 nsuri = SISDS_INCOMING_NS;
11920 err = _isds_extract_cms_data(context,
11921 message->raw, message->raw_length,
11922 &xml_stream, &xml_stream_length);
11923 if (err) goto leave;
11924 break;
11926 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11927 nsuri = SISDS_OUTGOING_NS;
11928 xml_stream = message->raw;
11929 xml_stream_length = message->raw_length;
11930 break;
11932 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11933 nsuri = SISDS_OUTGOING_NS;
11934 err = _isds_extract_cms_data(context,
11935 message->raw, message->raw_length,
11936 &xml_stream, &xml_stream_length);
11937 if (err) goto leave;
11938 break;
11940 default:
11941 isds_log_message(context, _("Bad raw representation type"));
11942 return IE_INVAL;
11943 break;
11947 /* XXX: Hash is computed from original string representing isds:dmDm
11948 * subtree. That means no encoding, white space, xmlns attributes changes.
11949 * In other words, input for hash can be invalid XML stream. */
11950 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
11951 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11952 PHYSXML_ELEMENT_SEPARATOR,
11953 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
11954 PHYSXML_ELEMENT_SEPARATOR
11955 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
11956 err = IE_NOMEM;
11957 goto leave;
11959 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11960 phys_path, &phys_start, &phys_end);
11961 zfree(phys_path);
11962 if (err) {
11963 isds_log_message(context,
11964 _("Substring with isds:dmDM element could not be located "
11965 "in raw message"));
11966 goto leave;
11970 /* Compute hash */
11971 new_hash = calloc(1, sizeof(*new_hash));
11972 if (!new_hash) {
11973 err = IE_NOMEM;
11974 goto leave;
11976 new_hash->algorithm = algorithm;
11977 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
11978 new_hash);
11979 if (err) {
11980 isds_log_message(context, _("Could not compute message hash"));
11981 goto leave;
11984 /* Save computed hash */
11985 if (!message->envelope) {
11986 message->envelope = calloc(1, sizeof(*message->envelope));
11987 if (!message->envelope) {
11988 err = IE_NOMEM;
11989 goto leave;
11992 isds_hash_free(&message->envelope->hash);
11993 message->envelope->hash = new_hash;
11995 leave:
11996 if (err) {
11997 isds_hash_free(&new_hash);
12000 free(phys_path);
12001 if (xml_stream != message->raw) free(xml_stream);
12002 return err;
12006 /* Compare two hashes.
12007 * @h1 is first hash
12008 * @h2 is another hash
12009 * @return
12010 * IE_SUCCESS if hashes equal
12011 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12012 * IE_ENUM if not comparable, but both structures defined
12013 * IE_INVAL if some of the structures are undefined (NULL)
12014 * IE_ERROR if internal error occurs */
12015 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
12016 if (h1 == NULL || h2 == NULL) return IE_INVAL;
12017 if (h1->algorithm != h2->algorithm) return IE_ENUM;
12018 if (h1->length != h2->length) return IE_ERROR;
12019 if (h1->length > 0 && !h1->value) return IE_ERROR;
12020 if (h2->length > 0 && !h2->value) return IE_ERROR;
12022 for (size_t i = 0; i < h1->length; i++) {
12023 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12024 return IE_NOTEQUAL;
12026 return IE_SUCCESS;
12030 /* Check message has gone through ISDS by comparing message hash stored in
12031 * ISDS and locally computed hash. You must provide message with valid raw
12032 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12033 * This is convenient wrapper for isds_download_message_hash(),
12034 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12035 * @context is session context
12036 * @message is message with valid raw and envelope member; envelope->hash
12037 * member will be changed during function run. Use envelope on heap only.
12038 * @return
12039 * IE_SUCCESS if message originates in ISDS
12040 * IE_NOTEQUAL if message is unknown to ISDS
12041 * other code for other errors */
12042 isds_error isds_verify_message_hash(struct isds_ctx *context,
12043 struct isds_message *message) {
12044 isds_error err = IE_SUCCESS;
12045 struct isds_hash *downloaded_hash = NULL;
12047 if (!context) return IE_INVALID_CONTEXT;
12048 zfree(context->long_message);
12049 if (!message) return IE_INVAL;
12051 if (!message->envelope) {
12052 isds_log_message(context,
12053 _("Given message structure is missing envelope"));
12054 return IE_INVAL;
12056 if (!message->raw) {
12057 isds_log_message(context,
12058 _("Given message structure is missing raw representation"));
12059 return IE_INVAL;
12062 err = isds_download_message_hash(context, message->envelope->dmID,
12063 &downloaded_hash);
12064 if (err) goto leave;
12066 err = isds_compute_message_hash(context, message,
12067 downloaded_hash->algorithm);
12068 if (err) goto leave;
12070 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12072 leave:
12073 isds_hash_free(&downloaded_hash);
12074 return err;
12078 /* Search for document by document ID in list of documents. IDs are compared
12079 * as UTF-8 string.
12080 * @documents is list of isds_documents
12081 * @id is document identifier
12082 * @return first matching document or NULL. */
12083 const struct isds_document *isds_find_document_by_id(
12084 const struct isds_list *documents, const char *id) {
12085 const struct isds_list *item;
12086 const struct isds_document *document;
12088 for (item = documents; item; item = item->next) {
12089 document = (struct isds_document *) item->data;
12090 if (!document) continue;
12092 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12093 return document;
12096 return NULL;
12100 /* Normalize @mime_type to be proper MIME type.
12101 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12102 * guess regular MIME type (e.g. "application/pdf").
12103 * @mime_type is UTF-8 encoded MIME type to fix
12104 * @return original @mime_type if no better interpretation exists, or
12105 * constant static UTF-8 encoded string with proper MIME type. */
12106 const char *isds_normalize_mime_type(const char *mime_type) {
12107 if (!mime_type) return NULL;
12109 for (size_t offset = 0;
12110 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12111 offset += 2) {
12112 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12113 extension_map_mime[offset]))
12114 return (const char *) extension_map_mime[offset + 1];
12117 return mime_type;
12121 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12122 struct isds_message **message);
12123 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12124 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12125 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12126 struct isds_address **address);
12128 int isds_message_free(struct isds_message **message);
12129 int isds_address_free(struct isds_address **address);
12133 /* Makes known all relevant namespaces to given XPath context
12134 * @xpath_ctx is XPath context
12135 * @message_ns selects proper message name space. Unsigned and signed
12136 * messages and delivery info's differ in prefix and URI. */
12137 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12138 const message_ns_type message_ns) {
12139 const xmlChar *message_namespace = NULL;
12141 if (!xpath_ctx) return IE_ERROR;
12143 switch(message_ns) {
12144 case MESSAGE_NS_1:
12145 message_namespace = BAD_CAST ISDS1_NS; break;
12146 case MESSAGE_NS_UNSIGNED:
12147 message_namespace = BAD_CAST ISDS_NS; break;
12148 case MESSAGE_NS_SIGNED_INCOMING:
12149 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12150 case MESSAGE_NS_SIGNED_OUTGOING:
12151 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12152 case MESSAGE_NS_SIGNED_DELIVERY:
12153 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12154 default:
12155 return IE_ENUM;
12158 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12159 return IE_ERROR;
12160 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12161 return IE_ERROR;
12162 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12163 return IE_ERROR;
12164 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12165 return IE_ERROR;
12166 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12167 return IE_ERROR;
12168 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12169 return IE_ERROR;
12170 return IE_SUCCESS;