Update comment at isds_get_box_list_archive()
[libisds.git] / src / isds.c
blob726ac58ac61c7f7157d38a241b950e3316d1b901
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 "%1$sas/processLogin?type=hotp&uri=%1$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 "%1$sas/processLogin?type=totp&sendSms=true&"
1372 "uri=%1$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 "%1$sas/processLogin?type=totp&"
1380 "uri=%1$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))
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 "%1$.*2$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 "%1$.*2$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), POA (active boxes allowing receiving commercial
7065 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7066 * commercial messages). This argument is a string because specification
7067 * states new values can appear in the future. Not all list types are
7068 * available to all users.
7069 * @buffer is automatically reallocated memory to store the list of boxes. The
7070 * list is zipped CSV file.
7071 * @buffer_length is size of @buffer data in bytes.
7072 * In case of error @buffer will be freed and @buffer_length will be
7073 * undefined.*/
7074 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7075 const char *list_identifier, void **buffer, size_t *buffer_length) {
7076 isds_error err = IE_SUCCESS;
7077 #if HAVE_LIBCURL
7078 xmlNsPtr isds_ns = NULL;
7079 xmlNodePtr request = NULL, node;
7080 xmlDocPtr response = NULL;
7081 xmlXPathContextPtr xpath_ctx = NULL;
7082 xmlXPathObjectPtr result = NULL;
7083 char *string = NULL;
7084 #endif
7087 if (!context) return IE_INVALID_CONTEXT;
7088 zfree(context->long_message);
7089 if (buffer) zfree(*buffer);
7090 if (!buffer || !buffer_length) return IE_INVAL;
7093 #if HAVE_LIBCURL
7094 /* Check if connection is established
7095 * TODO: This check should be done downstairs. */
7096 if (!context->curl) return IE_CONNECTION_CLOSED;
7099 /* Build AuthenticateMessage request */
7100 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7101 if (!request) {
7102 isds_log_message(context,
7103 _("Could not build GetDataBoxList request"));
7104 return IE_ERROR;
7106 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7107 if(!isds_ns) {
7108 isds_log_message(context, _("Could not create ISDS name space"));
7109 xmlFreeNode(request);
7110 return IE_ERROR;
7112 xmlSetNs(request, isds_ns);
7113 INSERT_STRING(request, "dblType", list_identifier);
7115 /* Send request to server and process response */
7116 err = send_destroy_request_check_response(context,
7117 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7118 &response, NULL, NULL);
7119 if (err) goto leave;
7122 /* Extract Base-64 encoded ZIP file */
7123 xpath_ctx = xmlXPathNewContext(response);
7124 if (!xpath_ctx) {
7125 err = IE_ERROR;
7126 goto leave;
7128 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7129 err = IE_ERROR;
7130 goto leave;
7132 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7134 /* Decode non-empty archive */
7135 if (string && string[0] != '\0') {
7136 *buffer_length = _isds_b64decode(string, buffer);
7137 if (*buffer_length == (size_t) -1) {
7138 isds_printf_message(context,
7139 _("Error while Base64-decoding box list archive"));
7140 err = IE_ERROR;
7141 goto leave;
7146 leave:
7147 free(string);
7148 xmlXPathFreeObject(result);
7149 xmlXPathFreeContext(xpath_ctx);
7150 xmlFreeDoc(response);
7151 xmlFreeNode(request);
7153 if (!err) {
7154 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7155 "processed by server successfully.\n"));
7157 #else /* not HAVE_LIBCURL */
7158 err = IE_NOTSUP;
7159 #endif
7161 return err;
7165 /* Find boxes suiting given criteria.
7166 * @criteria is filter. You should fill in at least some members.
7167 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7168 * possibly empty. Input NULL or valid old structure.
7169 * @return:
7170 * IE_SUCCESS if search succeeded, @boxes contains useful data
7171 * IE_NOEXIST if no such box exists, @boxes will be NULL
7172 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7173 * contains still valid data
7174 * other code if something bad happens. @boxes will be NULL. */
7175 isds_error isds_FindDataBox(struct isds_ctx *context,
7176 const struct isds_DbOwnerInfo *criteria,
7177 struct isds_list **boxes) {
7178 isds_error err = IE_SUCCESS;
7179 #if HAVE_LIBCURL
7180 _Bool truncated = 0;
7181 xmlNsPtr isds_ns = NULL;
7182 xmlNodePtr request = NULL;
7183 xmlDocPtr response = NULL;
7184 xmlChar *code = NULL, *message = NULL;
7185 xmlNodePtr db_owner_info;
7186 xmlXPathContextPtr xpath_ctx = NULL;
7187 xmlXPathObjectPtr result = NULL;
7188 xmlChar *string = NULL;
7189 #endif
7192 if (!context) return IE_INVALID_CONTEXT;
7193 zfree(context->long_message);
7194 if (!boxes) return IE_INVAL;
7195 isds_list_free(boxes);
7197 if (!criteria) {
7198 return IE_INVAL;
7201 #if HAVE_LIBCURL
7202 /* Check if connection is established
7203 * TODO: This check should be done downstairs. */
7204 if (!context->curl) return IE_CONNECTION_CLOSED;
7207 /* Build FindDataBox request */
7208 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
7209 if (!request) {
7210 isds_log_message(context,
7211 _("Could build FindDataBox request"));
7212 return IE_ERROR;
7214 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7215 if(!isds_ns) {
7216 isds_log_message(context, _("Could not create ISDS name space"));
7217 xmlFreeNode(request);
7218 return IE_ERROR;
7220 xmlSetNs(request, isds_ns);
7221 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7222 if (!db_owner_info) {
7223 isds_log_message(context, _("Could not add dbOwnerInfo child to "
7224 "FindDataBox element"));
7225 xmlFreeNode(request);
7226 return IE_ERROR;
7229 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
7230 if (err) goto leave;
7233 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
7235 /* Sent request */
7236 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7238 /* Destroy request */
7239 xmlFreeNode(request); request = NULL;
7241 if (err) {
7242 isds_log(ILF_ISDS, ILL_DEBUG,
7243 _("Processing ISDS response on FindDataBox "
7244 "request failed\n"));
7245 goto leave;
7248 /* Check for response status */
7249 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7250 &code, &message, NULL);
7251 if (err) {
7252 isds_log(ILF_ISDS, ILL_DEBUG,
7253 _("ISDS response on FindDataBox request is missing status\n"));
7254 goto leave;
7257 /* Request processed, but nothing found */
7258 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7259 !xmlStrcmp(code, BAD_CAST "5001")) {
7260 char *code_locale = _isds_utf82locale((char*)code);
7261 char *message_locale = _isds_utf82locale((char*)message);
7262 isds_log(ILF_ISDS, ILL_DEBUG,
7263 _("Server did not found any box on FindDataBox request "
7264 "(code=%s, message=%s)\n"), code_locale, message_locale);
7265 isds_log_message(context, message_locale);
7266 free(code_locale);
7267 free(message_locale);
7268 err = IE_NOEXIST;
7269 goto leave;
7272 /* Warning, not a error */
7273 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7274 char *code_locale = _isds_utf82locale((char*)code);
7275 char *message_locale = _isds_utf82locale((char*)message);
7276 isds_log(ILF_ISDS, ILL_DEBUG,
7277 _("Server truncated response on FindDataBox request "
7278 "(code=%s, message=%s)\n"), code_locale, message_locale);
7279 isds_log_message(context, message_locale);
7280 free(code_locale);
7281 free(message_locale);
7282 truncated = 1;
7285 /* Other error */
7286 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7287 char *code_locale = _isds_utf82locale((char*)code);
7288 char *message_locale = _isds_utf82locale((char*)message);
7289 isds_log(ILF_ISDS, ILL_DEBUG,
7290 _("Server refused FindDataBox request "
7291 "(code=%s, message=%s)\n"), code_locale, message_locale);
7292 isds_log_message(context, message_locale);
7293 free(code_locale);
7294 free(message_locale);
7295 err = IE_ISDS;
7296 goto leave;
7299 xpath_ctx = xmlXPathNewContext(response);
7300 if (!xpath_ctx) {
7301 err = IE_ERROR;
7302 goto leave;
7304 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7305 err = IE_ERROR;
7306 goto leave;
7309 /* Extract boxes if they present */
7310 result = xmlXPathEvalExpression(BAD_CAST
7311 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7312 xpath_ctx);
7313 if (!result) {
7314 err = IE_ERROR;
7315 goto leave;
7317 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7318 struct isds_list *item, *prev_item = NULL;
7319 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7320 item = calloc(1, sizeof(*item));
7321 if (!item) {
7322 err = IE_NOMEM;
7323 goto leave;
7326 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7327 if (i == 0) *boxes = item;
7328 else prev_item->next = item;
7329 prev_item = item;
7331 xpath_ctx->node = result->nodesetval->nodeTab[i];
7332 err = extract_DbOwnerInfo(context,
7333 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7334 if (err) goto leave;
7338 leave:
7339 if (err) {
7340 isds_list_free(boxes);
7341 } else {
7342 if (truncated) err = IE_2BIG;
7345 free(string);
7346 xmlFreeNode(request);
7347 xmlXPathFreeObject(result);
7348 xmlXPathFreeContext(xpath_ctx);
7350 free(code);
7351 free(message);
7352 xmlFreeDoc(response);
7354 if (!err)
7355 isds_log(ILF_ISDS, ILL_DEBUG,
7356 _("FindDataBox request processed by server successfully.\n"));
7357 #else /* not HAVE_LIBCURL */
7358 err = IE_NOTSUP;
7359 #endif
7361 return err;
7365 #if HAVE_LIBCURL
7366 /* Convert a string with match markers into a plain string with list of
7367 * pointers to the matches
7368 * @string is an UTF-8 encoded non-constant string with match markers
7369 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7370 * The markers will be removed from the string.
7371 * @starts is a reallocated list of static pointers into the @string pointing
7372 * to places where match start markers occured.
7373 * @ends is a reallocated list of static pointers into the @string pointing
7374 * to places where match end markers occured.
7375 * @return IE_SUCCESS in case of no failure. */
7376 static isds_error interpret_matches(xmlChar *string,
7377 struct isds_list **starts, struct isds_list **ends) {
7378 isds_error err = IE_SUCCESS;
7379 xmlChar *pointer, *destination, *source;
7380 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7382 isds_list_free(starts);
7383 isds_list_free(ends);
7384 if (NULL == starts || NULL == ends) return IE_INVAL;
7385 if (NULL == string) return IE_SUCCESS;
7387 for (pointer = string; *pointer != '\0';) {
7388 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7389 /* Remove the start marker */
7390 for (source = pointer + 14, destination = pointer;
7391 *source != '\0'; source++, destination++) {
7392 *destination = *source;
7394 *destination = '\0';
7395 /* Append the pointer into the list */
7396 item = calloc(1, sizeof(*item));
7397 if (!item) {
7398 err = IE_NOMEM;
7399 goto leave;
7401 item->destructor = (void (*)(void **))NULL;
7402 item->data = pointer;
7403 if (NULL == prev_start) *starts = item;
7404 else prev_start->next = item;
7405 prev_start = item;
7406 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7407 /* Remove the end marker */
7408 for (source = pointer + 12, destination = pointer;
7409 *source != '\0'; source++, destination++) {
7410 *destination = *source;
7412 *destination = '\0';
7413 /* Append the pointer into the list */
7414 item = calloc(1, sizeof(*item));
7415 if (!item) {
7416 err = IE_NOMEM;
7417 goto leave;
7419 item->destructor = (void (*)(void **))NULL;
7420 item->data = pointer;
7421 if (NULL == prev_end) *ends = item;
7422 else prev_end->next = item;
7423 prev_end = item;
7424 } else {
7425 pointer++;
7429 leave:
7430 if (err) {
7431 isds_list_free(starts);
7432 isds_list_free(ends);
7434 return err;
7438 /* Convert isds:dbResult XML tree into structure
7439 * @context is ISDS context.
7440 * @fulltext_result is automatically reallocated found box structure.
7441 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7442 * @collect_matches is true to interpret match markers.
7443 * In case of error @result will be freed. */
7444 static isds_error extract_dbResult(struct isds_ctx *context,
7445 struct isds_fulltext_result **fulltext_result,
7446 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7447 isds_error err = IE_SUCCESS;
7448 xmlXPathObjectPtr result = NULL;
7449 char *string = NULL;
7451 if (NULL == context) return IE_INVALID_CONTEXT;
7452 if (NULL == fulltext_result) return IE_INVAL;
7453 isds_fulltext_result_free(fulltext_result);
7454 if (!xpath_ctx) return IE_INVAL;
7457 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7458 if (NULL == *fulltext_result) {
7459 err = IE_NOMEM;
7460 goto leave;
7463 /* Extract data */
7464 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7466 EXTRACT_STRING("isds:dbType", string);
7467 if (NULL == string) {
7468 err = IE_ISDS;
7469 isds_log_message(context, _("Empty isds:dbType element"));
7470 goto leave;
7472 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7473 if (err) {
7474 if (err == IE_ENUM) {
7475 err = IE_ISDS;
7476 char *string_locale = _isds_utf82locale(string);
7477 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7478 string_locale);
7479 free(string_locale);
7481 goto leave;
7483 zfree(string);
7485 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7486 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7488 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7489 if (err) goto leave;
7491 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7492 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7493 (*fulltext_result)->dbEffectiveOVM);
7495 EXTRACT_STRING("isds:dbSendOptions", string);
7496 if (NULL == string) {
7497 err = IE_ISDS;
7498 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7499 goto leave;
7501 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7502 (*fulltext_result)->active = 1;
7503 (*fulltext_result)->public_sending = 1;
7504 (*fulltext_result)->commercial_sending = 0;
7505 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7506 (*fulltext_result)->active = 1;
7507 (*fulltext_result)->public_sending = 1;
7508 (*fulltext_result)->commercial_sending = 1;
7509 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7510 (*fulltext_result)->active = 1;
7511 (*fulltext_result)->public_sending = 0;
7512 (*fulltext_result)->commercial_sending = 1;
7513 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7514 (*fulltext_result)->active = 1;
7515 (*fulltext_result)->public_sending = 0;
7516 (*fulltext_result)->commercial_sending = 0;
7517 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7518 (*fulltext_result)->active = 0;
7519 (*fulltext_result)->public_sending = 0;
7520 (*fulltext_result)->commercial_sending = 0;
7521 } else {
7522 err = IE_ISDS;
7523 char *string_locale = _isds_utf82locale(string);
7524 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7525 string_locale);
7526 free(string_locale);
7527 goto leave;
7529 zfree(string);
7531 /* Interpret match marks */
7532 if (collect_matches) {
7533 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7534 &((*fulltext_result)->name_match_start),
7535 &((*fulltext_result)->name_match_end));
7536 if (err) goto leave;
7537 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7538 &((*fulltext_result)->address_match_start),
7539 &((*fulltext_result)->address_match_end));
7540 if (err) goto leave;
7543 leave:
7544 if (err) isds_fulltext_result_free(fulltext_result);
7545 free(string);
7546 xmlXPathFreeObject(result);
7547 return err;
7549 #endif /* HAVE_LIBCURL */
7552 /* Find boxes matching a given full-text criteria.
7553 * @context is a session context
7554 * @query is a non-empty string which consists of words to search
7555 * @target selects box attributes to search for @query words. Pass NULL if you
7556 * don't care.
7557 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7558 * to search in all box types. Pass NULL to let server to use default value
7559 * which is DBTYPE_SYSTEM.
7560 * @page_size defines count of boxes to constitute a response page. It counts
7561 * from zero. Pass NULL to let server to use a default value (50 now).
7562 * @page_number defines ordinar number of the response page to return. It
7563 * counts from zero. Pass NULL to let server to use a default value (0 now).
7564 * @track_matches points to true for marking @query words found in the box
7565 * attributes. It points to false for not marking. Pass NULL to let the server
7566 * to use default value (false now).
7567 * @total_matching_boxes outputs reallocated number of all boxes matching the
7568 * query. Will be pointer to NULL if server did not provide the value.
7569 * Pass NULL if you don't care.
7570 * @current_page_beginning outputs reallocated ordinar number of the first box
7571 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7572 * server did not provide the value. Pass NULL if you don't care.
7573 * @current_page_size outputs reallocated count of boxes in the this @boxes
7574 * page. It will be pointer to NULL if the server did not provide the value.
7575 * Pass NULL if you don't care.
7576 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7577 * is the last one, false if more boxes match, NULL if the server did not
7578 * provude the value. Pass NULL if you don't care.
7579 * @boxes outputs reallocated list of isds_fulltext_result structures,
7580 * possibly empty.
7581 * @return:
7582 * IE_SUCCESS if search succeeded
7583 * IE_2BIG if @page_size is too large
7584 * other code if something bad happens; output arguments will be NULL. */
7585 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7586 const char *query,
7587 const isds_fulltext_target *target,
7588 const isds_DbType *box_type,
7589 const unsigned long int *page_size,
7590 const unsigned long int *page_number,
7591 const _Bool *track_matches,
7592 unsigned long int **total_matching_boxes,
7593 unsigned long int **current_page_beginning,
7594 unsigned long int **current_page_size,
7595 _Bool **last_page,
7596 struct isds_list **boxes) {
7597 isds_error err = IE_SUCCESS;
7598 #if HAVE_LIBCURL
7599 xmlNsPtr isds_ns = NULL;
7600 xmlNodePtr request = NULL;
7601 xmlDocPtr response = NULL;
7602 xmlNodePtr node;
7603 xmlXPathContextPtr xpath_ctx = NULL;
7604 xmlXPathObjectPtr result = NULL;
7605 const xmlChar *static_string = NULL;
7606 xmlChar *string = NULL;
7608 const xmlChar *codes[] = {
7609 BAD_CAST "1004",
7610 BAD_CAST "1152",
7611 BAD_CAST "1153",
7612 BAD_CAST "1154",
7613 BAD_CAST "1155",
7614 BAD_CAST "1156",
7615 BAD_CAST "9002",
7616 NULL
7618 const char *meanings[] = {
7619 N_("You are not allowed to perform the search"),
7620 N_("The query string is empty"),
7621 N_("Searched box ID is malformed"),
7622 N_("Searched organization ID is malformed"),
7623 N_("Invalid input"),
7624 N_("Requested page size is too large"),
7625 N_("Search engine internal error")
7627 const isds_error errors[] = {
7628 IE_ISDS,
7629 IE_INVAL,
7630 IE_INVAL,
7631 IE_INVAL,
7632 IE_INVAL,
7633 IE_2BIG,
7634 IE_ISDS
7636 struct code_map_isds_error map = {
7637 .codes = codes,
7638 .meanings = meanings,
7639 .errors = errors
7641 #endif
7644 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7645 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7646 if (NULL != current_page_size) zfree(*current_page_size);
7647 if (NULL != last_page) zfree(*last_page);
7648 isds_list_free(boxes);
7650 if (NULL == context) return IE_INVALID_CONTEXT;
7651 zfree(context->long_message);
7653 if (NULL == boxes) return IE_INVAL;
7655 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
7656 isds_log_message(context, _("Query string must be non-empty"));
7657 return IE_INVAL;
7660 #if HAVE_LIBCURL
7661 /* Check if connection is established
7662 * TODO: This check should be done downstairs. */
7663 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7665 /* Build FindDataBox request */
7666 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
7667 if (NULL == request) {
7668 isds_log_message(context,
7669 _("Could not build ISDSSearch2 request"));
7670 return IE_ERROR;
7672 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7673 if(NULL == isds_ns) {
7674 isds_log_message(context, _("Could not create ISDS name space"));
7675 xmlFreeNode(request);
7676 return IE_ERROR;
7678 xmlSetNs(request, isds_ns);
7680 INSERT_STRING(request, "searchText", query);
7682 if (NULL != target) {
7683 static_string = isds_fulltext_target2string(*(target));
7684 if (NULL == static_string) {
7685 isds_printf_message(context, _("Invalid target value: %d"),
7686 *(target));
7687 err = IE_ENUM;
7688 goto leave;
7691 INSERT_STRING(request, "searchType", static_string);
7692 static_string = NULL;
7694 if (NULL != box_type) {
7695 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7696 if (DBTYPE_SYSTEM == *box_type) {
7697 static_string = BAD_CAST "ALL";
7698 } else {
7699 static_string = isds_DbType2string(*(box_type));
7700 if (NULL == static_string) {
7701 isds_printf_message(context, _("Invalid box type value: %d"),
7702 *(box_type));
7703 err = IE_ENUM;
7704 goto leave;
7708 INSERT_STRING(request, "searchScope", static_string);
7709 static_string = NULL;
7711 INSERT_ULONGINT(request, "page", page_number, string);
7712 INSERT_ULONGINT(request, "pageSize", page_size, string);
7713 INSERT_BOOLEAN(request, "highlighting", track_matches);
7715 /* Send request and check response */
7716 err = send_destroy_request_check_response(context,
7717 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
7718 &request, &response, NULL, &map);
7719 if (err) goto leave;
7721 /* Parse response */
7722 xpath_ctx = xmlXPathNewContext(response);
7723 if (NULL == xpath_ctx) {
7724 err = IE_ERROR;
7725 goto leave;
7727 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7728 err = IE_ERROR;
7729 goto leave;
7731 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
7732 xpath_ctx);
7733 if (!result) {
7734 err = IE_ERROR;
7735 goto leave;
7737 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7738 isds_log_message(context, _("Missing ISDSSearch2 element"));
7739 err = IE_ISDS;
7740 goto leave;
7742 if (result->nodesetval->nodeNr > 1) {
7743 isds_log_message(context, _("Multiple ISDSSearch2 element"));
7744 err = IE_ISDS;
7745 goto leave;
7747 xpath_ctx->node = result->nodesetval->nodeTab[0];
7748 xmlXPathFreeObject(result); result = NULL;
7751 /* Extract counters */
7752 if (NULL != total_matching_boxes) {
7753 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
7755 if (NULL != current_page_size) {
7756 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
7758 if (NULL != current_page_beginning) {
7759 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
7761 if (NULL != last_page) {
7762 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
7764 xmlXPathFreeObject(result); result = NULL;
7766 /* Extract boxes if they present */
7767 result = xmlXPathEvalExpression(BAD_CAST
7768 "isds:dbResults/isds:dbResult", xpath_ctx);
7769 if (NULL == result) {
7770 err = IE_ERROR;
7771 goto leave;
7773 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7774 struct isds_list *item, *prev_item = NULL;
7775 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7776 item = calloc(1, sizeof(*item));
7777 if (!item) {
7778 err = IE_NOMEM;
7779 goto leave;
7782 item->destructor = (void (*)(void **))isds_fulltext_result_free;
7783 if (i == 0) *boxes = item;
7784 else prev_item->next = item;
7785 prev_item = item;
7787 xpath_ctx->node = result->nodesetval->nodeTab[i];
7788 err = extract_dbResult(context,
7789 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
7790 (NULL == track_matches) ? 0 : *track_matches);
7791 if (err) goto leave;
7795 leave:
7796 if (err) {
7797 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7798 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7799 if (NULL != current_page_size) zfree(*current_page_size);
7800 if (NULL != last_page) zfree(*last_page);
7801 isds_list_free(boxes);
7804 free(string);
7805 xmlFreeNode(request);
7806 xmlXPathFreeObject(result);
7807 xmlXPathFreeContext(xpath_ctx);
7808 xmlFreeDoc(response);
7810 if (!err)
7811 isds_log(ILF_ISDS, ILL_DEBUG,
7812 _("ISDSSearch2 request processed by server successfully.\n"));
7813 #else /* not HAVE_LIBCURL */
7814 err = IE_NOTSUP;
7815 #endif
7817 return err;
7821 /* Get status of a box.
7822 * @context is ISDS session context.
7823 * @box_id is UTF-8 encoded box identifier as zero terminated string
7824 * @box_status is return value of box status.
7825 * @return:
7826 * IE_SUCCESS if box has been found and its status retrieved
7827 * IE_NOEXIST if box is not known to ISDS server
7828 * or other appropriate error.
7829 * You can use isds_DbState to enumerate box status. However out of enum
7830 * range value can be returned too. This is feature because ISDS
7831 * specification leaves the set of values open.
7832 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7833 * the box has been deleted, but ISDS still lists its former existence. */
7834 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7835 long int *box_status) {
7836 isds_error err = IE_SUCCESS;
7837 #if HAVE_LIBCURL
7838 xmlNsPtr isds_ns = NULL;
7839 xmlNodePtr request = NULL, db_id;
7840 xmlDocPtr response = NULL;
7841 xmlXPathContextPtr xpath_ctx = NULL;
7842 xmlXPathObjectPtr result = NULL;
7843 xmlChar *string = NULL;
7845 const xmlChar *codes[] = {
7846 BAD_CAST "5001",
7847 BAD_CAST "1007",
7848 BAD_CAST "2011",
7849 NULL
7851 const char *meanings[] = {
7852 "The box does not exist",
7853 "Box ID is malformed",
7854 "Box ID malformed",
7856 const isds_error errors[] = {
7857 IE_NOEXIST,
7858 IE_INVAL,
7859 IE_INVAL,
7861 struct code_map_isds_error map = {
7862 .codes = codes,
7863 .meanings = meanings,
7864 .errors = errors
7866 #endif
7868 if (!context) return IE_INVALID_CONTEXT;
7869 zfree(context->long_message);
7870 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7872 #if HAVE_LIBCURL
7873 /* Check if connection is established
7874 * TODO: This check should be done downstairs. */
7875 if (!context->curl) return IE_CONNECTION_CLOSED;
7878 /* Build CheckDataBox request */
7879 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7880 if (!request) {
7881 isds_log_message(context,
7882 _("Could build CheckDataBox request"));
7883 return IE_ERROR;
7885 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7886 if(!isds_ns) {
7887 isds_log_message(context, _("Could not create ISDS name space"));
7888 xmlFreeNode(request);
7889 return IE_ERROR;
7891 xmlSetNs(request, isds_ns);
7892 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7893 if (!db_id) {
7894 isds_log_message(context, _("Could not add dbID child to "
7895 "CheckDataBox element"));
7896 xmlFreeNode(request);
7897 return IE_ERROR;
7901 /* Send request and check response*/
7902 err = send_destroy_request_check_response(context,
7903 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7904 &request, &response, NULL, &map);
7905 if (err) goto leave;
7908 /* Extract data */
7909 xpath_ctx = xmlXPathNewContext(response);
7910 if (!xpath_ctx) {
7911 err = IE_ERROR;
7912 goto leave;
7914 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7915 err = IE_ERROR;
7916 goto leave;
7918 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7919 xpath_ctx);
7920 if (!result) {
7921 err = IE_ERROR;
7922 goto leave;
7924 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7925 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7926 err = IE_ISDS;
7927 goto leave;
7929 if (result->nodesetval->nodeNr > 1) {
7930 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7931 err = IE_ISDS;
7932 goto leave;
7934 xpath_ctx->node = result->nodesetval->nodeTab[0];
7935 xmlXPathFreeObject(result); result = NULL;
7937 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7940 leave:
7941 free(string);
7942 xmlXPathFreeObject(result);
7943 xmlXPathFreeContext(xpath_ctx);
7945 xmlFreeDoc(response);
7947 if (!err)
7948 isds_log(ILF_ISDS, ILL_DEBUG,
7949 _("CheckDataBox request processed by server successfully.\n"));
7950 #else /* not HAVE_LIBCURL */
7951 err = IE_NOTSUP;
7952 #endif
7954 return err;
7958 /* Get list of permissions to send commercial messages.
7959 * @context is ISDS session context.
7960 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7961 * @permissions is a reallocated list of permissions (struct
7962 * isds_commercial_permission*) to send commercial messages from @box_id. The
7963 * order of permissions is significant as the server applies the permissions
7964 * and associated pre-paid credits in the order. Empty list means no
7965 * permission.
7966 * @return:
7967 * IE_SUCCESS if the list has been obtained correctly,
7968 * or other appropriate error. */
7969 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7970 const char *box_id, struct isds_list **permissions) {
7971 isds_error err = IE_SUCCESS;
7972 #if HAVE_LIBCURL
7973 xmlDocPtr response = NULL;
7974 xmlXPathContextPtr xpath_ctx = NULL;
7975 xmlXPathObjectPtr result = NULL;
7976 #endif
7978 if (!context) return IE_INVALID_CONTEXT;
7979 zfree(context->long_message);
7980 if (NULL == permissions) return IE_INVAL;
7981 isds_list_free(permissions);
7982 if (NULL == box_id) return IE_INVAL;
7984 #if HAVE_LIBCURL
7985 /* Check if connection is established */
7986 if (!context->curl) return IE_CONNECTION_CLOSED;
7988 /* Do request and check for success */
7989 err = build_send_dbid_request_check_response(context,
7990 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7991 BAD_CAST box_id, NULL, &response, NULL);
7992 if (!err) {
7993 isds_log(ILF_ISDS, ILL_DEBUG,
7994 _("PDZInfo request processed by server successfully.\n"));
7997 /* Extract data */
7998 /* Prepare structure */
7999 xpath_ctx = xmlXPathNewContext(response);
8000 if (!xpath_ctx) {
8001 err = IE_ERROR;
8002 goto leave;
8004 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8005 err = IE_ERROR;
8006 goto leave;
8009 /* Set context node */
8010 result = xmlXPathEvalExpression(BAD_CAST
8011 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8012 xpath_ctx);
8013 if (!result) {
8014 err = IE_ERROR;
8015 goto leave;
8017 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8018 struct isds_list *prev_item = NULL;
8020 /* Iterate over all permission records */
8021 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8022 struct isds_list *item;
8024 /* Prepare structure */
8025 item = calloc(1, sizeof(*item));
8026 if (!item) {
8027 err = IE_NOMEM;
8028 goto leave;
8030 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8031 if (i == 0) *permissions = item;
8032 else prev_item->next = item;
8033 prev_item = item;
8035 /* Extract it */
8036 xpath_ctx->node = result->nodesetval->nodeTab[i];
8037 err = extract_DbPDZRecord(context,
8038 (struct isds_commercial_permission **) (&item->data),
8039 xpath_ctx);
8040 if (err) goto leave;
8044 leave:
8045 if (err) {
8046 isds_list_free(permissions);
8049 xmlXPathFreeObject(result);
8050 xmlXPathFreeContext(xpath_ctx);
8051 xmlFreeDoc(response);
8053 #else /* not HAVE_LIBCURL */
8054 err = IE_NOTSUP;
8055 #endif
8057 return err;
8061 /* Get details about credit for sending pre-paid commercial messages.
8062 * @context is ISDS session context.
8063 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8064 * @from_date is first day of credit history to return in @history. Only
8065 * tm_year, tm_mon and tm_mday carry sane value.
8066 * @to_date is last day of credit history to return in @history. Only
8067 * tm_year, tm_mon and tm_mday carry sane value.
8068 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8069 * if you don't care. This and all other credit values are integers in
8070 * hundredths of Czech Crowns.
8071 * @email outputs notification e-mail address where notifications about credit
8072 * are sent. This is automatically reallocated string. Pass NULL if you don't
8073 * care. It can return NULL if no address is defined.
8074 * @history outputs auto-reallocated list of pointers to struct
8075 * isds_credit_event. Events in closed interval @from_time to @to_time are
8076 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8077 * are sorted by time.
8078 * @return:
8079 * IE_SUCCESS if the credit details have been obtained correctly,
8080 * or other appropriate error. Please note that server allows to retrieve
8081 * only limited history of events. */
8082 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8083 const char *box_id,
8084 const struct tm *from_date, const struct tm *to_date,
8085 long int *credit, char **email, struct isds_list **history) {
8086 isds_error err = IE_SUCCESS;
8087 #if HAVE_LIBCURL
8088 char *box_id_locale = NULL;
8089 xmlNodePtr request = NULL, node;
8090 xmlNsPtr isds_ns = NULL;
8091 xmlChar *string = NULL;
8093 xmlDocPtr response = NULL;
8094 xmlXPathContextPtr xpath_ctx = NULL;
8095 xmlXPathObjectPtr result = NULL;
8097 const xmlChar *codes[] = {
8098 BAD_CAST "1004",
8099 BAD_CAST "2011",
8100 BAD_CAST "1093",
8101 BAD_CAST "1137",
8102 BAD_CAST "1058",
8103 NULL
8105 const char *meanings[] = {
8106 "Insufficient priviledges for the box",
8107 "The box does not exist",
8108 "Date is too long (history is not available after 15 months)",
8109 "Interval is too long (limit is 3 months)",
8110 "Invalid date"
8112 const isds_error errors[] = {
8113 IE_ISDS,
8114 IE_NOEXIST,
8115 IE_DATE,
8116 IE_DATE,
8117 IE_DATE,
8119 struct code_map_isds_error map = {
8120 .codes = codes,
8121 .meanings = meanings,
8122 .errors = errors
8124 #endif
8126 if (!context) return IE_INVALID_CONTEXT;
8127 zfree(context->long_message);
8129 /* Free output argument */
8130 if (NULL != credit) *credit = 0;
8131 if (NULL != email) zfree(*email);
8132 isds_list_free(history);
8134 if (NULL == box_id) return IE_INVAL;
8136 #if HAVE_LIBCURL
8137 /* Check if connection is established */
8138 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8140 box_id_locale = _isds_utf82locale((char*)box_id);
8141 if (NULL == box_id_locale) {
8142 err = IE_NOMEM;
8143 goto leave;
8146 /* Build request */
8147 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8148 if (NULL == request) {
8149 isds_printf_message(context,
8150 _("Could not build DataBoxCreditInfo request for %s box"),
8151 box_id_locale);
8152 err = IE_ERROR;
8153 goto leave;
8155 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8156 if(!isds_ns) {
8157 isds_log_message(context, _("Could not create ISDS name space"));
8158 err = IE_ERROR;
8159 goto leave;
8161 xmlSetNs(request, isds_ns);
8163 /* Add mandatory XSD:tIdDbInput child */
8164 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8165 /* Add mandatory dates elements with optional values */
8166 if (from_date) {
8167 err = tm2datestring(from_date, &string);
8168 if (err) {
8169 isds_log_message(context,
8170 _("Could not convert `from_date' argument to ISO date "
8171 "string"));
8172 goto leave;
8174 INSERT_STRING(request, "ciFromDate", string);
8175 zfree(string);
8176 } else {
8177 INSERT_STRING(request, "ciFromDate", NULL);
8179 if (to_date) {
8180 err = tm2datestring(to_date, &string);
8181 if (err) {
8182 isds_log_message(context,
8183 _("Could not convert `to_date' argument to ISO date "
8184 "string"));
8185 goto leave;
8187 INSERT_STRING(request, "ciTodate", string);
8188 zfree(string);
8189 } else {
8190 INSERT_STRING(request, "ciTodate", NULL);
8193 /* Send request and check response*/
8194 err = send_destroy_request_check_response(context,
8195 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8196 &request, &response, NULL, &map);
8197 if (err) goto leave;
8200 /* Extract data */
8201 /* Set context to the root */
8202 xpath_ctx = xmlXPathNewContext(response);
8203 if (!xpath_ctx) {
8204 err = IE_ERROR;
8205 goto leave;
8207 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8208 err = IE_ERROR;
8209 goto leave;
8211 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8212 xpath_ctx);
8213 if (!result) {
8214 err = IE_ERROR;
8215 goto leave;
8217 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8218 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8219 err = IE_ISDS;
8220 goto leave;
8222 if (result->nodesetval->nodeNr > 1) {
8223 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8224 err = IE_ISDS;
8225 goto leave;
8227 xpath_ctx->node = result->nodesetval->nodeTab[0];
8228 xmlXPathFreeObject(result); result = NULL;
8230 /* Extract common data */
8231 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8232 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8234 /* Extract records */
8235 if (NULL == history) goto leave;
8236 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8237 xpath_ctx);
8238 if (!result) {
8239 err = IE_ERROR;
8240 goto leave;
8242 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8243 struct isds_list *prev_item = NULL;
8245 /* Iterate over all records */
8246 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8247 struct isds_list *item;
8249 /* Prepare structure */
8250 item = calloc(1, sizeof(*item));
8251 if (!item) {
8252 err = IE_NOMEM;
8253 goto leave;
8255 item->destructor = (void(*)(void**))isds_credit_event_free;
8256 if (i == 0) *history = item;
8257 else prev_item->next = item;
8258 prev_item = item;
8260 /* Extract it */
8261 xpath_ctx->node = result->nodesetval->nodeTab[i];
8262 err = extract_CiRecord(context,
8263 (struct isds_credit_event **) (&item->data),
8264 xpath_ctx);
8265 if (err) goto leave;
8269 leave:
8270 if (!err) {
8271 isds_log(ILF_ISDS, ILL_DEBUG,
8272 _("DataBoxCreditInfo request processed by server successfully.\n"));
8274 if (err) {
8275 isds_list_free(history);
8276 if (NULL != email) zfree(*email)
8279 free(box_id_locale);
8280 xmlXPathFreeObject(result);
8281 xmlXPathFreeContext(xpath_ctx);
8282 xmlFreeDoc(response);
8284 #else /* not HAVE_LIBCURL */
8285 err = IE_NOTSUP;
8286 #endif
8288 return err;
8292 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8293 * code, destroy response and log success.
8294 * @context is ISDS session context.
8295 * @service_name is name of SERVICE_DB_MANIPULATION service
8296 * @box_id is UTF-8 encoded box identifier as zero terminated string
8297 * @approval is optional external approval of box manipulation
8298 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8299 * NULL, if you don't care. */
8300 static isds_error build_send_manipulationdbid_request_check_drop_response(
8301 struct isds_ctx *context, const xmlChar *service_name,
8302 const xmlChar *box_id, const struct isds_approval *approval,
8303 xmlChar **refnumber) {
8304 isds_error err = IE_SUCCESS;
8305 #if HAVE_LIBCURL
8306 xmlDocPtr response = NULL;
8307 #endif
8309 if (!context) return IE_INVALID_CONTEXT;
8310 zfree(context->long_message);
8311 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8313 #if HAVE_LIBCURL
8314 /* Check if connection is established */
8315 if (!context->curl) return IE_CONNECTION_CLOSED;
8317 /* Do request and check for success */
8318 err = build_send_dbid_request_check_response(context,
8319 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8320 &response, refnumber);
8321 xmlFreeDoc(response);
8323 if (!err) {
8324 char *service_name_locale = _isds_utf82locale((char *) service_name);
8325 isds_log(ILF_ISDS, ILL_DEBUG,
8326 _("%s request processed by server successfully.\n"),
8327 service_name_locale);
8328 free(service_name_locale);
8330 #else /* not HAVE_LIBCURL */
8331 err = IE_NOTSUP;
8332 #endif
8334 return err;
8338 /* Switch box into state where box can receive commercial messages (off by
8339 * default)
8340 * @context is ISDS session context.
8341 * @box_id is UTF-8 encoded box identifier as zero terminated string
8342 * @allow is true for enable, false for disable commercial messages income
8343 * @approval is optional external approval of box manipulation
8344 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8345 * NULL, if you don't care. */
8346 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8347 const char *box_id, const _Bool allow,
8348 const struct isds_approval *approval, char **refnumber) {
8349 return build_send_manipulationdbid_request_check_drop_response(context,
8350 (allow) ? BAD_CAST "SetOpenAddressing" :
8351 BAD_CAST "ClearOpenAddressing",
8352 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8356 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8357 * message acceptance). This is just a box permission. Sender must apply
8358 * such role by sending each message.
8359 * @context is ISDS session context.
8360 * @box_id is UTF-8 encoded box identifier as zero terminated string
8361 * @allow is true for enable, false for disable OVM role permission
8362 * @approval is optional external approval of box manipulation
8363 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8364 * NULL, if you don't care. */
8365 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
8366 const char *box_id, const _Bool allow,
8367 const struct isds_approval *approval, char **refnumber) {
8368 return build_send_manipulationdbid_request_check_drop_response(context,
8369 (allow) ? BAD_CAST "SetEffectiveOVM" :
8370 BAD_CAST "ClearEffectiveOVM",
8371 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8375 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8376 * code, destroy response and log success.
8377 * @context is ISDS session context.
8378 * @service_name is name of SERVICE_DB_MANIPULATION service
8379 * @owner is structure describing box
8380 * @approval is optional external approval of box manipulation
8381 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8382 * NULL, if you don't care. */
8383 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8384 struct isds_ctx *context, const xmlChar *service_name,
8385 const struct isds_DbOwnerInfo *owner,
8386 const struct isds_approval *approval, xmlChar **refnumber) {
8387 isds_error err = IE_SUCCESS;
8388 #if HAVE_LIBCURL
8389 char *service_name_locale = NULL;
8390 xmlNodePtr request = NULL, db_owner_info;
8391 xmlNsPtr isds_ns = NULL;
8392 #endif
8395 if (!context) return IE_INVALID_CONTEXT;
8396 zfree(context->long_message);
8397 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8399 #if HAVE_LIBCURL
8400 service_name_locale = _isds_utf82locale((char*)service_name);
8401 if (!service_name_locale) {
8402 err = IE_NOMEM;
8403 goto leave;
8406 /* Build request */
8407 request = xmlNewNode(NULL, service_name);
8408 if (!request) {
8409 isds_printf_message(context,
8410 _("Could not build %s request"), service_name_locale);
8411 err = IE_ERROR;
8412 goto leave;
8414 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8415 if(!isds_ns) {
8416 isds_log_message(context, _("Could not create ISDS name space"));
8417 err = IE_ERROR;
8418 goto leave;
8420 xmlSetNs(request, isds_ns);
8423 /* Add XSD:tOwnerInfoInput child*/
8424 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8425 err = insert_DbOwnerInfo(context, owner, db_owner_info);
8426 if (err) goto leave;
8428 /* Add XSD:gExtApproval*/
8429 err = insert_GExtApproval(context, approval, request);
8430 if (err) goto leave;
8432 /* Send it to server and process response */
8433 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8434 service_name, &request, refnumber);
8436 leave:
8437 xmlFreeNode(request);
8438 free(service_name_locale);
8439 #else /* not HAVE_LIBCURL */
8440 err = IE_NOTSUP;
8441 #endif
8443 return err;
8447 /* Switch box accessibility state on request of box owner.
8448 * Despite the name, owner must do the request off-line. This function is
8449 * designed for such off-line meeting points (e.g. Czech POINT).
8450 * @context is ISDS session context.
8451 * @box identifies box to switch accessibility state.
8452 * @allow is true for making accessible, false to disallow access.
8453 * @approval is optional external approval of box manipulation
8454 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8455 * NULL, if you don't care. */
8456 isds_error isds_switch_box_accessibility_on_owner_request(
8457 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8458 const _Bool allow, const struct isds_approval *approval,
8459 char **refnumber) {
8460 return build_send_manipulationdbowner_request_check_drop_response(context,
8461 (allow) ? BAD_CAST "EnableOwnDataBox" :
8462 BAD_CAST "DisableOwnDataBox",
8463 box, approval, (xmlChar **) refnumber);
8467 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8468 * date.
8469 * @context is ISDS session context.
8470 * @box identifies box to switch accessibility state.
8471 * @since is date since accessibility has been denied. This can be past too.
8472 * Only tm_year, tm_mon and tm_mday carry sane value.
8473 * @approval is optional external approval of box manipulation
8474 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8475 * NULL, if you don't care. */
8476 isds_error isds_disable_box_accessibility_externaly(
8477 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8478 const struct tm *since, const struct isds_approval *approval,
8479 char **refnumber) {
8480 isds_error err = IE_SUCCESS;
8481 #if HAVE_LIBCURL
8482 char *service_name_locale = NULL;
8483 xmlNodePtr request = NULL, node;
8484 xmlNsPtr isds_ns = NULL;
8485 xmlChar *string = NULL;
8486 #endif
8489 if (!context) return IE_INVALID_CONTEXT;
8490 zfree(context->long_message);
8491 if (!box || !since) return IE_INVAL;
8493 #if HAVE_LIBCURL
8494 /* Build request */
8495 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
8496 if (!request) {
8497 isds_printf_message(context,
8498 _("Could not build %s request"), "DisableDataBoxExternally");
8499 err = IE_ERROR;
8500 goto leave;
8502 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8503 if(!isds_ns) {
8504 isds_log_message(context, _("Could not create ISDS name space"));
8505 err = IE_ERROR;
8506 goto leave;
8508 xmlSetNs(request, isds_ns);
8511 /* Add @box identification */
8512 INSERT_ELEMENT(node, request, "dbOwnerInfo");
8513 err = insert_DbOwnerInfo(context, box, node);
8514 if (err) goto leave;
8516 /* Add @since date */
8517 err = tm2datestring(since, &string);
8518 if(err) {
8519 isds_log_message(context,
8520 _("Could not convert `since' argument to ISO date string"));
8521 goto leave;
8523 INSERT_STRING(request, "dbOwnerDisableDate", string);
8524 zfree(string);
8526 /* Add @approval */
8527 err = insert_GExtApproval(context, approval, request);
8528 if (err) goto leave;
8530 /* Send it to server and process response */
8531 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8532 BAD_CAST "DisableDataBoxExternally", &request,
8533 (xmlChar **) refnumber);
8535 leave:
8536 free(string);
8537 xmlFreeNode(request);
8538 free(service_name_locale);
8539 #else /* not HAVE_LIBCURL */
8540 err = IE_NOTSUP;
8541 #endif
8543 return err;
8547 #if HAVE_LIBCURL
8548 /* Insert struct isds_message data (envelope (recipient data optional) and
8549 * documents into XML tree
8550 * @context is session context
8551 * @outgoing_message is libisds structure with message data
8552 * @create_message is XML CreateMessage or CreateMultipleMessage element
8553 * @process_recipient true for recipient data serialization, false for no
8554 * serialization */
8555 static isds_error insert_envelope_files(struct isds_ctx *context,
8556 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8557 const _Bool process_recipient) {
8559 isds_error err = IE_SUCCESS;
8560 xmlNodePtr envelope, dm_files, node;
8561 xmlChar *string = NULL;
8563 if (!context) return IE_INVALID_CONTEXT;
8564 if (!outgoing_message || !create_message) return IE_INVAL;
8567 /* Build envelope */
8568 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8569 if (!envelope) {
8570 isds_printf_message(context, _("Could not add dmEnvelope child to "
8571 "%s element"), create_message->name);
8572 return IE_ERROR;
8575 if (!outgoing_message->envelope) {
8576 isds_log_message(context, _("Outgoing message is missing envelope"));
8577 err = IE_INVAL;
8578 goto leave;
8581 /* Insert optional message type */
8582 err = insert_message_type(context, outgoing_message->envelope->dmType,
8583 envelope);
8584 if (err) goto leave;
8586 INSERT_STRING(envelope, "dmSenderOrgUnit",
8587 outgoing_message->envelope->dmSenderOrgUnit);
8588 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8589 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8591 if (process_recipient) {
8592 if (!outgoing_message->envelope->dbIDRecipient) {
8593 isds_log_message(context,
8594 _("Outgoing message is missing recipient box identifier"));
8595 err = IE_INVAL;
8596 goto leave;
8598 INSERT_STRING(envelope, "dbIDRecipient",
8599 outgoing_message->envelope->dbIDRecipient);
8601 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8602 outgoing_message->envelope->dmRecipientOrgUnit);
8603 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8604 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8605 INSERT_STRING(envelope, "dmToHands",
8606 outgoing_message->envelope->dmToHands);
8609 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8610 "dmAnnotation");
8611 INSERT_STRING(envelope, "dmAnnotation",
8612 outgoing_message->envelope->dmAnnotation);
8614 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8615 0, 50, "dmRecipientRefNumber");
8616 INSERT_STRING(envelope, "dmRecipientRefNumber",
8617 outgoing_message->envelope->dmRecipientRefNumber);
8619 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8620 0, 50, "dmSenderRefNumber");
8621 INSERT_STRING(envelope, "dmSenderRefNumber",
8622 outgoing_message->envelope->dmSenderRefNumber);
8624 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8625 0, 50, "dmRecipientIdent");
8626 INSERT_STRING(envelope, "dmRecipientIdent",
8627 outgoing_message->envelope->dmRecipientIdent);
8629 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8630 0, 50, "dmSenderIdent");
8631 INSERT_STRING(envelope, "dmSenderIdent",
8632 outgoing_message->envelope->dmSenderIdent);
8634 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8635 outgoing_message->envelope->dmLegalTitleLaw, string);
8636 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8637 outgoing_message->envelope->dmLegalTitleYear, string);
8638 INSERT_STRING(envelope, "dmLegalTitleSect",
8639 outgoing_message->envelope->dmLegalTitleSect);
8640 INSERT_STRING(envelope, "dmLegalTitlePar",
8641 outgoing_message->envelope->dmLegalTitlePar);
8642 INSERT_STRING(envelope, "dmLegalTitlePoint",
8643 outgoing_message->envelope->dmLegalTitlePoint);
8645 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8646 outgoing_message->envelope->dmPersonalDelivery);
8647 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8648 outgoing_message->envelope->dmAllowSubstDelivery);
8650 /* ???: Should we require value for dbEffectiveOVM sender?
8651 * ISDS has default as true */
8652 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8653 INSERT_BOOLEAN(envelope, "dmOVM",
8654 outgoing_message->envelope->dmPublishOwnID);
8657 /* Append dmFiles */
8658 if (!outgoing_message->documents) {
8659 isds_log_message(context,
8660 _("Outgoing message is missing list of documents"));
8661 err = IE_INVAL;
8662 goto leave;
8664 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8665 if (!dm_files) {
8666 isds_printf_message(context, _("Could not add dmFiles child to "
8667 "%s element"), create_message->name);
8668 err = IE_ERROR;
8669 goto leave;
8672 /* Check for document hierarchy */
8673 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8674 if (err) goto leave;
8676 /* Process each document */
8677 for (struct isds_list *item =
8678 (struct isds_list *) outgoing_message->documents;
8679 item; item = item->next) {
8680 if (!item->data) {
8681 isds_log_message(context,
8682 _("List of documents contains empty item"));
8683 err = IE_INVAL;
8684 goto leave;
8686 /* FIXME: Check for dmFileMetaType and for document references.
8687 * Only first document can be of MAIN type */
8688 err = insert_document(context, (struct isds_document*) item->data,
8689 dm_files);
8691 if (err) goto leave;
8694 leave:
8695 free(string);
8696 return err;
8698 #endif /* HAVE_LIBCURL */
8701 /* Send a message via ISDS to a recipient
8702 * @context is session context
8703 * @outgoing_message is message to send; Some members are mandatory (like
8704 * dbIDRecipient), some are optional and some are irrelevant (especially data
8705 * about sender). Included pointer to isds_list documents must contain at
8706 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8707 * members will be filled with valid data from ISDS. Exact list of write
8708 * members is subject to change. Currently dmID is changed.
8709 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8710 isds_error isds_send_message(struct isds_ctx *context,
8711 struct isds_message *outgoing_message) {
8713 isds_error err = IE_SUCCESS;
8714 #if HAVE_LIBCURL
8715 xmlNsPtr isds_ns = NULL;
8716 xmlNodePtr request = NULL;
8717 xmlDocPtr response = NULL;
8718 xmlChar *code = NULL, *message = NULL;
8719 xmlXPathContextPtr xpath_ctx = NULL;
8720 xmlXPathObjectPtr result = NULL;
8721 /*_Bool message_is_complete = 0;*/
8722 #endif
8724 if (!context) return IE_INVALID_CONTEXT;
8725 zfree(context->long_message);
8726 if (!outgoing_message) return IE_INVAL;
8728 #if HAVE_LIBCURL
8729 /* Check if connection is established
8730 * TODO: This check should be done downstairs. */
8731 if (!context->curl) return IE_CONNECTION_CLOSED;
8734 /* Build CreateMessage request */
8735 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8736 if (!request) {
8737 isds_log_message(context,
8738 _("Could not build CreateMessage request"));
8739 return IE_ERROR;
8741 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8742 if(!isds_ns) {
8743 isds_log_message(context, _("Could not create ISDS name space"));
8744 xmlFreeNode(request);
8745 return IE_ERROR;
8747 xmlSetNs(request, isds_ns);
8749 /* Append envelope and files */
8750 err = insert_envelope_files(context, outgoing_message, request, 1);
8751 if (err) goto leave;
8754 /* Signal we can serialize message since now */
8755 /*message_is_complete = 1;*/
8758 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8760 /* Sent request */
8761 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8763 /* Don't' destroy request, we want to provide it to application later */
8765 if (err) {
8766 isds_log(ILF_ISDS, ILL_DEBUG,
8767 _("Processing ISDS response on CreateMessage "
8768 "request failed\n"));
8769 goto leave;
8772 /* Check for response status */
8773 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8774 &code, &message, NULL);
8775 if (err) {
8776 isds_log(ILF_ISDS, ILL_DEBUG,
8777 _("ISDS response on CreateMessage request "
8778 "is missing status\n"));
8779 goto leave;
8782 /* Request processed, but refused by server or server failed */
8783 if (xmlStrcmp(code, BAD_CAST "0000")) {
8784 char *box_id_locale =
8785 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8786 char *code_locale = _isds_utf82locale((char*)code);
8787 char *message_locale = _isds_utf82locale((char*)message);
8788 isds_log(ILF_ISDS, ILL_DEBUG,
8789 _("Server did not accept message for %s on CreateMessage "
8790 "request (code=%s, message=%s)\n"),
8791 box_id_locale, code_locale, message_locale);
8792 isds_log_message(context, message_locale);
8793 free(box_id_locale);
8794 free(code_locale);
8795 free(message_locale);
8796 err = IE_ISDS;
8797 goto leave;
8801 /* Extract data */
8802 xpath_ctx = xmlXPathNewContext(response);
8803 if (!xpath_ctx) {
8804 err = IE_ERROR;
8805 goto leave;
8807 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8808 err = IE_ERROR;
8809 goto leave;
8811 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8812 xpath_ctx);
8813 if (!result) {
8814 err = IE_ERROR;
8815 goto leave;
8817 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8818 isds_log_message(context, _("Missing CreateMessageResponse element"));
8819 err = IE_ISDS;
8820 goto leave;
8822 if (result->nodesetval->nodeNr > 1) {
8823 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8824 err = IE_ISDS;
8825 goto leave;
8827 xpath_ctx->node = result->nodesetval->nodeTab[0];
8828 xmlXPathFreeObject(result); result = NULL;
8830 if (outgoing_message->envelope->dmID) {
8831 free(outgoing_message->envelope->dmID);
8832 outgoing_message->envelope->dmID = NULL;
8834 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8835 if (!outgoing_message->envelope->dmID) {
8836 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8837 "but did not return assigned message ID\n"));
8840 leave:
8841 /* TODO: Serialize message into structure member raw */
8842 /* XXX: Each web service transport message in different format.
8843 * Therefore it's not possible to save them directly.
8844 * To save them, one must figure out common format.
8845 * We can leave it on application, or we can implement the ESS format. */
8846 /*if (message_is_complete) {
8847 if (outgoing_message->envelope->dmID) {
8849 /* Add assigned message ID as first child*/
8850 /*xmlNodePtr dmid_text = xmlNewText(
8851 (xmlChar *) outgoing_message->envelope->dmID);
8852 if (!dmid_text) goto serialization_failed;
8854 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8855 BAD_CAST "dmID");
8856 if (!dmid_element) {
8857 xmlFreeNode(dmid_text);
8858 goto serialization_failed;
8861 xmlNodePtr dmid_element_with_text =
8862 xmlAddChild(dmid_element, dmid_text);
8863 if (!dmid_element_with_text) {
8864 xmlFreeNode(dmid_element);
8865 xmlFreeNode(dmid_text);
8866 goto serialization_failed;
8869 node = xmlAddPrevSibling(envelope->childern,
8870 dmid_element_with_text);
8871 if (!node) {
8872 xmlFreeNodeList(dmid_element_with_text);
8873 goto serialization_failed;
8877 /* Serialize message with ID into raw */
8878 /*buffer = serialize_element(envelope)*/
8879 /* }
8881 serialization_failed:
8885 /* Clean up */
8886 xmlXPathFreeObject(result);
8887 xmlXPathFreeContext(xpath_ctx);
8889 free(code);
8890 free(message);
8891 xmlFreeDoc(response);
8892 xmlFreeNode(request);
8894 if (!err)
8895 isds_log(ILF_ISDS, ILL_DEBUG,
8896 _("CreateMessage request processed by server "
8897 "successfully.\n"));
8898 #else /* not HAVE_LIBCURL */
8899 err = IE_NOTSUP;
8900 #endif
8902 return err;
8906 /* Send a message via ISDS to a multiple recipients
8907 * @context is session context
8908 * @outgoing_message is message to send; Some members are mandatory,
8909 * some are optional and some are irrelevant (especially data
8910 * about sender). Data about recipient will be substituted by ISDS from
8911 * @copies. Included pointer to isds_list documents must
8912 * contain at least one document of FILEMETATYPE_MAIN.
8913 * @copies is list of isds_message_copy structures addressing all desired
8914 * recipients. This is read-write structure, some members will be filled with
8915 * valid data from ISDS (message IDs, error codes, error descriptions).
8916 * @return
8917 * ISDS_SUCCESS if all messages have been sent
8918 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8919 * succeeded messages can be identified by copies->data->error),
8920 * or other error code if something other goes wrong. */
8921 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8922 const struct isds_message *outgoing_message,
8923 struct isds_list *copies) {
8925 isds_error err = IE_SUCCESS;
8926 #if HAVE_LIBCURL
8927 isds_error append_err;
8928 xmlNsPtr isds_ns = NULL;
8929 xmlNodePtr request = NULL, recipients, recipient, node;
8930 struct isds_list *item;
8931 struct isds_message_copy *copy;
8932 xmlDocPtr response = NULL;
8933 xmlChar *code = NULL, *message = NULL;
8934 xmlXPathContextPtr xpath_ctx = NULL;
8935 xmlXPathObjectPtr result = NULL;
8936 xmlChar *string = NULL;
8937 int i;
8938 #endif
8940 if (!context) return IE_INVALID_CONTEXT;
8941 zfree(context->long_message);
8942 if (!outgoing_message || !copies) return IE_INVAL;
8944 #if HAVE_LIBCURL
8945 /* Check if connection is established
8946 * TODO: This check should be done downstairs. */
8947 if (!context->curl) return IE_CONNECTION_CLOSED;
8950 /* Build CreateMultipleMessage request */
8951 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
8952 if (!request) {
8953 isds_log_message(context,
8954 _("Could not build CreateMultipleMessage request"));
8955 return IE_ERROR;
8957 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8958 if(!isds_ns) {
8959 isds_log_message(context, _("Could not create ISDS name space"));
8960 xmlFreeNode(request);
8961 return IE_ERROR;
8963 xmlSetNs(request, isds_ns);
8966 /* Build recipients */
8967 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8968 if (!recipients) {
8969 isds_log_message(context, _("Could not add dmRecipients child to "
8970 "CreateMultipleMessage element"));
8971 xmlFreeNode(request);
8972 return IE_ERROR;
8975 /* Insert each recipient */
8976 for (item = copies; item; item = item->next) {
8977 copy = (struct isds_message_copy *) item->data;
8978 if (!copy) {
8979 isds_log_message(context,
8980 _("`copies' list item contains empty data"));
8981 err = IE_INVAL;
8982 goto leave;
8985 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
8986 if (!recipient) {
8987 isds_log_message(context, _("Could not add dmRecipient child to "
8988 "dmRecipients element"));
8989 err = IE_ERROR;
8990 goto leave;
8993 if (!copy->dbIDRecipient) {
8994 isds_log_message(context,
8995 _("Message copy is missing recipient box identifier"));
8996 err = IE_INVAL;
8997 goto leave;
8999 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
9000 INSERT_STRING(recipient, "dmRecipientOrgUnit",
9001 copy->dmRecipientOrgUnit);
9002 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
9003 copy->dmRecipientOrgUnitNum, string);
9004 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
9007 /* Append envelope and files */
9008 err = insert_envelope_files(context, outgoing_message, request, 0);
9009 if (err) goto leave;
9012 isds_log(ILF_ISDS, ILL_DEBUG,
9013 _("Sending CreateMultipleMessage request to ISDS\n"));
9015 /* Sent request */
9016 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9017 if (err) {
9018 isds_log(ILF_ISDS, ILL_DEBUG,
9019 _("Processing ISDS response on CreateMultipleMessage "
9020 "request failed\n"));
9021 goto leave;
9024 /* Check for response status */
9025 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9026 &code, &message, NULL);
9027 if (err) {
9028 isds_log(ILF_ISDS, ILL_DEBUG,
9029 _("ISDS response on CreateMultipleMessage request "
9030 "is missing status\n"));
9031 goto leave;
9034 /* Request processed, but some copies failed */
9035 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9036 char *box_id_locale =
9037 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9038 char *code_locale = _isds_utf82locale((char*)code);
9039 char *message_locale = _isds_utf82locale((char*)message);
9040 isds_log(ILF_ISDS, ILL_DEBUG,
9041 _("Server did accept message for multiple recipients "
9042 "on CreateMultipleMessage request but delivery to "
9043 "some of them failed (code=%s, message=%s)\n"),
9044 box_id_locale, code_locale, message_locale);
9045 isds_log_message(context, message_locale);
9046 free(box_id_locale);
9047 free(code_locale);
9048 free(message_locale);
9049 err = IE_PARTIAL_SUCCESS;
9052 /* Request refused by server as whole */
9053 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9054 char *box_id_locale =
9055 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9056 char *code_locale = _isds_utf82locale((char*)code);
9057 char *message_locale = _isds_utf82locale((char*)message);
9058 isds_log(ILF_ISDS, ILL_DEBUG,
9059 _("Server did not accept message for multiple recipients "
9060 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9061 box_id_locale, code_locale, message_locale);
9062 isds_log_message(context, message_locale);
9063 free(box_id_locale);
9064 free(code_locale);
9065 free(message_locale);
9066 err = IE_ISDS;
9067 goto leave;
9071 /* Extract data */
9072 xpath_ctx = xmlXPathNewContext(response);
9073 if (!xpath_ctx) {
9074 err = IE_ERROR;
9075 goto leave;
9077 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9078 err = IE_ERROR;
9079 goto leave;
9081 result = xmlXPathEvalExpression(
9082 BAD_CAST "/isds:CreateMultipleMessageResponse"
9083 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9084 xpath_ctx);
9085 if (!result) {
9086 err = IE_ERROR;
9087 goto leave;
9089 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9090 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9091 err = IE_ISDS;
9092 goto leave;
9095 /* Extract message ID and delivery status for each copy */
9096 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9097 item = item->next, i++) {
9098 copy = (struct isds_message_copy *) item->data;
9099 xpath_ctx->node = result->nodesetval->nodeTab[i];
9101 append_err = append_TMStatus(context, copy, xpath_ctx);
9102 if (append_err) {
9103 err = append_err;
9104 goto leave;
9107 if (item || i < result->nodesetval->nodeNr) {
9108 isds_printf_message(context, _("ISDS returned unexpected number of "
9109 "message copy delivery states: %d"),
9110 result->nodesetval->nodeNr);
9111 err = IE_ISDS;
9112 goto leave;
9116 leave:
9117 /* Clean up */
9118 free(string);
9119 xmlXPathFreeObject(result);
9120 xmlXPathFreeContext(xpath_ctx);
9122 free(code);
9123 free(message);
9124 xmlFreeDoc(response);
9125 xmlFreeNode(request);
9127 if (!err)
9128 isds_log(ILF_ISDS, ILL_DEBUG,
9129 _("CreateMultipleMessageResponse request processed by server "
9130 "successfully.\n"));
9131 #else /* not HAVE_LIBCURL */
9132 err = IE_NOTSUP;
9133 #endif
9135 return err;
9139 /* Get list of messages. This is common core for getting sent or received
9140 * messages.
9141 * Any criterion argument can be NULL, if you don't care about it.
9142 * @context is session context. Must not be NULL.
9143 * @outgoing_direction is true if you want list of outgoing messages,
9144 * it's false if you want incoming messages.
9145 * @from_time is minimal time and date of message sending inclusive.
9146 * @to_time is maximal time and date of message sending inclusive
9147 * @organization_unit_number is number of sender/recipient respectively.
9148 * @status_filter is bit field of isds_message_status values. Use special
9149 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9150 * all values, you can use bit-wise arithmetic if you want.)
9151 * @offset is index of first message we are interested in. First message is 1.
9152 * Set to 0 (or 1) if you don't care.
9153 * @number is maximal length of list you want to get as input value, outputs
9154 * number of messages matching these criteria. Can be NULL if you don't care
9155 * (applies to output value either).
9156 * @messages is automatically reallocated list of isds_message's. Be ware that
9157 * it returns only brief overview (envelope and some other fields) about each
9158 * message, not the complete message. FIXME: Specify exact fields.
9159 * The list is sorted by delivery time in ascending order.
9160 * Use NULL if you don't care about don't need the data (useful if you want to
9161 * know only the @number). If you provide &NULL, list will be allocated on
9162 * heap, if you provide pointer to non-NULL, list will be freed automatically
9163 * at first. Also in case of error the list will be NULLed.
9164 * @return IE_SUCCESS or appropriate error code. */
9165 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9166 _Bool outgoing_direction,
9167 const struct timeval *from_time, const struct timeval *to_time,
9168 const long int *organization_unit_number,
9169 const unsigned int status_filter,
9170 const unsigned long int offset, unsigned long int *number,
9171 struct isds_list **messages) {
9173 isds_error err = IE_SUCCESS;
9174 #if HAVE_LIBCURL
9175 xmlNsPtr isds_ns = NULL;
9176 xmlNodePtr request = NULL, node;
9177 xmlDocPtr response = NULL;
9178 xmlChar *code = NULL, *message = NULL;
9179 xmlXPathContextPtr xpath_ctx = NULL;
9180 xmlXPathObjectPtr result = NULL;
9181 xmlChar *string = NULL;
9182 int count = 0;
9183 #endif
9185 if (!context) return IE_INVALID_CONTEXT;
9186 zfree(context->long_message);
9188 /* Free former message list if any */
9189 if (messages) isds_list_free(messages);
9191 #if HAVE_LIBCURL
9192 /* Check if connection is established
9193 * TODO: This check should be done downstairs. */
9194 if (!context->curl) return IE_CONNECTION_CLOSED;
9196 /* Build GetListOf*Messages request */
9197 request = xmlNewNode(NULL,
9198 (outgoing_direction) ?
9199 BAD_CAST "GetListOfSentMessages" :
9200 BAD_CAST "GetListOfReceivedMessages"
9202 if (!request) {
9203 isds_log_message(context,
9204 (outgoing_direction) ?
9205 _("Could not build GetListOfSentMessages request") :
9206 _("Could not build GetListOfReceivedMessages request")
9208 return IE_ERROR;
9210 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9211 if(!isds_ns) {
9212 isds_log_message(context, _("Could not create ISDS name space"));
9213 xmlFreeNode(request);
9214 return IE_ERROR;
9216 xmlSetNs(request, isds_ns);
9219 if (from_time) {
9220 err = timeval2timestring(from_time, &string);
9221 if (err) goto leave;
9223 INSERT_STRING(request, "dmFromTime", string);
9224 free(string); string = NULL;
9226 if (to_time) {
9227 err = timeval2timestring(to_time, &string);
9228 if (err) goto leave;
9230 INSERT_STRING(request, "dmToTime", string);
9231 free(string); string = NULL;
9233 if (outgoing_direction) {
9234 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9235 organization_unit_number, string);
9236 } else {
9237 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9238 organization_unit_number, string);
9241 if (status_filter > MESSAGESTATE_ANY) {
9242 isds_printf_message(context,
9243 _("Invalid message state filter value: %ld"), status_filter);
9244 err = IE_INVAL;
9245 goto leave;
9247 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9249 if (offset > 0 ) {
9250 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9251 } else {
9252 INSERT_STRING(request, "dmOffset", "1");
9255 /* number 0 means no limit */
9256 if (number && *number == 0) {
9257 INSERT_STRING(request, "dmLimit", NULL);
9258 } else {
9259 INSERT_ULONGINT(request, "dmLimit", number, string);
9263 isds_log(ILF_ISDS, ILL_DEBUG,
9264 (outgoing_direction) ?
9265 _("Sending GetListOfSentMessages request to ISDS\n") :
9266 _("Sending GetListOfReceivedMessages request to ISDS\n")
9269 /* Sent request */
9270 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9271 xmlFreeNode(request); request = NULL;
9273 if (err) {
9274 isds_log(ILF_ISDS, ILL_DEBUG,
9275 (outgoing_direction) ?
9276 _("Processing ISDS response on GetListOfSentMessages "
9277 "request failed\n") :
9278 _("Processing ISDS response on GetListOfReceivedMessages "
9279 "request failed\n")
9281 goto leave;
9284 /* Check for response status */
9285 err = isds_response_status(context, SERVICE_DM_INFO, response,
9286 &code, &message, NULL);
9287 if (err) {
9288 isds_log(ILF_ISDS, ILL_DEBUG,
9289 (outgoing_direction) ?
9290 _("ISDS response on GetListOfSentMessages request "
9291 "is missing status\n") :
9292 _("ISDS response on GetListOfReceivedMessages request "
9293 "is missing status\n")
9295 goto leave;
9298 /* Request processed, but nothing found */
9299 if (xmlStrcmp(code, BAD_CAST "0000")) {
9300 char *code_locale = _isds_utf82locale((char*)code);
9301 char *message_locale = _isds_utf82locale((char*)message);
9302 isds_log(ILF_ISDS, ILL_DEBUG,
9303 (outgoing_direction) ?
9304 _("Server refused GetListOfSentMessages request "
9305 "(code=%s, message=%s)\n") :
9306 _("Server refused GetListOfReceivedMessages request "
9307 "(code=%s, message=%s)\n"),
9308 code_locale, message_locale);
9309 isds_log_message(context, message_locale);
9310 free(code_locale);
9311 free(message_locale);
9312 err = IE_ISDS;
9313 goto leave;
9317 /* Extract data */
9318 xpath_ctx = xmlXPathNewContext(response);
9319 if (!xpath_ctx) {
9320 err = IE_ERROR;
9321 goto leave;
9323 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9324 err = IE_ERROR;
9325 goto leave;
9327 result = xmlXPathEvalExpression(
9328 (outgoing_direction) ?
9329 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9330 "isds:dmRecords/isds:dmRecord" :
9331 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9332 "isds:dmRecords/isds:dmRecord",
9333 xpath_ctx);
9334 if (!result) {
9335 err = IE_ERROR;
9336 goto leave;
9339 /* Fill output arguments in */
9340 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9341 struct isds_envelope *envelope;
9342 struct isds_list *item = NULL, *last_item = NULL;
9344 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9345 /* Create new message */
9346 item = calloc(1, sizeof(*item));
9347 if (!item) {
9348 err = IE_NOMEM;
9349 goto leave;
9351 item->destructor = (void(*)(void**)) &isds_message_free;
9352 item->data = calloc(1, sizeof(struct isds_message));
9353 if (!item->data) {
9354 isds_list_free(&item);
9355 err = IE_NOMEM;
9356 goto leave;
9359 /* Extract envelope data */
9360 xpath_ctx->node = result->nodesetval->nodeTab[count];
9361 envelope = NULL;
9362 err = extract_DmRecord(context, &envelope, xpath_ctx);
9363 if (err) {
9364 isds_list_free(&item);
9365 goto leave;
9368 /* Attach extracted envelope */
9369 ((struct isds_message *) item->data)->envelope = envelope;
9371 /* Append new message into the list */
9372 if (!*messages) {
9373 *messages = last_item = item;
9374 } else {
9375 last_item->next = item;
9376 last_item = item;
9380 if (number) *number = count;
9382 leave:
9383 if (err) {
9384 isds_list_free(messages);
9387 free(string);
9388 xmlXPathFreeObject(result);
9389 xmlXPathFreeContext(xpath_ctx);
9391 free(code);
9392 free(message);
9393 xmlFreeDoc(response);
9394 xmlFreeNode(request);
9396 if (!err)
9397 isds_log(ILF_ISDS, ILL_DEBUG,
9398 (outgoing_direction) ?
9399 _("GetListOfSentMessages request processed by server "
9400 "successfully.\n") :
9401 _("GetListOfReceivedMessages request processed by server "
9402 "successfully.\n")
9404 #else /* not HAVE_LIBCURL */
9405 err = IE_NOTSUP;
9406 #endif
9407 return err;
9411 /* Get list of outgoing (already sent) messages.
9412 * Any criterion argument can be NULL, if you don't care about it.
9413 * @context is session context. Must not be NULL.
9414 * @from_time is minimal time and date of message sending inclusive.
9415 * @to_time is maximal time and date of message sending inclusive
9416 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9417 * @status_filter is bit field of isds_message_status values. Use special
9418 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9419 * all values, you can use bit-wise arithmetic if you want.)
9420 * @offset is index of first message we are interested in. First message is 1.
9421 * Set to 0 (or 1) if you don't care.
9422 * @number is maximal length of list you want to get as input value, outputs
9423 * number of messages matching these criteria. Can be NULL if you don't care
9424 * (applies to output value either).
9425 * @messages is automatically reallocated list of isds_message's. Be ware that
9426 * it returns only brief overview (envelope and some other fields) about each
9427 * message, not the complete message. FIXME: Specify exact fields.
9428 * The list is sorted by delivery time in ascending order.
9429 * Use NULL if you don't care about the meta data (useful if you want to know
9430 * only the @number). If you provide &NULL, list will be allocated on heap,
9431 * if you provide pointer to non-NULL, list will be freed automatically at
9432 * first. Also in case of error the list will be NULLed.
9433 * @return IE_SUCCESS or appropriate error code. */
9434 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9435 const struct timeval *from_time, const struct timeval *to_time,
9436 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9437 const unsigned long int offset, unsigned long int *number,
9438 struct isds_list **messages) {
9440 return isds_get_list_of_messages(
9441 context, 1,
9442 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9443 offset, number,
9444 messages);
9448 /* Get list of incoming (addressed to you) messages.
9449 * Any criterion argument can be NULL, if you don't care about it.
9450 * @context is session context. Must not be NULL.
9451 * @from_time is minimal time and date of message sending inclusive.
9452 * @to_time is maximal time and date of message sending inclusive
9453 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9454 * @status_filter is bit field of isds_message_status values. Use special
9455 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9456 * all values, you can use bit-wise arithmetic if you want.)
9457 * @offset is index of first message we are interested in. First message is 1.
9458 * Set to 0 (or 1) if you don't care.
9459 * @number is maximal length of list you want to get as input value, outputs
9460 * number of messages matching these criteria. Can be NULL if you don't care
9461 * (applies to output value either).
9462 * @messages is automatically reallocated list of isds_message's. Be ware that
9463 * it returns only brief overview (envelope and some other fields) about each
9464 * message, not the complete message. FIXME: Specify exact fields.
9465 * Use NULL if you don't care about the meta data (useful if you want to know
9466 * only the @number). If you provide &NULL, list will be allocated on heap,
9467 * if you provide pointer to non-NULL, list will be freed automatically at
9468 * first. Also in case of error the list will be NULLed.
9469 * @return IE_SUCCESS or appropriate error code. */
9470 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9471 const struct timeval *from_time, const struct timeval *to_time,
9472 const long int *dmRecipientOrgUnitNum,
9473 const unsigned int status_filter,
9474 const unsigned long int offset, unsigned long int *number,
9475 struct isds_list **messages) {
9477 return isds_get_list_of_messages(
9478 context, 0,
9479 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9480 offset, number,
9481 messages);
9485 /* Get list of sent message state changes.
9486 * Any criterion argument can be NULL, if you don't care about it.
9487 * @context is session context. Must not be NULL.
9488 * @from_time is minimal time and date of status changes inclusive
9489 * @to_time is maximal time and date of status changes inclusive
9490 * @changed_states is automatically reallocated list of
9491 * isds_message_status_change's. If you provide &NULL, list will be allocated
9492 * on heap, if you provide pointer to non-NULL, list will be freed
9493 * automatically at first. Also in case of error the list will be NULLed.
9494 * XXX: The list item ordering is not specified.
9495 * XXX: Server provides only `recent' changes.
9496 * @return IE_SUCCESS or appropriate error code. */
9497 isds_error isds_get_list_of_sent_message_state_changes(
9498 struct isds_ctx *context,
9499 const struct timeval *from_time, const struct timeval *to_time,
9500 struct isds_list **changed_states) {
9502 isds_error err = IE_SUCCESS;
9503 #if HAVE_LIBCURL
9504 xmlNsPtr isds_ns = NULL;
9505 xmlNodePtr request = NULL, node;
9506 xmlDocPtr response = NULL;
9507 xmlXPathContextPtr xpath_ctx = NULL;
9508 xmlXPathObjectPtr result = NULL;
9509 xmlChar *string = NULL;
9510 int count = 0;
9511 #endif
9513 if (!context) return IE_INVALID_CONTEXT;
9514 zfree(context->long_message);
9516 /* Free former message list if any */
9517 isds_list_free(changed_states);
9519 #if HAVE_LIBCURL
9520 /* Check if connection is established
9521 * TODO: This check should be done downstairs. */
9522 if (!context->curl) return IE_CONNECTION_CLOSED;
9524 /* Build GetMessageStateChanges request */
9525 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
9526 if (!request) {
9527 isds_log_message(context,
9528 _("Could not build GetMessageStateChanges request"));
9529 return IE_ERROR;
9531 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9532 if(!isds_ns) {
9533 isds_log_message(context, _("Could not create ISDS name space"));
9534 xmlFreeNode(request);
9535 return IE_ERROR;
9537 xmlSetNs(request, isds_ns);
9540 if (from_time) {
9541 err = timeval2timestring(from_time, &string);
9542 if (err) goto leave;
9544 INSERT_STRING(request, "dmFromTime", string);
9545 zfree(string);
9547 if (to_time) {
9548 err = timeval2timestring(to_time, &string);
9549 if (err) goto leave;
9551 INSERT_STRING(request, "dmToTime", string);
9552 zfree(string);
9555 /* Sent request */
9556 err = send_destroy_request_check_response(context,
9557 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9558 &response, NULL, NULL);
9559 if (err) goto leave;
9562 /* Extract data */
9563 xpath_ctx = xmlXPathNewContext(response);
9564 if (!xpath_ctx) {
9565 err = IE_ERROR;
9566 goto leave;
9568 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9569 err = IE_ERROR;
9570 goto leave;
9572 result = xmlXPathEvalExpression(
9573 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9574 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9575 if (!result) {
9576 err = IE_ERROR;
9577 goto leave;
9580 /* Fill output arguments in */
9581 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9582 struct isds_list *item = NULL, *last_item = NULL;
9584 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9585 /* Create new status change */
9586 item = calloc(1, sizeof(*item));
9587 if (!item) {
9588 err = IE_NOMEM;
9589 goto leave;
9591 item->destructor =
9592 (void(*)(void**)) &isds_message_status_change_free;
9594 /* Extract message status change */
9595 xpath_ctx->node = result->nodesetval->nodeTab[count];
9596 err = extract_StateChangesRecord(context,
9597 (struct isds_message_status_change **) &item->data,
9598 xpath_ctx);
9599 if (err) {
9600 isds_list_free(&item);
9601 goto leave;
9604 /* Append new message status change into the list */
9605 if (!*changed_states) {
9606 *changed_states = last_item = item;
9607 } else {
9608 last_item->next = item;
9609 last_item = item;
9614 leave:
9615 if (err) {
9616 isds_list_free(changed_states);
9619 free(string);
9620 xmlXPathFreeObject(result);
9621 xmlXPathFreeContext(xpath_ctx);
9622 xmlFreeDoc(response);
9623 xmlFreeNode(request);
9625 if (!err)
9626 isds_log(ILF_ISDS, ILL_DEBUG,
9627 _("GetMessageStateChanges request processed by server "
9628 "successfully.\n"));
9629 #else /* not HAVE_LIBCURL */
9630 err = IE_NOTSUP;
9631 #endif
9632 return err;
9636 #if HAVE_LIBCURL
9637 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9638 * code
9639 * @context is session context
9640 * @service is ISDS WS service handler
9641 * @service_name is name of SERVICE_DM_OPERATIONS
9642 * @message_id is message ID to send as service argument to ISDS
9643 * @response is reallocated server SOAP body response as XML document
9644 * @raw_response is reallocated bit stream with response body. Use
9645 * NULL if you don't care
9646 * @raw_response_length is size of @raw_response in bytes
9647 * @code is reallocated ISDS status code
9648 * @status_message is reallocated ISDS status message
9649 * @return error coded from lower layer, context message will be set up
9650 * appropriately. */
9651 static isds_error build_send_check_message_request(struct isds_ctx *context,
9652 const isds_service service, const xmlChar *service_name,
9653 const char *message_id,
9654 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9655 xmlChar **code, xmlChar **status_message) {
9657 isds_error err = IE_SUCCESS;
9658 char *service_name_locale = NULL, *message_id_locale = NULL;
9659 xmlNodePtr request = NULL, node;
9660 xmlNsPtr isds_ns = NULL;
9662 if (!context) return IE_INVALID_CONTEXT;
9663 if (!service_name || !message_id) return IE_INVAL;
9664 if (!response || !code || !status_message) return IE_INVAL;
9665 if (!raw_response_length && raw_response) return IE_INVAL;
9667 /* Free output argument */
9668 xmlFreeDoc(*response); *response = NULL;
9669 if (raw_response) zfree(*raw_response);
9670 zfree(*code);
9671 zfree(*status_message);
9674 /* Check if connection is established
9675 * TODO: This check should be done downstairs. */
9676 if (!context->curl) return IE_CONNECTION_CLOSED;
9678 service_name_locale = _isds_utf82locale((char*)service_name);
9679 message_id_locale = _isds_utf82locale(message_id);
9680 if (!service_name_locale || !message_id_locale) {
9681 err = IE_NOMEM;
9682 goto leave;
9685 /* Build request */
9686 request = xmlNewNode(NULL, service_name);
9687 if (!request) {
9688 isds_printf_message(context,
9689 _("Could not build %s request for %s message ID"),
9690 service_name_locale, message_id_locale);
9691 err = IE_ERROR;
9692 goto leave;
9694 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9695 if(!isds_ns) {
9696 isds_log_message(context, _("Could not create ISDS name space"));
9697 err = IE_ERROR;
9698 goto leave;
9700 xmlSetNs(request, isds_ns);
9703 /* Add requested ID */
9704 err = validate_message_id_length(context, (xmlChar *) message_id);
9705 if (err) goto leave;
9706 INSERT_STRING(request, "dmID", message_id);
9709 isds_log(ILF_ISDS, ILL_DEBUG,
9710 _("Sending %s request for %s message ID to ISDS\n"),
9711 service_name_locale, message_id_locale);
9713 /* Send request */
9714 err = _isds(context, service, request, response,
9715 raw_response, raw_response_length);
9716 xmlFreeNode(request); request = NULL;
9718 if (err) {
9719 isds_log(ILF_ISDS, ILL_DEBUG,
9720 _("Processing ISDS response on %s request failed\n"),
9721 service_name_locale);
9722 goto leave;
9725 /* Check for response status */
9726 err = isds_response_status(context, service, *response,
9727 code, status_message, NULL);
9728 if (err) {
9729 isds_log(ILF_ISDS, ILL_DEBUG,
9730 _("ISDS response on %s request is missing status\n"),
9731 service_name_locale);
9732 goto leave;
9735 /* Request processed, but nothing found */
9736 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9737 char *code_locale = _isds_utf82locale((char*) *code);
9738 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9739 isds_log(ILF_ISDS, ILL_DEBUG,
9740 _("Server refused %s request for %s message ID "
9741 "(code=%s, message=%s)\n"),
9742 service_name_locale, message_id_locale,
9743 code_locale, status_message_locale);
9744 isds_log_message(context, status_message_locale);
9745 free(code_locale);
9746 free(status_message_locale);
9747 err = IE_ISDS;
9748 goto leave;
9751 leave:
9752 free(message_id_locale);
9753 free(service_name_locale);
9754 xmlFreeNode(request);
9755 return err;
9759 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9760 * signed data and free ISDS response.
9761 * @context is session context
9762 * @message_id is UTF-8 encoded message ID for logging purpose
9763 * @response is parsed XML document. It will be freed and NULLed in the middle
9764 * of function run to save memory. This is not guaranteed in case of error.
9765 * @request_name is name of ISDS request used to construct response root
9766 * element name and for logging purpose.
9767 * @raw is reallocated output buffer with DER encoded CMS data
9768 * @raw_length is size of @raw buffer in bytes
9769 * @returns standard error codes, in case of error, @raw will be freed and
9770 * NULLed, @response sometimes. */
9771 static isds_error find_extract_signed_data_free_response(
9772 struct isds_ctx *context, const xmlChar *message_id,
9773 xmlDocPtr *response, const xmlChar *request_name,
9774 void **raw, size_t *raw_length) {
9776 isds_error err = IE_SUCCESS;
9777 char *xpath_expression = NULL;
9778 xmlXPathContextPtr xpath_ctx = NULL;
9779 xmlXPathObjectPtr result = NULL;
9780 char *encoded_structure = NULL;
9782 if (!context) return IE_INVALID_CONTEXT;
9783 if (!raw) return IE_INVAL;
9784 zfree(*raw);
9785 if (!message_id || !response || !*response || !request_name || !raw_length)
9786 return IE_INVAL;
9788 /* Build XPath expression */
9789 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9790 "Response/isds:dmSignature");
9791 if (!xpath_expression) return IE_NOMEM;
9793 /* Extract data */
9794 xpath_ctx = xmlXPathNewContext(*response);
9795 if (!xpath_ctx) {
9796 err = IE_ERROR;
9797 goto leave;
9799 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9800 err = IE_ERROR;
9801 goto leave;
9803 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9804 if (!result) {
9805 err = IE_ERROR;
9806 goto leave;
9808 /* Empty response */
9809 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9810 char *message_id_locale = _isds_utf82locale((char*) message_id);
9811 isds_printf_message(context,
9812 _("Server did not return any signed data for message ID `%s' "
9813 "on %s request"),
9814 message_id_locale, request_name);
9815 free(message_id_locale);
9816 err = IE_ISDS;
9817 goto leave;
9819 /* More responses */
9820 if (result->nodesetval->nodeNr > 1) {
9821 char *message_id_locale = _isds_utf82locale((char*) message_id);
9822 isds_printf_message(context,
9823 _("Server did return more signed data for message ID `%s' "
9824 "on %s request"),
9825 message_id_locale, request_name);
9826 free(message_id_locale);
9827 err = IE_ISDS;
9828 goto leave;
9830 /* One response */
9831 xpath_ctx->node = result->nodesetval->nodeTab[0];
9833 /* Extract PKCS#7 structure */
9834 EXTRACT_STRING(".", encoded_structure);
9835 if (!encoded_structure) {
9836 isds_log_message(context, _("dmSignature element is empty"));
9839 /* Here we have delivery info as standalone CMS in encoded_structure.
9840 * We don't need any other data, free them: */
9841 xmlXPathFreeObject(result); result = NULL;
9842 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9843 xmlFreeDoc(*response); *response = NULL;
9846 /* Decode PKCS#7 to DER format */
9847 *raw_length = _isds_b64decode(encoded_structure, raw);
9848 if (*raw_length == (size_t) -1) {
9849 isds_log_message(context,
9850 _("Error while Base64-decoding PKCS#7 structure"));
9851 err = IE_ERROR;
9852 goto leave;
9855 leave:
9856 if (err) {
9857 zfree(*raw);
9858 raw_length = 0;
9861 free(encoded_structure);
9862 xmlXPathFreeObject(result);
9863 xmlXPathFreeContext(xpath_ctx);
9864 free(xpath_expression);
9866 return err;
9868 #endif /* HAVE_LIBCURL */
9871 /* Download incoming message envelope identified by ID.
9872 * @context is session context
9873 * @message_id is message identifier (you can get them from
9874 * isds_get_list_of_received_messages())
9875 * @message is automatically reallocated message retrieved from ISDS.
9876 * It will miss documents per se. Use isds_get_received_message(), if you are
9877 * interested in documents (content) too.
9878 * Returned hash and timestamp require documents to be verifiable. */
9879 isds_error isds_get_received_envelope(struct isds_ctx *context,
9880 const char *message_id, struct isds_message **message) {
9882 isds_error err = IE_SUCCESS;
9883 #if HAVE_LIBCURL
9884 xmlDocPtr response = NULL;
9885 xmlChar *code = NULL, *status_message = NULL;
9886 xmlXPathContextPtr xpath_ctx = NULL;
9887 xmlXPathObjectPtr result = NULL;
9888 #endif
9890 if (!context) return IE_INVALID_CONTEXT;
9891 zfree(context->long_message);
9893 /* Free former message if any */
9894 if (!message) return IE_INVAL;
9895 isds_message_free(message);
9897 #if HAVE_LIBCURL
9898 /* Do request and check for success */
9899 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9900 BAD_CAST "MessageEnvelopeDownload", message_id,
9901 &response, NULL, NULL, &code, &status_message);
9902 if (err) goto leave;
9904 /* Extract data */
9905 xpath_ctx = xmlXPathNewContext(response);
9906 if (!xpath_ctx) {
9907 err = IE_ERROR;
9908 goto leave;
9910 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9911 err = IE_ERROR;
9912 goto leave;
9914 result = xmlXPathEvalExpression(
9915 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9916 "isds:dmReturnedMessageEnvelope",
9917 xpath_ctx);
9918 if (!result) {
9919 err = IE_ERROR;
9920 goto leave;
9922 /* Empty response */
9923 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9924 char *message_id_locale = _isds_utf82locale((char*) message_id);
9925 isds_printf_message(context,
9926 _("Server did not return any envelope for ID `%s' "
9927 "on MessageEnvelopeDownload request"), message_id_locale);
9928 free(message_id_locale);
9929 err = IE_ISDS;
9930 goto leave;
9932 /* More envelops */
9933 if (result->nodesetval->nodeNr > 1) {
9934 char *message_id_locale = _isds_utf82locale((char*) message_id);
9935 isds_printf_message(context,
9936 _("Server did return more envelopes for ID `%s' "
9937 "on MessageEnvelopeDownload request"), message_id_locale);
9938 free(message_id_locale);
9939 err = IE_ISDS;
9940 goto leave;
9942 /* One message */
9943 xpath_ctx->node = result->nodesetval->nodeTab[0];
9945 /* Extract the envelope (= message without documents, hence 0) */
9946 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9947 if (err) goto leave;
9949 /* Save XML blob */
9950 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9951 &(*message)->raw_length);
9953 leave:
9954 if (err) {
9955 isds_message_free(message);
9958 xmlXPathFreeObject(result);
9959 xmlXPathFreeContext(xpath_ctx);
9961 free(code);
9962 free(status_message);
9963 if (!*message || !(*message)->xml) {
9964 xmlFreeDoc(response);
9967 if (!err)
9968 isds_log(ILF_ISDS, ILL_DEBUG,
9969 _("MessageEnvelopeDownload request processed by server "
9970 "successfully.\n")
9972 #else /* not HAVE_LIBCURL */
9973 err = IE_NOTSUP;
9974 #endif
9975 return err;
9979 /* Load delivery info of any format from buffer.
9980 * @context is session context
9981 * @raw_type advertises format of @buffer content. Only delivery info types
9982 * are accepted.
9983 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9984 * retrieve such data from message->raw after calling
9985 * isds_get_signed_delivery_info().
9986 * @length is length of buffer in bytes.
9987 * @message is automatically reallocated message parsed from @buffer.
9988 * @strategy selects how buffer will be attached into raw isds_message member.
9989 * */
9990 isds_error isds_load_delivery_info(struct isds_ctx *context,
9991 const isds_raw_type raw_type,
9992 const void *buffer, const size_t length,
9993 struct isds_message **message, const isds_buffer_strategy strategy) {
9995 isds_error err = IE_SUCCESS;
9996 message_ns_type message_ns;
9997 xmlDocPtr message_doc = NULL;
9998 xmlXPathContextPtr xpath_ctx = NULL;
9999 xmlXPathObjectPtr result = NULL;
10000 void *xml_stream = NULL;
10001 size_t xml_stream_length = 0;
10003 if (!context) return IE_INVALID_CONTEXT;
10004 zfree(context->long_message);
10005 if (!message) return IE_INVAL;
10006 isds_message_free(message);
10007 if (!buffer) return IE_INVAL;
10010 /* Select buffer format and extract XML from CMS*/
10011 switch (raw_type) {
10012 case RAWTYPE_DELIVERYINFO:
10013 message_ns = MESSAGE_NS_UNSIGNED;
10014 xml_stream = (void *) buffer;
10015 xml_stream_length = length;
10016 break;
10018 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
10019 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10020 xml_stream = (void *) buffer;
10021 xml_stream_length = length;
10022 break;
10024 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10025 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10026 err = _isds_extract_cms_data(context, buffer, length,
10027 &xml_stream, &xml_stream_length);
10028 if (err) goto leave;
10029 break;
10031 default:
10032 isds_log_message(context, _("Bad raw delivery representation type"));
10033 return IE_INVAL;
10034 break;
10037 isds_log(ILF_ISDS, ILL_DEBUG,
10038 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10039 xml_stream_length, xml_stream);
10041 /* Convert delivery info XML stream into XPath context */
10042 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10043 if (!message_doc) {
10044 err = IE_XML;
10045 goto leave;
10047 xpath_ctx = xmlXPathNewContext(message_doc);
10048 if (!xpath_ctx) {
10049 err = IE_ERROR;
10050 goto leave;
10052 /* XXX: Name spaces mangled for signed delivery info:
10053 * http://isds.czechpoint.cz/v20/delivery:
10055 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10056 * <q:dmDelivery>
10057 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10058 * <p:dmID>170272</p:dmID>
10059 * ...
10060 * </p:dmDm>
10061 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10062 * ...
10063 * </q:dmEvents>...</q:dmEvents>
10064 * </q:dmDelivery>
10065 * </q:GetDeliveryInfoResponse>
10066 * */
10067 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10068 err = IE_ERROR;
10069 goto leave;
10071 result = xmlXPathEvalExpression(
10072 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10073 xpath_ctx);
10074 if (!result) {
10075 err = IE_ERROR;
10076 goto leave;
10078 /* Empty delivery info */
10079 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10080 isds_printf_message(context,
10081 _("XML document is not sisds:dmDelivery document"));
10082 err = IE_ISDS;
10083 goto leave;
10085 /* More delivery info's */
10086 if (result->nodesetval->nodeNr > 1) {
10087 isds_printf_message(context,
10088 _("XML document has more sisds:dmDelivery elements"));
10089 err = IE_ISDS;
10090 goto leave;
10092 /* One delivery info */
10093 xpath_ctx->node = result->nodesetval->nodeTab[0];
10095 /* Extract the envelope (= message without documents, hence 0).
10096 * XXX: extract_TReturnedMessage() can obtain attachments size,
10097 * but delivery info carries none. It's coded as option elements,
10098 * so it should work. */
10099 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10100 if (err) goto leave;
10102 /* Extract events */
10103 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10104 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10105 if (err) { err = IE_ERROR; goto leave; }
10106 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10107 if (err) goto leave;
10109 /* Append raw CMS structure into message */
10110 (*message)->raw_type = raw_type;
10111 switch (strategy) {
10112 case BUFFER_DONT_STORE:
10113 break;
10114 case BUFFER_COPY:
10115 (*message)->raw = malloc(length);
10116 if (!(*message)->raw) {
10117 err = IE_NOMEM;
10118 goto leave;
10120 memcpy((*message)->raw, buffer, length);
10121 (*message)->raw_length = length;
10122 break;
10123 case BUFFER_MOVE:
10124 (*message)->raw = (void *) buffer;
10125 (*message)->raw_length = length;
10126 break;
10127 default:
10128 err = IE_ENUM;
10129 goto leave;
10132 leave:
10133 if (err) {
10134 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10135 isds_message_free(message);
10138 xmlXPathFreeObject(result);
10139 xmlXPathFreeContext(xpath_ctx);
10140 if (!*message || !(*message)->xml) {
10141 xmlFreeDoc(message_doc);
10143 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10145 if (!err)
10146 isds_log(ILF_ISDS, ILL_DEBUG,
10147 _("Delivery info loaded successfully.\n"));
10148 return err;
10152 /* Download signed delivery info-sheet of given message identified by ID.
10153 * @context is session context
10154 * @message_id is message identifier (you can get them from
10155 * isds_get_list_of_{sent,received}_messages())
10156 * @message is automatically reallocated message retrieved from ISDS.
10157 * It will miss documents per se. Use isds_get_signed_received_message(),
10158 * if you are interested in documents (content). OTOH, only this function
10159 * can get list events message has gone through. */
10160 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10161 const char *message_id, struct isds_message **message) {
10163 isds_error err = IE_SUCCESS;
10164 #if HAVE_LIBCURL
10165 xmlDocPtr response = NULL;
10166 xmlChar *code = NULL, *status_message = NULL;
10167 void *raw = NULL;
10168 size_t raw_length = 0;
10169 #endif
10171 if (!context) return IE_INVALID_CONTEXT;
10172 zfree(context->long_message);
10174 /* Free former message if any */
10175 if (!message) return IE_INVAL;
10176 isds_message_free(message);
10178 #if HAVE_LIBCURL
10179 /* Do request and check for success */
10180 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10181 BAD_CAST "GetSignedDeliveryInfo", message_id,
10182 &response, NULL, NULL, &code, &status_message);
10183 if (err) goto leave;
10185 /* Find signed delivery info, extract it into raw and maybe free
10186 * response */
10187 err = find_extract_signed_data_free_response(context,
10188 (xmlChar *)message_id, &response,
10189 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10190 if (err) goto leave;
10192 /* Parse delivery info */
10193 err = isds_load_delivery_info(context,
10194 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10195 message, BUFFER_MOVE);
10196 if (err) goto leave;
10198 raw = NULL;
10200 leave:
10201 if (err) {
10202 isds_message_free(message);
10205 free(raw);
10206 free(code);
10207 free(status_message);
10208 xmlFreeDoc(response);
10210 if (!err)
10211 isds_log(ILF_ISDS, ILL_DEBUG,
10212 _("GetSignedDeliveryInfo request processed by server "
10213 "successfully.\n")
10215 #else /* not HAVE_LIBCURL */
10216 err = IE_NOTSUP;
10217 #endif
10218 return err;
10222 /* Download delivery info-sheet of given message identified by ID.
10223 * @context is session context
10224 * @message_id is message identifier (you can get them from
10225 * isds_get_list_of_{sent,received}_messages())
10226 * @message is automatically reallocated message retrieved from ISDS.
10227 * It will miss documents per se. Use isds_get_received_message(), if you are
10228 * interested in documents (content). OTOH, only this function can get list
10229 * of events message has gone through. */
10230 isds_error isds_get_delivery_info(struct isds_ctx *context,
10231 const char *message_id, struct isds_message **message) {
10233 isds_error err = IE_SUCCESS;
10234 #if HAVE_LIBCURL
10235 xmlDocPtr response = NULL;
10236 xmlChar *code = NULL, *status_message = NULL;
10237 xmlNodePtr delivery_node = NULL;
10238 void *raw = NULL;
10239 size_t raw_length = 0;
10240 #endif
10242 if (!context) return IE_INVALID_CONTEXT;
10243 zfree(context->long_message);
10245 /* Free former message if any */
10246 if (!message) return IE_INVAL;
10247 isds_message_free(message);
10249 #if HAVE_LIBCURL
10250 /* Do request and check for success */
10251 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10252 BAD_CAST "GetDeliveryInfo", message_id,
10253 &response, NULL, NULL, &code, &status_message);
10254 if (err) goto leave;
10257 /* Serialize delivery info */
10258 delivery_node = xmlDocGetRootElement(response);
10259 if (!delivery_node) {
10260 char *message_id_locale = _isds_utf82locale((char*) message_id);
10261 isds_printf_message(context,
10262 _("Server did not return any delivery info for ID `%s' "
10263 "on GetDeliveryInfo request"), message_id_locale);
10264 free(message_id_locale);
10265 err = IE_ISDS;
10266 goto leave;
10268 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10269 if (err) goto leave;
10271 /* Parse delivery info */
10272 /* TODO: Here we parse the response second time. We could single delivery
10273 * parser from isds_load_delivery_info() to make things faster. */
10274 err = isds_load_delivery_info(context,
10275 RAWTYPE_DELIVERYINFO, raw, raw_length,
10276 message, BUFFER_MOVE);
10277 if (err) goto leave;
10279 raw = NULL;
10282 leave:
10283 if (err) {
10284 isds_message_free(message);
10287 free(raw);
10288 free(code);
10289 free(status_message);
10290 xmlFreeDoc(response);
10292 if (!err)
10293 isds_log(ILF_ISDS, ILL_DEBUG,
10294 _("GetDeliveryInfo request processed by server "
10295 "successfully.\n")
10297 #else /* not HAVE_LIBCURL */
10298 err = IE_NOTSUP;
10299 #endif
10300 return err;
10304 /* Download incoming message identified by ID.
10305 * @context is session context
10306 * @message_id is message identifier (you can get them from
10307 * isds_get_list_of_received_messages())
10308 * @message is automatically reallocated message retrieved from ISDS */
10309 isds_error isds_get_received_message(struct isds_ctx *context,
10310 const char *message_id, struct isds_message **message) {
10312 isds_error err = IE_SUCCESS;
10313 #if HAVE_LIBCURL
10314 xmlDocPtr response = NULL;
10315 void *xml_stream = NULL;
10316 size_t xml_stream_length;
10317 xmlChar *code = NULL, *status_message = NULL;
10318 xmlXPathContextPtr xpath_ctx = NULL;
10319 xmlXPathObjectPtr result = NULL;
10320 char *phys_path = NULL;
10321 size_t phys_start, phys_end;
10322 #endif
10324 if (!context) return IE_INVALID_CONTEXT;
10325 zfree(context->long_message);
10327 /* Free former message if any */
10328 if (NULL == message) return IE_INVAL;
10329 if (message) isds_message_free(message);
10331 #if HAVE_LIBCURL
10332 /* Do request and check for success */
10333 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10334 BAD_CAST "MessageDownload", message_id,
10335 &response, &xml_stream, &xml_stream_length,
10336 &code, &status_message);
10337 if (err) goto leave;
10339 /* Extract data */
10340 xpath_ctx = xmlXPathNewContext(response);
10341 if (!xpath_ctx) {
10342 err = IE_ERROR;
10343 goto leave;
10345 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10346 err = IE_ERROR;
10347 goto leave;
10349 result = xmlXPathEvalExpression(
10350 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10351 xpath_ctx);
10352 if (!result) {
10353 err = IE_ERROR;
10354 goto leave;
10356 /* Empty response */
10357 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10358 char *message_id_locale = _isds_utf82locale((char*) message_id);
10359 isds_printf_message(context,
10360 _("Server did not return any message for ID `%s' "
10361 "on MessageDownload request"), message_id_locale);
10362 free(message_id_locale);
10363 err = IE_ISDS;
10364 goto leave;
10366 /* More messages */
10367 if (result->nodesetval->nodeNr > 1) {
10368 char *message_id_locale = _isds_utf82locale((char*) message_id);
10369 isds_printf_message(context,
10370 _("Server did return more messages for ID `%s' "
10371 "on MessageDownload request"), message_id_locale);
10372 free(message_id_locale);
10373 err = IE_ISDS;
10374 goto leave;
10376 /* One message */
10377 xpath_ctx->node = result->nodesetval->nodeTab[0];
10379 /* Extract the message */
10380 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10381 if (err) goto leave;
10383 /* Locate raw XML blob */
10384 phys_path = strdup(
10385 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10386 PHYSXML_ELEMENT_SEPARATOR
10387 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10388 PHYSXML_ELEMENT_SEPARATOR
10389 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10391 if (!phys_path) {
10392 err = IE_NOMEM;
10393 goto leave;
10395 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10396 phys_path, &phys_start, &phys_end);
10397 zfree(phys_path);
10398 if (err) {
10399 isds_log_message(context,
10400 _("Substring with isds:MessageDownloadResponse element "
10401 "could not be located in raw SOAP message"));
10402 goto leave;
10404 /* Save XML blob */
10405 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10406 &(*message)->raw_length);*/
10407 /* TODO: Store name space declarations from ancestors */
10408 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10409 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10410 (*message)->raw_length = phys_end - phys_start + 1;
10411 (*message)->raw = malloc((*message)->raw_length);
10412 if (!(*message)->raw) {
10413 err = IE_NOMEM;
10414 goto leave;
10416 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10419 leave:
10420 if (err) {
10421 isds_message_free(message);
10424 free(phys_path);
10426 xmlXPathFreeObject(result);
10427 xmlXPathFreeContext(xpath_ctx);
10429 free(code);
10430 free(status_message);
10431 free(xml_stream);
10432 if (!*message || !(*message)->xml) {
10433 xmlFreeDoc(response);
10436 if (!err)
10437 isds_log(ILF_ISDS, ILL_DEBUG,
10438 _("MessageDownload request processed by server "
10439 "successfully.\n")
10441 #else /* not HAVE_LIBCURL */
10442 err = IE_NOTSUP;
10443 #endif
10444 return err;
10448 /* Load message of any type from buffer.
10449 * @context is session context
10450 * @raw_type defines content type of @buffer. Only message types are allowed.
10451 * @buffer is message raw representation. Format (CMS, plain signed,
10452 * message direction) is defined in @raw_type. You can retrieve such data
10453 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10454 * @length is length of buffer in bytes.
10455 * @message is automatically reallocated message parsed from @buffer.
10456 * @strategy selects how buffer will be attached into raw isds_message member.
10457 * */
10458 isds_error isds_load_message(struct isds_ctx *context,
10459 const isds_raw_type raw_type, const void *buffer, const size_t length,
10460 struct isds_message **message, const isds_buffer_strategy strategy) {
10462 isds_error err = IE_SUCCESS;
10463 void *xml_stream = NULL;
10464 size_t xml_stream_length = 0;
10465 message_ns_type message_ns;
10466 xmlDocPtr message_doc = NULL;
10467 xmlXPathContextPtr xpath_ctx = NULL;
10468 xmlXPathObjectPtr result = NULL;
10470 if (!context) return IE_INVALID_CONTEXT;
10471 zfree(context->long_message);
10472 if (!message) return IE_INVAL;
10473 isds_message_free(message);
10474 if (!buffer) return IE_INVAL;
10477 /* Select buffer format and extract XML from CMS*/
10478 switch (raw_type) {
10479 case RAWTYPE_INCOMING_MESSAGE:
10480 message_ns = MESSAGE_NS_UNSIGNED;
10481 xml_stream = (void *) buffer;
10482 xml_stream_length = length;
10483 break;
10485 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10486 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10487 xml_stream = (void *) buffer;
10488 xml_stream_length = length;
10489 break;
10491 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10492 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10493 err = _isds_extract_cms_data(context, buffer, length,
10494 &xml_stream, &xml_stream_length);
10495 if (err) goto leave;
10496 break;
10498 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10499 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10500 xml_stream = (void *) buffer;
10501 xml_stream_length = length;
10502 break;
10504 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10505 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10506 err = _isds_extract_cms_data(context, buffer, length,
10507 &xml_stream, &xml_stream_length);
10508 if (err) goto leave;
10509 break;
10511 default:
10512 isds_log_message(context, _("Bad raw message representation type"));
10513 return IE_INVAL;
10514 break;
10517 isds_log(ILF_ISDS, ILL_DEBUG,
10518 _("Loading message:\n%.*s\nEnd of message\n"),
10519 xml_stream_length, xml_stream);
10521 /* Convert messages XML stream into XPath context */
10522 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10523 if (!message_doc) {
10524 err = IE_XML;
10525 goto leave;
10527 xpath_ctx = xmlXPathNewContext(message_doc);
10528 if (!xpath_ctx) {
10529 err = IE_ERROR;
10530 goto leave;
10532 /* XXX: Standard name space for unsigned incoming direction:
10533 * http://isds.czechpoint.cz/v20/
10535 * XXX: Name spaces mangled for signed outgoing direction:
10536 * http://isds.czechpoint.cz/v20/SentMessage:
10538 * <q:MessageDownloadResponse
10539 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10540 * <q:dmReturnedMessage>
10541 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10542 * <p:dmID>151916</p:dmID>
10543 * ...
10544 * </p:dmDm>
10545 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10546 * ...
10547 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10548 * </q:dmReturnedMessage>
10549 * </q:MessageDownloadResponse>
10551 * XXX: Name spaces mangled for signed incoming direction:
10552 * http://isds.czechpoint.cz/v20/message:
10554 * <q:MessageDownloadResponse
10555 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10556 * <q:dmReturnedMessage>
10557 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10558 * <p:dmID>151916</p:dmID>
10559 * ...
10560 * </p:dmDm>
10561 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10562 * ...
10563 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10564 * </q:dmReturnedMessage>
10565 * </q:MessageDownloadResponse>
10567 * Stupidity of ISDS developers is unlimited */
10568 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10569 err = IE_ERROR;
10570 goto leave;
10572 result = xmlXPathEvalExpression(
10573 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10574 xpath_ctx);
10575 if (!result) {
10576 err = IE_ERROR;
10577 goto leave;
10579 /* Empty message */
10580 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10581 isds_printf_message(context,
10582 _("XML document does not contain "
10583 "sisds:dmReturnedMessage element"));
10584 err = IE_ISDS;
10585 goto leave;
10587 /* More messages */
10588 if (result->nodesetval->nodeNr > 1) {
10589 isds_printf_message(context,
10590 _("XML document has more sisds:dmReturnedMessage elements"));
10591 err = IE_ISDS;
10592 goto leave;
10594 /* One message */
10595 xpath_ctx->node = result->nodesetval->nodeTab[0];
10597 /* Extract the message */
10598 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10599 if (err) goto leave;
10601 /* Append raw buffer into message */
10602 (*message)->raw_type = raw_type;
10603 switch (strategy) {
10604 case BUFFER_DONT_STORE:
10605 break;
10606 case BUFFER_COPY:
10607 (*message)->raw = malloc(length);
10608 if (!(*message)->raw) {
10609 err = IE_NOMEM;
10610 goto leave;
10612 memcpy((*message)->raw, buffer, length);
10613 (*message)->raw_length = length;
10614 break;
10615 case BUFFER_MOVE:
10616 (*message)->raw = (void *) buffer;
10617 (*message)->raw_length = length;
10618 break;
10619 default:
10620 err = IE_ENUM;
10621 goto leave;
10625 leave:
10626 if (err) {
10627 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10628 isds_message_free(message);
10631 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10632 xmlXPathFreeObject(result);
10633 xmlXPathFreeContext(xpath_ctx);
10634 if (!*message || !(*message)->xml) {
10635 xmlFreeDoc(message_doc);
10638 if (!err)
10639 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10640 return err;
10644 /* Determine type of raw message or delivery info according some heuristics.
10645 * It does not validate the raw blob.
10646 * @context is session context
10647 * @raw_type returns content type of @buffer. Valid only if exit code of this
10648 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10649 * reallocated memory.
10650 * @buffer is message raw representation.
10651 * @length is length of buffer in bytes. */
10652 isds_error isds_guess_raw_type(struct isds_ctx *context,
10653 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10654 isds_error err;
10655 void *xml_stream = NULL;
10656 size_t xml_stream_length = 0;
10657 xmlDocPtr document = NULL;
10658 xmlNodePtr root = NULL;
10660 if (!context) return IE_INVALID_CONTEXT;
10661 zfree(context->long_message);
10662 if (length == 0 || !buffer) return IE_INVAL;
10663 if (!raw_type) return IE_INVAL;
10665 /* Try CMS */
10666 err = _isds_extract_cms_data(context, buffer, length,
10667 &xml_stream, &xml_stream_length);
10668 if (err) {
10669 xml_stream = (void *) buffer;
10670 xml_stream_length = (size_t) length;
10671 err = IE_SUCCESS;
10674 /* Try XML */
10675 document = xmlParseMemory(xml_stream, xml_stream_length);
10676 if (!document) {
10677 isds_printf_message(context,
10678 _("Could not parse data as XML document"));
10679 err = IE_NOTSUP;
10680 goto leave;
10683 /* Get root element */
10684 root = xmlDocGetRootElement(document);
10685 if (!root) {
10686 isds_printf_message(context,
10687 _("XML document is missing root element"));
10688 err = IE_XML;
10689 goto leave;
10692 if (!root->ns || !root->ns->href) {
10693 isds_printf_message(context,
10694 _("Root element does not belong to any name space"));
10695 err = IE_NOTSUP;
10696 goto leave;
10699 /* Test name space */
10700 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10701 if (xml_stream == buffer)
10702 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10703 else
10704 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10705 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10706 if (xml_stream == buffer)
10707 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10708 else
10709 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10710 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10711 if (xml_stream == buffer)
10712 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10713 else
10714 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10715 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10716 if (xml_stream != buffer) {
10717 isds_printf_message(context,
10718 _("Document in ISDS name space is encapsulated into CMS" ));
10719 err = IE_NOTSUP;
10720 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10721 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10722 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10723 *raw_type = RAWTYPE_DELIVERYINFO;
10724 else {
10725 isds_printf_message(context,
10726 _("Unknown root element in ISDS name space"));
10727 err = IE_NOTSUP;
10729 } else {
10730 isds_printf_message(context,
10731 _("Unknown name space"));
10732 err = IE_NOTSUP;
10735 leave:
10736 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10737 xmlFreeDoc(document);
10738 return err;
10742 /* Download signed incoming/outgoing message identified by ID.
10743 * @context is session context
10744 * @output is true for outgoing message, false for incoming message
10745 * @message_id is message identifier (you can get them from
10746 * isds_get_list_of_{sent,received}_messages())
10747 * @message is automatically reallocated message retrieved from ISDS. The raw
10748 * member will be filled with PKCS#7 structure in DER format. */
10749 static isds_error isds_get_signed_message(struct isds_ctx *context,
10750 const _Bool outgoing, const char *message_id,
10751 struct isds_message **message) {
10753 isds_error err = IE_SUCCESS;
10754 #if HAVE_LIBCURL
10755 xmlDocPtr response = NULL;
10756 xmlChar *code = NULL, *status_message = NULL;
10757 xmlXPathContextPtr xpath_ctx = NULL;
10758 xmlXPathObjectPtr result = NULL;
10759 char *encoded_structure = NULL;
10760 void *raw = NULL;
10761 size_t raw_length = 0;
10762 #endif
10764 if (!context) return IE_INVALID_CONTEXT;
10765 zfree(context->long_message);
10766 if (!message) return IE_INVAL;
10767 isds_message_free(message);
10769 #if HAVE_LIBCURL
10770 /* Do request and check for success */
10771 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10772 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10773 BAD_CAST "SignedMessageDownload",
10774 message_id, &response, NULL, NULL, &code, &status_message);
10775 if (err) goto leave;
10777 /* Find signed message, extract it into raw and maybe free
10778 * response */
10779 err = find_extract_signed_data_free_response(context,
10780 (xmlChar *)message_id, &response,
10781 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10782 BAD_CAST "SignedMessageDownload",
10783 &raw, &raw_length);
10784 if (err) goto leave;
10786 /* Parse message */
10787 err = isds_load_message(context,
10788 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10789 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10790 raw, raw_length, message, BUFFER_MOVE);
10791 if (err) goto leave;
10793 raw = NULL;
10795 leave:
10796 if (err) {
10797 isds_message_free(message);
10800 free(encoded_structure);
10801 xmlXPathFreeObject(result);
10802 xmlXPathFreeContext(xpath_ctx);
10803 free(raw);
10805 free(code);
10806 free(status_message);
10807 xmlFreeDoc(response);
10809 if (!err)
10810 isds_log(ILF_ISDS, ILL_DEBUG,
10811 (outgoing) ?
10812 _("SignedSentMessageDownload request processed by server "
10813 "successfully.\n") :
10814 _("SignedMessageDownload request processed by server "
10815 "successfully.\n")
10817 #else /* not HAVE_LIBCURL */
10818 err = IE_NOTSUP;
10819 #endif
10820 return err;
10824 /* Download signed incoming message identified by ID.
10825 * @context is session context
10826 * @message_id is message identifier (you can get them from
10827 * isds_get_list_of_received_messages())
10828 * @message is automatically reallocated message retrieved from ISDS. The raw
10829 * member will be filled with PKCS#7 structure in DER format. */
10830 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10831 const char *message_id, struct isds_message **message) {
10832 return isds_get_signed_message(context, 0, message_id, message);
10836 /* Download signed outgoing message identified by ID.
10837 * @context is session context
10838 * @message_id is message identifier (you can get them from
10839 * isds_get_list_of_sent_messages())
10840 * @message is automatically reallocated message retrieved from ISDS. The raw
10841 * member will be filled with PKCS#7 structure in DER format. */
10842 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10843 const char *message_id, struct isds_message **message) {
10844 return isds_get_signed_message(context, 1, message_id, message);
10848 /* Get type and name of user who sent a message identified by ID.
10849 * @context is session context
10850 * @message_id is message identifier
10851 * @sender_type is pointer to automatically allocated type of sender detected
10852 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10853 * library or to the server, NULL will be returned. Pass NULL if you don't
10854 * care about it.
10855 * @raw_sender_type is automatically reallocated UTF-8 string describing
10856 * sender type or NULL if not known to server. Pass NULL if you don't care.
10857 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10858 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10859 isds_error isds_get_message_sender(struct isds_ctx *context,
10860 const char *message_id, isds_sender_type **sender_type,
10861 char **raw_sender_type, char **sender_name) {
10862 isds_error err = IE_SUCCESS;
10863 #if HAVE_LIBCURL
10864 xmlDocPtr response = NULL;
10865 xmlChar *code = NULL, *status_message = NULL;
10866 xmlXPathContextPtr xpath_ctx = NULL;
10867 xmlXPathObjectPtr result = NULL;
10868 char *type_string = NULL;
10869 #endif
10871 if (!context) return IE_INVALID_CONTEXT;
10872 zfree(context->long_message);
10873 if (sender_type) zfree(*sender_type);
10874 if (raw_sender_type) zfree(*raw_sender_type);
10875 if (sender_name) zfree(*sender_name);
10876 if (!message_id) return IE_INVAL;
10878 #if HAVE_LIBCURL
10879 /* Do request and check for success */
10880 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10881 BAD_CAST "GetMessageAuthor",
10882 message_id, &response, NULL, NULL, &code, &status_message);
10883 if (err) goto leave;
10885 /* Extract data */
10886 xpath_ctx = xmlXPathNewContext(response);
10887 if (!xpath_ctx) {
10888 err = IE_ERROR;
10889 goto leave;
10891 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10892 err = IE_ERROR;
10893 goto leave;
10895 result = xmlXPathEvalExpression(
10896 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10897 if (!result) {
10898 err = IE_ERROR;
10899 goto leave;
10901 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10902 isds_log_message(context,
10903 _("Missing GetMessageAuthorResponse element"));
10904 err = IE_ISDS;
10905 goto leave;
10907 if (result->nodesetval->nodeNr > 1) {
10908 isds_log_message(context,
10909 _("Multiple GetMessageAuthorResponse element"));
10910 err = IE_ISDS;
10911 goto leave;
10913 xpath_ctx->node = result->nodesetval->nodeTab[0];
10914 xmlXPathFreeObject(result); result = NULL;
10916 /* Fill output arguments in */
10917 EXTRACT_STRING("isds:userType", type_string);
10918 if (NULL != type_string) {
10919 if (NULL != sender_type) {
10920 *sender_type = calloc(1, sizeof(**sender_type));
10921 if (NULL == *sender_type) {
10922 err = IE_NOMEM;
10923 goto leave;
10926 err = string2isds_sender_type((xmlChar *)type_string,
10927 *sender_type);
10928 if (err) {
10929 zfree(*sender_type);
10930 if (err == IE_ENUM) {
10931 err = IE_SUCCESS;
10932 char *type_string_locale = _isds_utf82locale(type_string);
10933 isds_log(ILF_ISDS, ILL_WARNING,
10934 _("Unknown isds:userType value: %s"),
10935 type_string_locale);
10936 free(type_string_locale);
10941 if (NULL != sender_name)
10942 EXTRACT_STRING("isds:authorName", *sender_name);
10944 leave:
10945 if (err) {
10946 if (NULL != sender_type) zfree(*sender_type);
10947 zfree(type_string);
10948 if (NULL != sender_name) zfree(*sender_name);
10950 if (NULL != raw_sender_type) *raw_sender_type = type_string;
10952 xmlXPathFreeObject(result);
10953 xmlXPathFreeContext(xpath_ctx);
10955 free(code);
10956 free(status_message);
10957 xmlFreeDoc(response);
10959 if (!err)
10960 isds_log(ILF_ISDS, ILL_DEBUG,
10961 _("GetMessageAuthor request processed by server "
10962 "successfully.\n"));
10963 #else /* not HAVE_LIBCURL */
10964 err = IE_NOTSUP;
10965 #endif
10966 return err;
10970 /* Retrieve hash of message identified by ID stored in ISDS.
10971 * @context is session context
10972 * @message_id is message identifier
10973 * @hash is automatically reallocated message hash downloaded from ISDS.
10974 * Message must exist in system and must not be deleted. */
10975 isds_error isds_download_message_hash(struct isds_ctx *context,
10976 const char *message_id, struct isds_hash **hash) {
10978 isds_error err = IE_SUCCESS;
10979 #if HAVE_LIBCURL
10980 xmlDocPtr response = NULL;
10981 xmlChar *code = NULL, *status_message = NULL;
10982 xmlXPathContextPtr xpath_ctx = NULL;
10983 xmlXPathObjectPtr result = NULL;
10984 #endif
10986 if (!context) return IE_INVALID_CONTEXT;
10987 zfree(context->long_message);
10989 isds_hash_free(hash);
10991 #if HAVE_LIBCURL
10992 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10993 BAD_CAST "VerifyMessage", message_id,
10994 &response, NULL, NULL, &code, &status_message);
10995 if (err) goto leave;
10998 /* Extract data */
10999 xpath_ctx = xmlXPathNewContext(response);
11000 if (!xpath_ctx) {
11001 err = IE_ERROR;
11002 goto leave;
11004 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11005 err = IE_ERROR;
11006 goto leave;
11008 result = xmlXPathEvalExpression(
11009 BAD_CAST "/isds:VerifyMessageResponse",
11010 xpath_ctx);
11011 if (!result) {
11012 err = IE_ERROR;
11013 goto leave;
11015 /* Empty response */
11016 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11017 char *message_id_locale = _isds_utf82locale((char*) message_id);
11018 isds_printf_message(context,
11019 _("Server did not return any response for ID `%s' "
11020 "on VerifyMessage request"), message_id_locale);
11021 free(message_id_locale);
11022 err = IE_ISDS;
11023 goto leave;
11025 /* More responses */
11026 if (result->nodesetval->nodeNr > 1) {
11027 char *message_id_locale = _isds_utf82locale((char*) message_id);
11028 isds_printf_message(context,
11029 _("Server did return more responses for ID `%s' "
11030 "on VerifyMessage request"), message_id_locale);
11031 free(message_id_locale);
11032 err = IE_ISDS;
11033 goto leave;
11035 /* One response */
11036 xpath_ctx->node = result->nodesetval->nodeTab[0];
11038 /* Extract the hash */
11039 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11041 leave:
11042 if (err) {
11043 isds_hash_free(hash);
11046 xmlXPathFreeObject(result);
11047 xmlXPathFreeContext(xpath_ctx);
11049 free(code);
11050 free(status_message);
11051 xmlFreeDoc(response);
11053 if (!err)
11054 isds_log(ILF_ISDS, ILL_DEBUG,
11055 _("VerifyMessage request processed by server "
11056 "successfully.\n")
11058 #else /* not HAVE_LIBCURL */
11059 err = IE_NOTSUP;
11060 #endif
11061 return err;
11065 /* Erase message specified by @message_id from long term storage. Other
11066 * message cannot be erased on user request.
11067 * @context is session context
11068 * @message_id is message identifier.
11069 * @incoming is true for incoming message, false for outgoing message.
11070 * @return
11071 * IE_SUCCESS if message has ben removed
11072 * IE_INVAL if message does not exist in long term storage or message
11073 * belongs to different box
11074 * TODO: IE_NOEPRM if user has no permission to erase a message */
11075 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11076 const char *message_id, _Bool incoming) {
11077 isds_error err = IE_SUCCESS;
11078 #if HAVE_LIBCURL
11079 xmlNodePtr request = NULL, node;
11080 xmlNsPtr isds_ns = NULL;
11081 xmlDocPtr response = NULL;
11082 xmlChar *code = NULL, *status_message = NULL;
11083 #endif
11085 if (!context) return IE_INVALID_CONTEXT;
11086 zfree(context->long_message);
11087 if (NULL == message_id) return IE_INVAL;
11089 /* Check if connection is established
11090 * TODO: This check should be done downstairs. */
11091 if (!context->curl) return IE_CONNECTION_CLOSED;
11093 #if HAVE_LIBCURL
11094 /* Build request */
11095 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11096 if (!request) {
11097 isds_log_message(context,
11098 _("Could build EraseMessage request"));
11099 return IE_ERROR;
11101 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11102 if(!isds_ns) {
11103 isds_log_message(context, _("Could not create ISDS name space"));
11104 xmlFreeNode(request);
11105 return IE_ERROR;
11107 xmlSetNs(request, isds_ns);
11109 err = validate_message_id_length(context, (xmlChar *) message_id);
11110 if (err) goto leave;
11111 INSERT_STRING(request, "dmID", message_id);
11113 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11116 /* Send request */
11117 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11118 "message ID %s to ISDS\n"), message_id);
11119 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11120 xmlFreeNode(request); request = NULL;
11122 if (err) {
11123 isds_log(ILF_ISDS, ILL_DEBUG,
11124 _("Processing ISDS response on EraseMessage request "
11125 "failed\n"));
11126 goto leave;
11129 /* Check for response status */
11130 err = isds_response_status(context, SERVICE_DM_INFO, response,
11131 &code, &status_message, NULL);
11132 if (err) {
11133 isds_log(ILF_ISDS, ILL_DEBUG,
11134 _("ISDS response on EraseMessage request is missing "
11135 "status\n"));
11136 goto leave;
11139 /* Check server status code */
11140 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11141 isds_log_message(context, _("Message to erase belongs to other box"));
11142 err = IE_INVAL;
11143 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11144 isds_log_message(context, _("Message to erase is not saved in "
11145 "long term storage or the direction does not match"));
11146 err = IE_INVAL;
11147 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11148 char *code_locale = _isds_utf82locale((char*) code);
11149 char *message_locale = _isds_utf82locale((char*) status_message);
11150 isds_log(ILF_ISDS, ILL_DEBUG,
11151 _("Server refused EraseMessage request "
11152 "(code=%s, message=%s)\n"),
11153 code_locale, message_locale);
11154 isds_log_message(context, message_locale);
11155 free(code_locale);
11156 free(message_locale);
11157 err = IE_ISDS;
11158 goto leave;
11161 leave:
11162 free(code);
11163 free(status_message);
11164 xmlFreeDoc(response);
11165 xmlFreeNode(request);
11167 if (!err)
11168 isds_log(ILF_ISDS, ILL_DEBUG,
11169 _("EraseMessage request processed by server "
11170 "successfully.\n")
11172 #else /* not HAVE_LIBCURL */
11173 err = IE_NOTSUP;
11174 #endif
11175 return err;
11179 /* Mark message as read. This is a transactional commit function to acknowledge
11180 * to ISDS the message has been downloaded and processed by client properly.
11181 * @context is session context
11182 * @message_id is message identifier. */
11183 isds_error isds_mark_message_read(struct isds_ctx *context,
11184 const char *message_id) {
11186 isds_error err = IE_SUCCESS;
11187 #if HAVE_LIBCURL
11188 xmlDocPtr response = NULL;
11189 xmlChar *code = NULL, *status_message = NULL;
11190 #endif
11192 if (!context) return IE_INVALID_CONTEXT;
11193 zfree(context->long_message);
11195 #if HAVE_LIBCURL
11196 /* Do request and check for success */
11197 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11198 BAD_CAST "MarkMessageAsDownloaded", message_id,
11199 &response, NULL, NULL, &code, &status_message);
11201 free(code);
11202 free(status_message);
11203 xmlFreeDoc(response);
11205 if (!err)
11206 isds_log(ILF_ISDS, ILL_DEBUG,
11207 _("MarkMessageAsDownloaded request processed by server "
11208 "successfully.\n")
11210 #else /* not HAVE_LIBCURL */
11211 err = IE_NOTSUP;
11212 #endif
11213 return err;
11217 /* Mark message as received by recipient. This is applicable only to
11218 * commercial message. Use envelope->dmType message member to distinguish
11219 * commercial message from government message. Government message is
11220 * received automatically (by law), commercial message on recipient request.
11221 * @context is session context
11222 * @message_id is message identifier. */
11223 isds_error isds_mark_message_received(struct isds_ctx *context,
11224 const char *message_id) {
11226 isds_error err = IE_SUCCESS;
11227 #if HAVE_LIBCURL
11228 xmlDocPtr response = NULL;
11229 xmlChar *code = NULL, *status_message = NULL;
11230 #endif
11232 if (!context) return IE_INVALID_CONTEXT;
11233 zfree(context->long_message);
11235 #if HAVE_LIBCURL
11236 /* Do request and check for success */
11237 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11238 BAD_CAST "ConfirmDelivery", message_id,
11239 &response, NULL, NULL, &code, &status_message);
11241 free(code);
11242 free(status_message);
11243 xmlFreeDoc(response);
11245 if (!err)
11246 isds_log(ILF_ISDS, ILL_DEBUG,
11247 _("ConfirmDelivery request processed by server "
11248 "successfully.\n")
11250 #else /* not HAVE_LIBCURL */
11251 err = IE_NOTSUP;
11252 #endif
11253 return err;
11257 /* Send document for authorized conversion into Czech POINT system.
11258 * This is public anonymous service, no log-in necessary. Special context is
11259 * used to reuse keep-a-live HTTPS connection.
11260 * @context is Czech POINT session context. DO NOT use context connected to
11261 * ISDS server. Use new context or context used by this function previously.
11262 * @document is document to convert. Only data, data_length, dmFileDescr and
11263 * is_xml members are significant. Be ware that not all document formats can be
11264 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11265 * @id is reallocated identifier assigned by Czech POINT system to
11266 * your document on submit. Use is to tell it to Czech POINT officer.
11267 * @date is reallocated document submit date (submitted documents
11268 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11269 * value. */
11270 isds_error czp_convert_document(struct isds_ctx *context,
11271 const struct isds_document *document,
11272 char **id, struct tm **date) {
11273 isds_error err = IE_SUCCESS;
11274 #if HAVE_LIBCURL
11275 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11276 xmlNodePtr request = NULL, node;
11277 xmlDocPtr response = NULL;
11279 xmlXPathContextPtr xpath_ctx = NULL;
11280 xmlXPathObjectPtr result = NULL;
11281 long int status = -1;
11282 long int *status_ptr = &status;
11283 char *string = NULL;
11284 #endif
11287 if (!context) return IE_INVALID_CONTEXT;
11288 zfree(context->long_message);
11289 if (!document || !id || !date) return IE_INVAL;
11291 if (document->is_xml) {
11292 isds_log_message(context,
11293 _("XML documents cannot be submitted to conversion"));
11294 return IE_NOTSUP;
11297 /* Free output arguments */
11298 zfree(*id);
11299 zfree(*date);
11301 #if HAVE_LIBCURL
11302 /* Store configuration */
11303 context->type = CTX_TYPE_CZP;
11304 free(context->url);
11305 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11306 if (!(context->url))
11307 return IE_NOMEM;
11309 /* Prepare CURL handle if not yet connected */
11310 if (!context->curl) {
11311 context->curl = curl_easy_init();
11312 if (!(context->curl))
11313 return IE_ERROR;
11316 /* Build conversion request */
11317 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11318 if (!request) {
11319 isds_log_message(context,
11320 _("Could not build Czech POINT conversion request"));
11321 return IE_ERROR;
11323 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11324 if(!deposit_ns) {
11325 isds_log_message(context,
11326 _("Could not create Czech POINT deposit name space"));
11327 xmlFreeNode(request);
11328 return IE_ERROR;
11330 xmlSetNs(request, deposit_ns);
11332 /* Insert children. They are in empty namespace! */
11333 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11334 if(!empty_ns) {
11335 isds_log_message(context, _("Could not create empty name space"));
11336 err = IE_ERROR;
11337 goto leave;
11339 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11340 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11341 document->dmFileDescr);
11343 /* Document encoded in Base64 */
11344 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11345 document->data, document->data_length);
11346 if (err) goto leave;
11348 isds_log(ILF_ISDS, ILL_DEBUG,
11349 _("Submitting document for conversion into Czech POINT deposit"));
11351 /* Send conversion request */
11352 err = _czp_czpdeposit(context, request, &response);
11353 xmlFreeNode(request); request = NULL;
11355 if (err) {
11356 czp_do_close_connection(context);
11357 goto leave;
11361 /* Extract response */
11362 xpath_ctx = xmlXPathNewContext(response);
11363 if (!xpath_ctx) {
11364 err = IE_ERROR;
11365 goto leave;
11367 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11368 err = IE_ERROR;
11369 goto leave;
11371 result = xmlXPathEvalExpression(
11372 BAD_CAST "/deposit:saveDocumentResponse/return",
11373 xpath_ctx);
11374 if (!result) {
11375 err = IE_ERROR;
11376 goto leave;
11378 /* Empty response */
11379 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11380 isds_printf_message(context,
11381 _("Missing `return' element in Czech POINT deposit response"));
11382 err = IE_ISDS;
11383 goto leave;
11385 /* More responses */
11386 if (result->nodesetval->nodeNr > 1) {
11387 isds_printf_message(context,
11388 _("Multiple `return' element in Czech POINT deposit response"));
11389 err = IE_ISDS;
11390 goto leave;
11392 /* One response */
11393 xpath_ctx->node = result->nodesetval->nodeTab[0];
11395 /* Get status */
11396 EXTRACT_LONGINT("status", status_ptr, 1);
11397 if (status) {
11398 EXTRACT_STRING("statusMsg", string);
11399 char *string_locale = _isds_utf82locale(string);
11400 isds_printf_message(context,
11401 _("Czech POINT deposit refused document for conversion "
11402 "(code=%ld, message=%s)"),
11403 status, string_locale);
11404 free(string_locale);
11405 err = IE_ISDS;
11406 goto leave;
11409 /* Get document ID */
11410 EXTRACT_STRING("documentID", *id);
11412 /* Get submit date */
11413 EXTRACT_STRING("dateInserted", string);
11414 if (string) {
11415 *date = calloc(1, sizeof(**date));
11416 if (!*date) {
11417 err = IE_NOMEM;
11418 goto leave;
11420 err = _isds_datestring2tm((xmlChar *)string, *date);
11421 if (err) {
11422 if (err == IE_NOTSUP) {
11423 err = IE_ISDS;
11424 char *string_locale = _isds_utf82locale(string);
11425 isds_printf_message(context,
11426 _("Invalid dateInserted value: %s"), string_locale);
11427 free(string_locale);
11429 goto leave;
11433 leave:
11434 free(string);
11435 xmlXPathFreeObject(result);
11436 xmlXPathFreeContext(xpath_ctx);
11438 xmlFreeDoc(response);
11439 xmlFreeNode(request);
11441 if (!err) {
11442 char *id_locale = _isds_utf82locale((char *) *id);
11443 isds_log(ILF_ISDS, ILL_DEBUG,
11444 _("Document %s has been submitted for conversion "
11445 "to server successfully\n"), id_locale);
11446 free(id_locale);
11448 #else /* not HAVE_LIBCURL */
11449 err = IE_NOTSUP;
11450 #endif
11451 return err;
11455 /* Close possibly opened connection to Czech POINT document deposit.
11456 * @context is Czech POINT session context. */
11457 isds_error czp_close_connection(struct isds_ctx *context) {
11458 if (!context) return IE_INVALID_CONTEXT;
11459 zfree(context->long_message);
11460 #if HAVE_LIBCURL
11461 return czp_do_close_connection(context);
11462 #else
11463 return IE_NOTSUP;
11464 #endif
11468 /* Send request for new box creation in testing ISDS instance.
11469 * It's not possible to request for a production box currently, as it
11470 * communicates via e-mail.
11471 * XXX: This function does not work either. Server complains about invalid
11472 * e-mail address.
11473 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11474 * this function
11475 * @context is special session context for box creation request. DO NOT use
11476 * standard context as it could reveal your password. Use fresh new context or
11477 * context previously used by this function.
11478 * @box is box description to create including single primary user (in case of
11479 * FO box type). It outputs box ID assigned by ISDS in dbID element.
11480 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11481 * box, or contact address of PFO box owner). The email member is mandatory as
11482 * it will be used to deliver credentials.
11483 * @former_names is former name of box owner. Pass NULL if you don't care.
11484 * @approval is optional external approval of box manipulation
11485 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11486 * NULL, if you don't care.*/
11487 isds_error isds_request_new_testing_box(struct isds_ctx *context,
11488 struct isds_DbOwnerInfo *box, const struct isds_list *users,
11489 const char *former_names, const struct isds_approval *approval,
11490 char **refnumber) {
11491 isds_error err = IE_SUCCESS;
11492 #if HAVE_LIBCURL
11493 xmlNodePtr request = NULL;
11494 xmlDocPtr response = NULL;
11495 xmlXPathContextPtr xpath_ctx = NULL;
11496 xmlXPathObjectPtr result = NULL;
11497 #endif
11500 if (!context) return IE_INVALID_CONTEXT;
11501 zfree(context->long_message);
11502 if (!box) return IE_INVAL;
11504 #if HAVE_LIBCURL
11505 if (!box->email || box->email[0] == '\0') {
11506 isds_log_message(context, _("E-mail field is mandatory"));
11507 return IE_INVAL;
11510 /* Scratch box ID */
11511 zfree(box->dbID);
11513 /* Store configuration */
11514 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
11515 free(context->url);
11516 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
11517 if (!(context->url))
11518 return IE_NOMEM;
11520 /* Prepare CURL handle if not yet connected */
11521 if (!context->curl) {
11522 context->curl = curl_easy_init();
11523 if (!(context->curl))
11524 return IE_ERROR;
11527 /* Build CreateDataBox request */
11528 err = build_CreateDBInput_request(context,
11529 &request, BAD_CAST "CreateDataBox",
11530 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
11531 if (err) goto leave;
11533 /* Send it to server and process response */
11534 err = send_destroy_request_check_response(context,
11535 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
11536 &response, (xmlChar **) refnumber, NULL);
11537 if (err) goto leave;
11539 /* Extract box ID */
11540 xpath_ctx = xmlXPathNewContext(response);
11541 if (!xpath_ctx) {
11542 err = IE_ERROR;
11543 goto leave;
11545 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11546 err = IE_ERROR;
11547 goto leave;
11549 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
11551 leave:
11552 xmlXPathFreeObject(result);
11553 xmlXPathFreeContext(xpath_ctx);
11554 xmlFreeDoc(response);
11555 xmlFreeNode(request);
11557 if (!err) {
11558 isds_log(ILF_ISDS, ILL_DEBUG,
11559 _("CreateDataBox request processed by server successfully.\n"));
11561 #else /* not HAVE_LIBCURL */
11562 err = IE_NOTSUP;
11563 #endif
11565 return err;
11569 /* Submit CMS signed message to ISDS to verify its originality. This is
11570 * stronger form of isds_verify_message_hash() because ISDS does more checks
11571 * than simple one (potentialy old weak) hash comparison.
11572 * @context is session context
11573 * @message is memory with raw CMS signed message bit stream
11574 * @length is @message size in bytes
11575 * @return
11576 * IE_SUCCESS if message originates in ISDS
11577 * IE_NOTEQUAL if message is unknown to ISDS
11578 * other code for other errors */
11579 isds_error isds_authenticate_message(struct isds_ctx *context,
11580 const void *message, size_t length) {
11581 isds_error err = IE_SUCCESS;
11582 #if HAVE_LIBCURL
11583 xmlNsPtr isds_ns = NULL;
11584 xmlNodePtr request = NULL;
11585 xmlDocPtr response = NULL;
11586 xmlXPathContextPtr xpath_ctx = NULL;
11587 xmlXPathObjectPtr result = NULL;
11588 _Bool *authentic = NULL;
11589 #endif
11591 if (!context) return IE_INVALID_CONTEXT;
11592 zfree(context->long_message);
11593 if (!message || length == 0) return IE_INVAL;
11595 #if HAVE_LIBCURL
11596 /* Check if connection is established
11597 * TODO: This check should be done downstairs. */
11598 if (!context->curl) return IE_CONNECTION_CLOSED;
11601 /* Build AuthenticateMessage request */
11602 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11603 if (!request) {
11604 isds_log_message(context,
11605 _("Could not build AuthenticateMessage request"));
11606 return IE_ERROR;
11608 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11609 if(!isds_ns) {
11610 isds_log_message(context, _("Could not create ISDS name space"));
11611 xmlFreeNode(request);
11612 return IE_ERROR;
11614 xmlSetNs(request, isds_ns);
11616 /* Insert Base64 encoded message */
11617 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11618 message, length);
11619 if (err) goto leave;
11621 /* Send request to server and process response */
11622 err = send_destroy_request_check_response(context,
11623 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11624 &response, NULL, NULL);
11625 if (err) goto leave;
11628 /* ISDS has decided */
11629 xpath_ctx = xmlXPathNewContext(response);
11630 if (!xpath_ctx) {
11631 err = IE_ERROR;
11632 goto leave;
11634 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11635 err = IE_ERROR;
11636 goto leave;
11639 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11641 if (!authentic) {
11642 isds_log_message(context,
11643 _("Server did not return any response on "
11644 "AuthenticateMessage request"));
11645 err = IE_ISDS;
11646 goto leave;
11648 if (*authentic) {
11649 isds_log(ILF_ISDS, ILL_DEBUG,
11650 _("ISDS authenticated the message successfully\n"));
11651 } else {
11652 isds_log_message(context, _("ISDS does not know the message"));
11653 err = IE_NOTEQUAL;
11657 leave:
11658 free(authentic);
11659 xmlXPathFreeObject(result);
11660 xmlXPathFreeContext(xpath_ctx);
11662 xmlFreeDoc(response);
11663 xmlFreeNode(request);
11664 #else /* not HAVE_LIBCURL */
11665 err = IE_NOTSUP;
11666 #endif
11668 return err;
11672 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11673 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11674 * be re-signed.
11675 * @context is session context
11676 * @input_data is memory with raw CMS signed message or delivery info bit
11677 * stream to re-sign
11678 * @input_length is @input_data size in bytes
11679 * @output_data is pointer to auto-allocated memory where to store re-signed
11680 * input data blob. Caller must free it.
11681 * @output_data is pointer where to store @output_data size in bytes
11682 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11683 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11684 * @return
11685 * IE_SUCCESS if CMS blob has been re-signed successfully
11686 * other code for other errors */
11687 isds_error isds_resign_message(struct isds_ctx *context,
11688 const void *input_data, size_t input_length,
11689 void **output_data, size_t *output_length, struct tm **valid_to) {
11690 isds_error err = IE_SUCCESS;
11691 #if HAVE_LIBCURL
11692 xmlNsPtr isds_ns = NULL;
11693 xmlNodePtr request = NULL;
11694 xmlDocPtr response = NULL;
11695 xmlXPathContextPtr xpath_ctx = NULL;
11696 xmlXPathObjectPtr result = NULL;
11697 char *string = NULL;
11698 const xmlChar *codes[] = {
11699 BAD_CAST "2200",
11700 BAD_CAST "2201",
11701 BAD_CAST "2204",
11702 BAD_CAST "2207",
11703 NULL
11705 const char *meanings[] = {
11706 "Message is bad",
11707 "Message is not original",
11708 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11709 "Time stamp could not been generated in time"
11711 const isds_error errors[] = {
11712 IE_INVAL,
11713 IE_NOTUNIQ,
11714 IE_INVAL,
11715 IE_ISDS,
11717 struct code_map_isds_error map = {
11718 .codes = codes,
11719 .meanings = meanings,
11720 .errors = errors
11722 #endif
11724 if (NULL != output_data) *output_data = NULL;
11725 if (NULL != output_length) *output_length = 0;
11726 if (NULL != valid_to) *valid_to = NULL;
11728 if (NULL == context) return IE_INVALID_CONTEXT;
11729 zfree(context->long_message);
11730 if (NULL == input_data || 0 == input_length) {
11731 isds_log_message(context, _("Empty CMS blob on input"));
11732 return IE_INVAL;
11734 if (NULL == output_data || NULL == output_length) {
11735 isds_log_message(context,
11736 _("NULL pointer provided for output CMS blob"));
11737 return IE_INVAL;
11740 #if HAVE_LIBCURL
11741 /* Check if connection is established
11742 * TODO: This check should be done downstairs. */
11743 if (!context->curl) return IE_CONNECTION_CLOSED;
11746 /* Build Re-signISDSDocument request */
11747 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11748 if (!request) {
11749 isds_log_message(context,
11750 _("Could not build Re-signISDSDocument request"));
11751 return IE_ERROR;
11753 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11754 if(!isds_ns) {
11755 isds_log_message(context, _("Could not create ISDS name space"));
11756 xmlFreeNode(request);
11757 return IE_ERROR;
11759 xmlSetNs(request, isds_ns);
11761 /* Insert Base64 encoded CMS blob */
11762 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11763 input_data, input_length);
11764 if (err) goto leave;
11766 /* Send request to server and process response */
11767 err = send_destroy_request_check_response(context,
11768 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11769 &response, NULL, &map);
11770 if (err) goto leave;
11773 /* Extract re-signed data */
11774 xpath_ctx = xmlXPathNewContext(response);
11775 if (!xpath_ctx) {
11776 err = IE_ERROR;
11777 goto leave;
11779 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11780 err = IE_ERROR;
11781 goto leave;
11783 result = xmlXPathEvalExpression(
11784 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11785 if (!result) {
11786 err = IE_ERROR;
11787 goto leave;
11789 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11790 isds_log_message(context,
11791 _("Missing Re-signISDSDocumentResponse element"));
11792 err = IE_ISDS;
11793 goto leave;
11795 if (result->nodesetval->nodeNr > 1) {
11796 isds_log_message(context,
11797 _("Multiple Re-signISDSDocumentResponse element"));
11798 err = IE_ISDS;
11799 goto leave;
11801 xpath_ctx->node = result->nodesetval->nodeTab[0];
11802 xmlXPathFreeObject(result); result = NULL;
11804 EXTRACT_STRING("isds:dmResultDoc", string);
11805 /* Decode non-empty data */
11806 if (NULL != string && string[0] != '\0') {
11807 *output_length = _isds_b64decode(string, output_data);
11808 if (*output_length == (size_t) -1) {
11809 isds_log_message(context,
11810 _("Error while Base64-decoding re-signed data"));
11811 err = IE_ERROR;
11812 goto leave;
11814 } else {
11815 isds_log_message(context, _("Server did not send re-signed data"));
11816 err = IE_ISDS;
11817 goto leave;
11819 zfree(string);
11821 if (NULL != valid_to) {
11822 /* Get time stamp expiration date */
11823 EXTRACT_STRING("isds:dmValidTo", string);
11824 if (NULL != string) {
11825 *valid_to = calloc(1, sizeof(**valid_to));
11826 if (!*valid_to) {
11827 err = IE_NOMEM;
11828 goto leave;
11830 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11831 if (err) {
11832 if (err == IE_NOTSUP) {
11833 err = IE_ISDS;
11834 char *string_locale = _isds_utf82locale(string);
11835 isds_printf_message(context,
11836 _("Invalid dmValidTo value: %s"), string_locale);
11837 free(string_locale);
11839 goto leave;
11844 leave:
11845 free(string);
11847 xmlXPathFreeObject(result);
11848 xmlXPathFreeContext(xpath_ctx);
11850 xmlFreeDoc(response);
11851 xmlFreeNode(request);
11852 #else /* not HAVE_LIBCURL */
11853 err = IE_NOTSUP;
11854 #endif
11856 return err;
11859 #undef INSERT_ELEMENT
11860 #undef CHECK_FOR_STRING_LENGTH
11861 #undef INSERT_STRING_ATTRIBUTE
11862 #undef INSERT_ULONGINTNOPTR
11863 #undef INSERT_ULONGINT
11864 #undef INSERT_LONGINT
11865 #undef INSERT_BOOLEAN
11866 #undef INSERT_SCALAR_BOOLEAN
11867 #undef INSERT_STRING
11868 #undef INSERT_STRING_WITH_NS
11869 #undef EXTRACT_STRING_ATTRIBUTE
11870 #undef EXTRACT_ULONGINT
11871 #undef EXTRACT_LONGINT
11872 #undef EXTRACT_BOOLEAN
11873 #undef EXTRACT_STRING
11876 /* Compute hash of message from raw representation and store it into envelope.
11877 * Original hash structure will be destroyed in envelope.
11878 * @context is session context
11879 * @message is message carrying raw XML message blob
11880 * @algorithm is desired hash algorithm to use */
11881 isds_error isds_compute_message_hash(struct isds_ctx *context,
11882 struct isds_message *message, const isds_hash_algorithm algorithm) {
11883 isds_error err = IE_SUCCESS;
11884 const char *nsuri;
11885 void *xml_stream = NULL;
11886 size_t xml_stream_length;
11887 size_t phys_start, phys_end;
11888 char *phys_path = NULL;
11889 struct isds_hash *new_hash = NULL;
11892 if (!context) return IE_INVALID_CONTEXT;
11893 zfree(context->long_message);
11894 if (!message) return IE_INVAL;
11896 if (!message->raw) {
11897 isds_log_message(context,
11898 _("Message does not carry raw representation"));
11899 return IE_INVAL;
11902 switch (message->raw_type) {
11903 case RAWTYPE_INCOMING_MESSAGE:
11904 nsuri = ISDS_NS;
11905 xml_stream = message->raw;
11906 xml_stream_length = message->raw_length;
11907 break;
11909 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11910 nsuri = SISDS_INCOMING_NS;
11911 xml_stream = message->raw;
11912 xml_stream_length = message->raw_length;
11913 break;
11915 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11916 nsuri = SISDS_INCOMING_NS;
11917 err = _isds_extract_cms_data(context,
11918 message->raw, message->raw_length,
11919 &xml_stream, &xml_stream_length);
11920 if (err) goto leave;
11921 break;
11923 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11924 nsuri = SISDS_OUTGOING_NS;
11925 xml_stream = message->raw;
11926 xml_stream_length = message->raw_length;
11927 break;
11929 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11930 nsuri = SISDS_OUTGOING_NS;
11931 err = _isds_extract_cms_data(context,
11932 message->raw, message->raw_length,
11933 &xml_stream, &xml_stream_length);
11934 if (err) goto leave;
11935 break;
11937 default:
11938 isds_log_message(context, _("Bad raw representation type"));
11939 return IE_INVAL;
11940 break;
11944 /* XXX: Hash is computed from original string representing isds:dmDm
11945 * subtree. That means no encoding, white space, xmlns attributes changes.
11946 * In other words, input for hash can be invalid XML stream. */
11947 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
11948 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11949 PHYSXML_ELEMENT_SEPARATOR,
11950 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
11951 PHYSXML_ELEMENT_SEPARATOR
11952 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
11953 err = IE_NOMEM;
11954 goto leave;
11956 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11957 phys_path, &phys_start, &phys_end);
11958 zfree(phys_path);
11959 if (err) {
11960 isds_log_message(context,
11961 _("Substring with isds:dmDM element could not be located "
11962 "in raw message"));
11963 goto leave;
11967 /* Compute hash */
11968 new_hash = calloc(1, sizeof(*new_hash));
11969 if (!new_hash) {
11970 err = IE_NOMEM;
11971 goto leave;
11973 new_hash->algorithm = algorithm;
11974 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
11975 new_hash);
11976 if (err) {
11977 isds_log_message(context, _("Could not compute message hash"));
11978 goto leave;
11981 /* Save computed hash */
11982 if (!message->envelope) {
11983 message->envelope = calloc(1, sizeof(*message->envelope));
11984 if (!message->envelope) {
11985 err = IE_NOMEM;
11986 goto leave;
11989 isds_hash_free(&message->envelope->hash);
11990 message->envelope->hash = new_hash;
11992 leave:
11993 if (err) {
11994 isds_hash_free(&new_hash);
11997 free(phys_path);
11998 if (xml_stream != message->raw) free(xml_stream);
11999 return err;
12003 /* Compare two hashes.
12004 * @h1 is first hash
12005 * @h2 is another hash
12006 * @return
12007 * IE_SUCCESS if hashes equal
12008 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12009 * IE_ENUM if not comparable, but both structures defined
12010 * IE_INVAL if some of the structures are undefined (NULL)
12011 * IE_ERROR if internal error occurs */
12012 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
12013 if (h1 == NULL || h2 == NULL) return IE_INVAL;
12014 if (h1->algorithm != h2->algorithm) return IE_ENUM;
12015 if (h1->length != h2->length) return IE_ERROR;
12016 if (h1->length > 0 && !h1->value) return IE_ERROR;
12017 if (h2->length > 0 && !h2->value) return IE_ERROR;
12019 for (size_t i = 0; i < h1->length; i++) {
12020 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12021 return IE_NOTEQUAL;
12023 return IE_SUCCESS;
12027 /* Check message has gone through ISDS by comparing message hash stored in
12028 * ISDS and locally computed hash. You must provide message with valid raw
12029 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12030 * This is convenient wrapper for isds_download_message_hash(),
12031 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12032 * @context is session context
12033 * @message is message with valid raw and envelope member; envelope->hash
12034 * member will be changed during function run. Use envelope on heap only.
12035 * @return
12036 * IE_SUCCESS if message originates in ISDS
12037 * IE_NOTEQUAL if message is unknown to ISDS
12038 * other code for other errors */
12039 isds_error isds_verify_message_hash(struct isds_ctx *context,
12040 struct isds_message *message) {
12041 isds_error err = IE_SUCCESS;
12042 struct isds_hash *downloaded_hash = NULL;
12044 if (!context) return IE_INVALID_CONTEXT;
12045 zfree(context->long_message);
12046 if (!message) return IE_INVAL;
12048 if (!message->envelope) {
12049 isds_log_message(context,
12050 _("Given message structure is missing envelope"));
12051 return IE_INVAL;
12053 if (!message->raw) {
12054 isds_log_message(context,
12055 _("Given message structure is missing raw representation"));
12056 return IE_INVAL;
12059 err = isds_download_message_hash(context, message->envelope->dmID,
12060 &downloaded_hash);
12061 if (err) goto leave;
12063 err = isds_compute_message_hash(context, message,
12064 downloaded_hash->algorithm);
12065 if (err) goto leave;
12067 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12069 leave:
12070 isds_hash_free(&downloaded_hash);
12071 return err;
12075 /* Search for document by document ID in list of documents. IDs are compared
12076 * as UTF-8 string.
12077 * @documents is list of isds_documents
12078 * @id is document identifier
12079 * @return first matching document or NULL. */
12080 const struct isds_document *isds_find_document_by_id(
12081 const struct isds_list *documents, const char *id) {
12082 const struct isds_list *item;
12083 const struct isds_document *document;
12085 for (item = documents; item; item = item->next) {
12086 document = (struct isds_document *) item->data;
12087 if (!document) continue;
12089 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12090 return document;
12093 return NULL;
12097 /* Normalize @mime_type to be proper MIME type.
12098 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12099 * guess regular MIME type (e.g. "application/pdf").
12100 * @mime_type is UTF-8 encoded MIME type to fix
12101 * @return original @mime_type if no better interpretation exists, or
12102 * constant static UTF-8 encoded string with proper MIME type. */
12103 const char *isds_normalize_mime_type(const char *mime_type) {
12104 if (!mime_type) return NULL;
12106 for (size_t offset = 0;
12107 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12108 offset += 2) {
12109 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12110 extension_map_mime[offset]))
12111 return (const char *) extension_map_mime[offset + 1];
12114 return mime_type;
12118 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12119 struct isds_message **message);
12120 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12121 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12122 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12123 struct isds_address **address);
12125 int isds_message_free(struct isds_message **message);
12126 int isds_address_free(struct isds_address **address);
12130 /* Makes known all relevant namespaces to given XPath context
12131 * @xpath_ctx is XPath context
12132 * @message_ns selects proper message name space. Unsigned and signed
12133 * messages and delivery info's differ in prefix and URI. */
12134 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12135 const message_ns_type message_ns) {
12136 const xmlChar *message_namespace = NULL;
12138 if (!xpath_ctx) return IE_ERROR;
12140 switch(message_ns) {
12141 case MESSAGE_NS_1:
12142 message_namespace = BAD_CAST ISDS1_NS; break;
12143 case MESSAGE_NS_UNSIGNED:
12144 message_namespace = BAD_CAST ISDS_NS; break;
12145 case MESSAGE_NS_SIGNED_INCOMING:
12146 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12147 case MESSAGE_NS_SIGNED_OUTGOING:
12148 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12149 case MESSAGE_NS_SIGNED_DELIVERY:
12150 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12151 default:
12152 return IE_ENUM;
12155 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12156 return IE_ERROR;
12157 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12158 return IE_ERROR;
12159 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12160 return IE_ERROR;
12161 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12162 return IE_ERROR;
12163 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12164 return IE_ERROR;
12165 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12166 return IE_ERROR;
12167 return IE_SUCCESS;