Fix a memory leak in the isds_find_box_by_fulltext()
[libisds.git] / src / isds.c
blob5f04139e39bd714aba6a5c24a17c9683213a5b03
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 if (!msg) return;
678 va_start(ap, msg);
679 isds_vasprintf(&text, msg, ap);
680 va_end(ap);
682 if (text)
683 isds_log(ILF_XML, ILL_ERR, "%s", text);
684 free(text);
688 /* Initialize ISDS library.
689 * Global function, must be called before other functions.
690 * If it fails you can not use ISDS library and must call isds_cleanup() to
691 * free partially initialized global variables. */
692 isds_error isds_init(void) {
693 /* NULL global variables */
694 log_facilities = ILF_ALL;
695 log_level = ILL_WARNING;
696 log_callback = NULL;
697 log_callback_data = NULL;
699 #if ENABLE_NLS
700 /* Initialize gettext */
701 bindtextdomain(PACKAGE, LOCALEDIR);
702 #endif
704 #if HAVE_LIBCURL
705 /* Initialize CURL */
706 if (curl_global_init(CURL_GLOBAL_ALL)) {
707 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
708 return IE_ERROR;
710 #endif /* HAVE_LIBCURL */
712 /* Initialise cryptographic back-ends. */
713 if (IE_SUCCESS != _isds_init_crypto()) {
714 isds_log(ILF_ISDS, ILL_CRIT,
715 _("initialisation of cryptographic back-end failed\n"));
716 return IE_ERROR;
719 /* This can _exit() current program. Find not so assertive check. */
720 LIBXML_TEST_VERSION;
721 xmlSetGenericErrorFunc(NULL, log_xml);
723 /* Check expat */
724 if (_isds_init_expat(&version_expat)) {
725 isds_log(ILF_ISDS, ILL_CRIT,
726 _("expat library initialization failed\n"));
727 return IE_ERROR;
730 /* Allocate global variables */
733 return IE_SUCCESS;
737 /* Deinitialize ISDS library.
738 * Global function, must be called as last library function. */
739 isds_error isds_cleanup(void) {
740 /* XML */
741 xmlCleanupParser();
743 #if HAVE_LIBCURL
744 /* Curl */
745 curl_global_cleanup();
746 #endif
748 return IE_SUCCESS;
752 /* Return version string of this library. Version of dependencies can be
753 * embedded. Do no try to parse it. You must free it. */
754 char *isds_version(void) {
755 char *buffer = NULL;
757 isds_asprintf(&buffer,
758 #if HAVE_LIBCURL
759 # ifndef USE_OPENSSL_BACKEND
760 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
761 # else
762 _("%s (%s, %s, %s, libxml2 %s)"),
763 # endif
764 #else
765 # ifndef USE_OPENSSL_BACKEND
766 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
767 # else
768 _("%s (%s, %s, libxml2 %s)"),
769 # endif
770 #endif
771 PACKAGE_VERSION,
772 #if HAVE_LIBCURL
773 curl_version(),
774 #endif
775 #ifndef USE_OPENSSL_BACKEND
776 version_gpgme, version_gcrypt,
777 #else
778 version_openssl,
779 #endif
780 version_expat, xmlParserVersion);
781 return buffer;
785 /* Return text description of ISDS error */
786 const char *isds_strerror(const isds_error error) {
787 switch (error) {
788 case IE_SUCCESS:
789 return(_("Success")); break;
790 case IE_ERROR:
791 return(_("Unspecified error")); break;
792 case IE_NOTSUP:
793 return(_("Not supported")); break;
794 case IE_INVAL:
795 return(_("Invalid value")); break;
796 case IE_INVALID_CONTEXT:
797 return(_("Invalid context")); break;
798 case IE_NOT_LOGGED_IN:
799 return(_("Not logged in")); break;
800 case IE_CONNECTION_CLOSED:
801 return(_("Connection closed")); break;
802 case IE_TIMED_OUT:
803 return(_("Timed out")); break;
804 case IE_NOEXIST:
805 return(_("Not exist")); break;
806 case IE_NOMEM:
807 return(_("Out of memory")); break;
808 case IE_NETWORK:
809 return(_("Network problem")); break;
810 case IE_HTTP:
811 return(_("HTTP problem")); break;
812 case IE_SOAP:
813 return(_("SOAP problem")); break;
814 case IE_XML:
815 return(_("XML problem")); break;
816 case IE_ISDS:
817 return(_("ISDS server problem")); break;
818 case IE_ENUM:
819 return(_("Invalid enum value")); break;
820 case IE_DATE:
821 return(_("Invalid date value")); break;
822 case IE_2BIG:
823 return(_("Too big")); break;
824 case IE_2SMALL:
825 return(_("Too small")); break;
826 case IE_NOTUNIQ:
827 return(_("Value not unique")); break;
828 case IE_NOTEQUAL:
829 return(_("Values not equal")); break;
830 case IE_PARTIAL_SUCCESS:
831 return(_("Some suboperations failed")); break;
832 case IE_ABORTED:
833 return(_("Operation aborted")); break;
834 case IE_SECURITY:
835 return(_("Security problem")); break;
836 default:
837 return(_("Unknown error"));
842 /* Create ISDS context.
843 * Each context can be used for different sessions to (possibly) different
844 * ISDS server with different credentials. */
845 struct isds_ctx *isds_ctx_create(void) {
846 struct isds_ctx *context;
847 context = malloc(sizeof(*context));
848 if (context) memset(context, 0, sizeof(*context));
849 return context;
852 #if HAVE_LIBCURL
853 /* Close possibly opened connection to Czech POINT document deposit without
854 * resetting long_message buffer.
855 * XXX: Do not use czp_close_connection() if you do not want to destroy log
856 * message.
857 * @context is Czech POINT session context. */
858 static isds_error czp_do_close_connection(struct isds_ctx *context) {
859 if (!context) return IE_INVALID_CONTEXT;
860 _isds_close_connection(context);
861 return IE_SUCCESS;
865 /* Discard credentials.
866 * @context is ISDS context
867 * @discard_saved_username is true for removing saved username, false for
868 * keeping it.
869 * Only that. It does not cause log out, connection close or similar. */
870 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context,
871 _Bool discard_saved_username) {
872 if(!context) return IE_INVALID_CONTEXT;
874 if (context->username) {
875 memset(context->username, 0, strlen(context->username));
876 zfree(context->username);
878 if (context->password) {
879 memset(context->password, 0, strlen(context->password));
880 zfree(context->password);
882 isds_pki_credentials_free(&context->pki_credentials);
883 if (discard_saved_username && context->saved_username) {
884 memset(context->saved_username, 0, strlen(context->saved_username));
885 zfree(context->saved_username);
888 return IE_SUCCESS;
890 #endif /* HAVE_LIBCURL */
893 /* Destroy ISDS context and free memory.
894 * @context will be NULLed on success. */
895 isds_error isds_ctx_free(struct isds_ctx **context) {
896 if (!context || !*context) {
897 return IE_INVALID_CONTEXT;
900 #if HAVE_LIBCURL
901 /* Discard credentials and close connection */
902 switch ((*context)->type) {
903 case CTX_TYPE_NONE: break;
904 case CTX_TYPE_ISDS: isds_logout(*context); break;
905 case CTX_TYPE_CZP:
906 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
907 czp_do_close_connection(*context); break;
910 /* For sure */
911 _isds_discard_credentials(*context, 1);
913 /* Free other structures */
914 free((*context)->url);
915 free((*context)->tls_verify_server);
916 free((*context)->tls_ca_file);
917 free((*context)->tls_ca_dir);
918 free((*context)->tls_crl_file);
919 #endif /* HAVE_LIBCURL */
920 free((*context)->long_message);
922 free(*context);
923 *context = NULL;
924 return IE_SUCCESS;
928 /* Return long message text produced by library function, e.g. detailed error
929 * message. Returned pointer is only valid until new library function is
930 * called for the same context. Could be NULL, especially if NULL context is
931 * supplied. Return string is locale encoded. */
932 char *isds_long_message(const struct isds_ctx *context) {
933 if (!context) return NULL;
934 return context->long_message;
938 /* Stores message into context' long_message buffer.
939 * Application can pick the message up using isds_long_message().
940 * NULL @message truncates the buffer but does not deallocate it.
941 * @message is coded in locale encoding */
942 _hidden isds_error isds_log_message(struct isds_ctx *context,
943 const char *message) {
944 char *buffer;
945 size_t length;
947 if (!context) return IE_INVALID_CONTEXT;
949 /* FIXME: Check for integer overflow */
950 length = 1 + ((message) ? strlen(message) : 0);
951 buffer = realloc(context->long_message, length);
952 if (!buffer) return IE_NOMEM;
954 if (message)
955 strcpy(buffer, message);
956 else
957 *buffer = '\0';
959 context->long_message = buffer;
960 return IE_SUCCESS;
964 /* Appends message into context' long_message buffer.
965 * Application can pick the message up using isds_long_message().
966 * NULL message has void effect. */
967 _hidden isds_error isds_append_message(struct isds_ctx *context,
968 const char *message) {
969 char *buffer;
970 size_t old_length, length;
972 if (!context) return IE_INVALID_CONTEXT;
973 if (!message) return IE_SUCCESS;
974 if (!context->long_message)
975 return isds_log_message(context, message);
977 old_length = strlen(context->long_message);
978 /* FIXME: Check for integer overflow */
979 length = 1 + old_length + strlen(message);
980 buffer = realloc(context->long_message, length);
981 if (!buffer) return IE_NOMEM;
983 strcpy(buffer + old_length, message);
985 context->long_message = buffer;
986 return IE_SUCCESS;
990 /* Stores formatted message into context' long_message buffer.
991 * Application can pick the message up using isds_long_message(). */
992 _hidden isds_error isds_printf_message(struct isds_ctx *context,
993 const char *format, ...) {
994 va_list ap;
995 int length;
997 if (!context) return IE_INVALID_CONTEXT;
998 va_start(ap, format);
999 length = isds_vasprintf(&(context->long_message), format, ap);
1000 va_end(ap);
1002 return (length < 0) ? IE_ERROR: IE_SUCCESS;
1006 /* Set logging up.
1007 * @facilities is bit mask of isds_log_facility values,
1008 * @level is verbosity level. */
1009 void isds_set_logging(const unsigned int facilities,
1010 const isds_log_level level) {
1011 log_facilities = facilities;
1012 log_level = level;
1016 /* Register callback function libisds calls when new global log message is
1017 * produced by library. Library logs to stderr by default.
1018 * @callback is function provided by application libisds will call. See type
1019 * definition for @callback argument explanation. Pass NULL to revert logging to
1020 * default behaviour.
1021 * @data is application specific data @callback gets as last argument */
1022 void isds_set_log_callback(isds_log_callback callback, void *data) {
1023 log_callback = callback;
1024 log_callback_data = data;
1028 /* Log @message in class @facility with log @level into global log. @message
1029 * is printf(3) formatting string, variadic arguments may be necessary.
1030 * For debugging purposes. */
1031 _hidden isds_error isds_log(const isds_log_facility facility,
1032 const isds_log_level level, const char *message, ...) {
1033 va_list ap;
1034 char *buffer = NULL;
1035 int length;
1037 if (level > log_level) return IE_SUCCESS;
1038 if (!(log_facilities & facility)) return IE_SUCCESS;
1039 if (!message) return IE_INVAL;
1041 if (log_callback) {
1042 /* Pass message to application supplied callback function */
1043 va_start(ap, message);
1044 length = isds_vasprintf(&buffer, message, ap);
1045 va_end(ap);
1047 if (length == -1) {
1048 return IE_ERROR;
1050 if (length > 0) {
1051 log_callback(facility, level, buffer, length, log_callback_data);
1053 free(buffer);
1054 } else {
1055 /* Default: Log it to stderr */
1056 va_start(ap, message);
1057 vfprintf(stderr, message, ap);
1058 va_end(ap);
1059 /* Line buffered printf is default.
1060 * fflush(stderr);*/
1063 return IE_SUCCESS;
1067 /* Set timeout in milliseconds for each network job like connecting to server
1068 * or sending message. Use 0 to disable timeout limits. */
1069 isds_error isds_set_timeout(struct isds_ctx *context,
1070 const unsigned int timeout) {
1071 if (!context) return IE_INVALID_CONTEXT;
1072 zfree(context->long_message);
1074 #if HAVE_LIBCURL
1075 context->timeout = timeout;
1077 if (context->curl) {
1078 CURLcode curl_err;
1080 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
1081 if (!curl_err)
1082 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1083 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
1084 context->timeout);
1085 #else
1086 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
1087 context->timeout / 1000);
1088 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1089 if (curl_err) return IE_ERROR;
1092 return IE_SUCCESS;
1093 #else /* not HAVE_LIBCURL */
1094 return IE_NOTSUP;
1095 #endif
1099 /* Register callback function libisds calls periodically during HTTP data
1100 * transfer.
1101 * @context is session context
1102 * @callback is function provided by application libisds will call. See type
1103 * definition for @callback argument explanation.
1104 * @data is application specific data @callback gets as last argument */
1105 isds_error isds_set_progress_callback(struct isds_ctx *context,
1106 isds_progress_callback callback, void *data) {
1107 if (!context) return IE_INVALID_CONTEXT;
1108 zfree(context->long_message);
1110 #if HAVE_LIBCURL
1111 context->progress_callback = callback;
1112 context->progress_callback_data = data;
1114 return IE_SUCCESS;
1115 #else /* not HAVE_LIBCURL */
1116 return IE_NOTSUP;
1117 #endif
1121 /* Change context settings.
1122 * @context is context which setting will be applied to
1123 * @option is name of option. It determines the type of last argument. See
1124 * isds_option definition for more info.
1125 * @... is value of new setting. Type is determined by @option
1126 * */
1127 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1128 ...) {
1129 isds_error err = IE_SUCCESS;
1130 va_list ap;
1131 #if HAVE_LIBCURL
1132 char *pointer, *string;
1133 #endif
1135 if (!context) return IE_INVALID_CONTEXT;
1136 zfree(context->long_message);
1138 va_start(ap, option);
1140 #define REPLACE_VA_BOOLEAN(destination) { \
1141 if (!(destination)) { \
1142 (destination) = malloc(sizeof(*(destination))); \
1143 if (!(destination)) { \
1144 err = IE_NOMEM; goto leave; \
1147 *(destination) = (_Bool) !!va_arg(ap, int); \
1150 #define REPLACE_VA_STRING(destination) { \
1151 string = va_arg(ap, char *); \
1152 if (string) { \
1153 pointer = realloc((destination), 1 + strlen(string)); \
1154 if (!pointer) { err = IE_NOMEM; goto leave; } \
1155 strcpy(pointer, string); \
1156 (destination) = pointer; \
1157 } else { \
1158 free(destination); \
1159 (destination) = NULL; \
1163 switch (option) {
1164 case IOPT_TLS_VERIFY_SERVER:
1165 #if HAVE_LIBCURL
1166 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1167 #else
1168 err = IE_NOTSUP; goto leave;
1169 #endif
1170 break;
1171 case IOPT_TLS_CA_FILE:
1172 #if HAVE_LIBCURL
1173 REPLACE_VA_STRING(context->tls_ca_file);
1174 #else
1175 err = IE_NOTSUP; goto leave;
1176 #endif
1177 break;
1178 case IOPT_TLS_CA_DIRECTORY:
1179 #if HAVE_LIBCURL
1180 REPLACE_VA_STRING(context->tls_ca_dir);
1181 #else
1182 err = IE_NOTSUP; goto leave;
1183 #endif
1184 break;
1185 case IOPT_TLS_CRL_FILE:
1186 #if HAVE_LIBCURL
1187 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1188 REPLACE_VA_STRING(context->tls_crl_file);
1189 #else
1190 isds_log_message(context,
1191 _("Curl library does not support CRL definition"));
1192 err = IE_NOTSUP;
1193 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1194 #else
1195 err = IE_NOTSUP; goto leave;
1196 #endif /* not HAVE_LIBCURL */
1197 break;
1198 case IOPT_NORMALIZE_MIME_TYPE:
1199 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1200 break;
1202 default:
1203 err = IE_ENUM; goto leave;
1206 #undef REPLACE_VA_STRING
1207 #undef REPLACE_VA_BOOLEAN
1209 leave:
1210 va_end(ap);
1211 return err;
1215 #if HAVE_LIBCURL
1216 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1217 * Destination for NULL argument will not be touched.
1218 * Destination pointers must be freed before calling this function.
1219 * If @username is @context->saved_username, the saved_username will not be
1220 * replaced. The saved_username is clobbered only if context has set otp
1221 * member.
1222 * Return IE_SUCCESS on success. */
1223 static isds_error _isds_store_credentials(struct isds_ctx *context,
1224 const char *username, const char *password,
1225 const struct isds_pki_credentials *pki_credentials) {
1226 if (NULL == context) return IE_INVALID_CONTEXT;
1228 /* FIXME: mlock password
1229 * (I have a library) */
1231 if (username) {
1232 context->username = strdup(username);
1233 if (context->otp && context->saved_username != username)
1234 context->saved_username = strdup(username);
1236 if (password) {
1237 if (NULL == context->otp_credentials)
1238 context->password = strdup(password);
1239 else
1240 context->password = _isds_astrcat(password,
1241 context->otp_credentials->otp_code);
1243 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1245 if ((NULL != username && NULL == context->username) ||
1246 (NULL != password && NULL == context->password) ||
1247 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1248 (context->otp && NULL != context->username &&
1249 NULL == context->saved_username)) {
1250 return IE_NOMEM;
1253 return IE_SUCCESS;
1255 #endif
1258 /* Connect and log into ISDS server.
1259 * All required arguments will be copied, you do not have to keep them after
1260 * that.
1261 * ISDS supports six different authentication methods. Exact method is
1262 * selected on @username, @password, @pki_credentials, and @otp arguments:
1263 * - If @pki_credentials == NULL, @username and @password must be supplied
1264 * and then
1265 * - If @otp == NULL, simple authentication by username and password will
1266 * be proceeded.
1267 * - If @otp != NULL, authentication by username and password and OTP
1268 * will be used.
1269 * - If @pki_credentials != NULL, then
1270 * - If @username == NULL, only certificate will be used
1271 * - If @username != NULL, then
1272 * - If @password == NULL, then certificate will be used and
1273 * @username shifts meaning to box ID. This is used for hosted
1274 * services.
1275 * - Otherwise all three arguments will be used.
1276 * Please note, that different cases require different certificate type
1277 * (system qualified one or commercial non qualified one). This library
1278 * does not check such political issues. Please see ISDS Specification
1279 * for more details.
1280 * @url is base address of ISDS web service. Pass extern isds_locator
1281 * variable to use production ISDS instance without client certificate
1282 * authentication (or extern isds_cert_locator with client certificate
1283 * authentication or extern isds_otp_locators with OTP authentication).
1284 * Passing NULL has the same effect, autoselection between isds_locator,
1285 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1286 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1287 * isds_otp_testing_locator) variable to select testing instance.
1288 * @username is user name of ISDS user or box ID
1289 * @password is user's secret password
1290 * @pki_credentials defines public key cryptographic material to use in client
1291 * authentication.
1292 * @otp selects one-time password authentication method to use, defines OTP
1293 * code (if known) and returns fine grade resolution of OTP procedure.
1294 * @return:
1295 * IE_SUCCESS if authentication succeeds
1296 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1297 * requested, fine grade reason will be set into @otp->resolution. Error
1298 * message from server can be obtained by isds_long_message() call.
1299 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1300 * server has sent OTP code through side channel. Application is expected to
1301 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1302 * this call to complete second phase of TOTP authentication;
1303 * or other appropriate error. */
1304 isds_error isds_login(struct isds_ctx *context, const char *url,
1305 const char *username, const char *password,
1306 const struct isds_pki_credentials *pki_credentials,
1307 struct isds_otp *otp) {
1308 #if HAVE_LIBCURL
1309 isds_error err = IE_NOT_LOGGED_IN;
1310 isds_error soap_err;
1311 xmlNsPtr isds_ns = NULL;
1312 xmlNodePtr request = NULL;
1313 #endif /* HAVE_LIBCURL */
1315 if (!context) return IE_INVALID_CONTEXT;
1316 zfree(context->long_message);
1318 #if HAVE_LIBCURL
1319 /* Close connection if already logged in */
1320 if (context->curl) {
1321 _isds_close_connection(context);
1324 /* Store configuration */
1325 context->type = CTX_TYPE_ISDS;
1326 zfree(context->url);
1328 /* Mangle base URI according to requested authentication method */
1329 if (NULL == pki_credentials) {
1330 isds_log(ILF_SEC, ILL_INFO,
1331 _("Selected authentication method: no certificate, "
1332 "username and password\n"));
1333 if (!username || !password) {
1334 isds_log_message(context,
1335 _("Both username and password must be supplied"));
1336 return IE_INVAL;
1338 context->otp_credentials = otp;
1339 context->otp = (NULL != context->otp_credentials);
1341 if (!context->otp) {
1342 /* Default locator is official system (without certificate or
1343 * OTP) */
1344 context->url = strdup((NULL != url) ? url : isds_locator);
1345 } else {
1346 const char *authenticator_uri = NULL;
1347 if (!url) url = isds_otp_locator;
1348 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1349 switch (context->otp_credentials->method) {
1350 case OTP_HMAC:
1351 isds_log(ILF_SEC, ILL_INFO,
1352 _("Selected authentication method: "
1353 "HMAC-based one-time password\n"));
1354 authenticator_uri =
1355 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1356 break;
1357 case OTP_TIME:
1358 isds_log(ILF_SEC, ILL_INFO,
1359 _("Selected authentication method: "
1360 "Time-based one-time password\n"));
1361 if (context->otp_credentials->otp_code == NULL) {
1362 isds_log(ILF_SEC, ILL_INFO,
1363 _("OTP code has not been provided by "
1364 "application, requesting server for "
1365 "new one.\n"));
1366 authenticator_uri =
1367 "%1$sas/processLogin?type=totp&sendSms=true&"
1368 "uri=%1$sapps/";
1369 } else {
1370 isds_log(ILF_SEC, ILL_INFO,
1371 _("OTP code has been provided by "
1372 "application, not requesting server "
1373 "for new one.\n"));
1374 authenticator_uri =
1375 "%1$sas/processLogin?type=totp&"
1376 "uri=%1$sapps/";
1378 break;
1379 default:
1380 isds_log_message(context,
1381 _("Unknown one-time password authentication "
1382 "method requested by application"));
1383 return IE_ENUM;
1385 if (-1 == isds_asprintf(&context->url, authenticator_uri, url))
1386 return IE_NOMEM;
1388 } else {
1389 /* Default locator is official system (with client certificate) */
1390 context->otp = 0;
1391 context->otp_credentials = NULL;
1392 if (!url) url = isds_cert_locator;
1394 if (!username) {
1395 isds_log(ILF_SEC, ILL_INFO,
1396 _("Selected authentication method: system certificate, "
1397 "no username and no password\n"));
1398 password = NULL;
1399 context->url = _isds_astrcat(url, "cert/");
1400 } else {
1401 if (!password) {
1402 isds_log(ILF_SEC, ILL_INFO,
1403 _("Selected authentication method: system certificate, "
1404 "box ID and no password\n"));
1405 context->url = _isds_astrcat(url, "hspis/");
1406 } else {
1407 isds_log(ILF_SEC, ILL_INFO,
1408 _("Selected authentication method: commercial "
1409 "certificate, username and password\n"));
1410 context->url = _isds_astrcat(url, "certds/");
1414 if (!(context->url))
1415 return IE_NOMEM;
1417 /* Prepare CURL handle */
1418 context->curl = curl_easy_init();
1419 if (!(context->curl))
1420 return IE_ERROR;
1422 /* Build log-in request */
1423 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1424 if (!request) {
1425 isds_log_message(context, _("Could not build ISDS log-in request"));
1426 return IE_ERROR;
1428 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1429 if(!isds_ns) {
1430 isds_log_message(context, _("Could not create ISDS name space"));
1431 xmlFreeNode(request);
1432 return IE_ERROR;
1434 xmlSetNs(request, isds_ns);
1436 /* Store credentials */
1437 _isds_discard_credentials(context, 1);
1438 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1439 _isds_discard_credentials(context, 1);
1440 xmlFreeNode(request);
1441 return IE_NOMEM;
1444 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1445 username, url);
1447 /* XXX: ISDS documentation does not specify response body for
1448 * DummyOperation request. However real server sends back
1449 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1450 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1451 * SOAP body content, e.g. the dmStatus element. */
1453 /* Send log-in request */
1454 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1456 if (context->otp) {
1457 /* Revert context URL from OTP authentication service URL to OTP web
1458 * service base URL for subsequent calls. Potenial isds_login() retry
1459 * will re-set context URL again. */
1460 zfree(context->url);
1461 context->url = _isds_astrcat(url, "apps/");
1462 if (context->url == NULL) {
1463 soap_err = IE_NOMEM;
1465 /* Detach pointer to OTP credentials from context */
1466 context->otp_credentials = NULL;
1469 /* Remove credentials */
1470 _isds_discard_credentials(context, 0);
1472 /* Destroy log-in request */
1473 xmlFreeNode(request);
1475 if (soap_err) {
1476 _isds_close_connection(context);
1477 return soap_err;
1480 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1481 * authentication succeeded if soap_err == IE_SUCCESS */
1482 err = IE_SUCCESS;
1484 if (!err)
1485 isds_log(ILF_ISDS, ILL_DEBUG,
1486 _("User %s has been logged into server %s successfully\n"),
1487 username, url);
1488 return err;
1489 #else /* not HAVE_LIBCURL */
1490 return IE_NOTSUP;
1491 #endif
1495 /* Log out from ISDS server discards credentials and connection configuration. */
1496 isds_error isds_logout(struct isds_ctx *context) {
1497 if (!context) return IE_INVALID_CONTEXT;
1498 zfree(context->long_message);
1500 #if HAVE_LIBCURL
1501 if (context->curl) {
1502 if (context->otp) {
1503 isds_error err = _isds_invalidate_otp_cookie(context);
1504 if (err) return err;
1507 /* Close connection */
1508 _isds_close_connection(context);
1510 /* Discard credentials for sure. They should not survive isds_login(),
1511 * even successful .*/
1512 _isds_discard_credentials(context, 1);
1514 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1515 } else {
1516 _isds_discard_credentials(context, 1);
1518 zfree(context->url);
1519 return IE_SUCCESS;
1520 #else /* not HAVE_LIBCURL */
1521 return IE_NOTSUP;
1522 #endif
1526 /* Verify connection to ISDS is alive and server is responding.
1527 * Send dummy request to ISDS and expect dummy response. */
1528 isds_error isds_ping(struct isds_ctx *context) {
1529 #if HAVE_LIBCURL
1530 isds_error soap_err;
1531 xmlNsPtr isds_ns = NULL;
1532 xmlNodePtr request = NULL;
1533 #endif /* HAVE_LIBCURL */
1535 if (!context) return IE_INVALID_CONTEXT;
1536 zfree(context->long_message);
1538 #if HAVE_LIBCURL
1539 /* Check if connection is established */
1540 if (!context->curl) return IE_CONNECTION_CLOSED;
1543 /* Build dummy request */
1544 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1545 if (!request) {
1546 isds_log_message(context, _("Could build ISDS dummy request"));
1547 return IE_ERROR;
1549 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1550 if(!isds_ns) {
1551 isds_log_message(context, _("Could not create ISDS name space"));
1552 xmlFreeNode(request);
1553 return IE_ERROR;
1555 xmlSetNs(request, isds_ns);
1557 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1559 /* XXX: ISDS documentation does not specify response body for
1560 * DummyOperation request. However real server sends back
1561 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1562 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1563 * SOAP body content, e.g. the dmStatus element. */
1565 /* Send dummy request */
1566 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1568 /* Destroy log-in request */
1569 xmlFreeNode(request);
1571 if (soap_err) {
1572 isds_log(ILF_ISDS, ILL_DEBUG,
1573 _("ISDS server could not be contacted\n"));
1574 return soap_err;
1577 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1578 * authentication succeeded if soap_err == IE_SUCCESS */
1581 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1583 return IE_SUCCESS;
1584 #else /* not HAVE_LIBCURL */
1585 return IE_NOTSUP;
1586 #endif
1590 /* Send bogus request to ISDS.
1591 * Just for test purposes */
1592 isds_error isds_bogus_request(struct isds_ctx *context) {
1593 #if HAVE_LIBCURL
1594 isds_error err;
1595 xmlNsPtr isds_ns = NULL;
1596 xmlNodePtr request = NULL;
1597 xmlDocPtr response = NULL;
1598 xmlChar *code = NULL, *message = NULL;
1599 #endif
1601 if (!context) return IE_INVALID_CONTEXT;
1602 zfree(context->long_message);
1604 #if HAVE_LIBCURL
1605 /* Check if connection is established */
1606 if (!context->curl) {
1607 /* Testing printf message */
1608 isds_printf_message(context, "%s", _("I said connection closed"));
1609 return IE_CONNECTION_CLOSED;
1613 /* Build dummy request */
1614 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1615 if (!request) {
1616 isds_log_message(context, _("Could build ISDS bogus request"));
1617 return IE_ERROR;
1619 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1620 if(!isds_ns) {
1621 isds_log_message(context, _("Could not create ISDS name space"));
1622 xmlFreeNode(request);
1623 return IE_ERROR;
1625 xmlSetNs(request, isds_ns);
1627 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1629 /* Sent bogus request */
1630 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1632 /* Destroy request */
1633 xmlFreeNode(request);
1635 if (err) {
1636 isds_log(ILF_ISDS, ILL_DEBUG,
1637 _("Processing ISDS response on bogus request failed\n"));
1638 xmlFreeDoc(response);
1639 return err;
1642 /* Check for response status */
1643 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1644 &code, &message, NULL);
1645 if (err) {
1646 isds_log(ILF_ISDS, ILL_DEBUG,
1647 _("ISDS response on bogus request is missing status\n"));
1648 free(code);
1649 free(message);
1650 xmlFreeDoc(response);
1651 return err;
1653 if (xmlStrcmp(code, BAD_CAST "0000")) {
1654 char *code_locale = _isds_utf82locale((char*)code);
1655 char *message_locale = _isds_utf82locale((char*)message);
1656 isds_log(ILF_ISDS, ILL_DEBUG,
1657 _("Server refused bogus request (code=%s, message=%s)\n"),
1658 code_locale, message_locale);
1659 /* XXX: Literal error messages from ISDS are Czech messages
1660 * (English sometimes) in UTF-8. It's hard to catch them for
1661 * translation. Successfully gettextized would return in locale
1662 * encoding, unsuccessfully translated would pass in UTF-8. */
1663 isds_log_message(context, message_locale);
1664 free(code_locale);
1665 free(message_locale);
1666 free(code);
1667 free(message);
1668 xmlFreeDoc(response);
1669 return IE_ISDS;
1673 free(code);
1674 free(message);
1675 xmlFreeDoc(response);
1677 isds_log(ILF_ISDS, ILL_DEBUG,
1678 _("Bogus message accepted by server. This should not happen.\n"));
1680 return IE_SUCCESS;
1681 #else /* not HAVE_LIBCURL */
1682 return IE_NOTSUP;
1683 #endif
1687 #if HAVE_LIBCURL
1688 /* Serialize XML subtree to buffer preserving XML indentation.
1689 * @context is session context
1690 * @subtree is XML element to be serialized (with children)
1691 * @buffer is automatically reallocated buffer where serialize to
1692 * @length is size of serialized stream in bytes
1693 * @return standard error code, free @buffer in case of error */
1694 static isds_error serialize_subtree(struct isds_ctx *context,
1695 xmlNodePtr subtree, void **buffer, size_t *length) {
1696 isds_error err = IE_SUCCESS;
1697 xmlBufferPtr xml_buffer = NULL;
1698 xmlSaveCtxtPtr save_ctx = NULL;
1699 xmlDocPtr subtree_doc = NULL;
1700 xmlNodePtr subtree_copy;
1701 xmlNsPtr isds_ns;
1702 void *new_buffer;
1704 if (!context) return IE_INVALID_CONTEXT;
1705 if (!buffer) return IE_INVAL;
1706 zfree(*buffer);
1707 if (!subtree || !length) return IE_INVAL;
1709 /* Make temporary XML document with @subtree root element */
1710 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1711 * It can result in not well-formed on invalid XML tree (e.g. name space
1712 * prefix definition can miss. */
1713 /*FIXME */
1715 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1716 if (!subtree_doc) {
1717 isds_log_message(context, _("Could not build temporary document"));
1718 err = IE_ERROR;
1719 goto leave;
1722 /* XXX: Copy subtree and attach the copy to document.
1723 * One node can not bee attached into more document at the same time.
1724 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1725 * automatically.
1726 * XXX: Check xmlSaveTree() too. */
1727 subtree_copy = xmlCopyNodeList(subtree);
1728 if (!subtree_copy) {
1729 isds_log_message(context, _("Could not copy subtree"));
1730 err = IE_ERROR;
1731 goto leave;
1733 xmlDocSetRootElement(subtree_doc, subtree_copy);
1735 /* Only this way we get namespace definition as @xmlns:isds,
1736 * otherwise we get namespace prefix without definition */
1737 /* FIXME: Don't overwrite original default namespace */
1738 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1739 if(!isds_ns) {
1740 isds_log_message(context, _("Could not create ISDS name space"));
1741 err = IE_ERROR;
1742 goto leave;
1744 xmlSetNs(subtree_copy, isds_ns);
1747 /* Serialize the document into buffer */
1748 xml_buffer = xmlBufferCreate();
1749 if (!xml_buffer) {
1750 isds_log_message(context, _("Could not create xmlBuffer"));
1751 err = IE_ERROR;
1752 goto leave;
1754 /* Last argument 0 means to not format the XML tree */
1755 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1756 if (!save_ctx) {
1757 isds_log_message(context, _("Could not create XML serializer"));
1758 err = IE_ERROR;
1759 goto leave;
1761 /* XXX: According LibXML documentation, this function does not return
1762 * meaningful value yet */
1763 xmlSaveDoc(save_ctx, subtree_doc);
1764 if (-1 == xmlSaveFlush(save_ctx)) {
1765 isds_log_message(context,
1766 _("Could not serialize XML subtree"));
1767 err = IE_ERROR;
1768 goto leave;
1770 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1771 * even after xmlSaveFlush(). Thus close it here */
1772 xmlSaveClose(save_ctx); save_ctx = NULL;
1775 /* Store and detach buffer from xml_buffer */
1776 *buffer = xml_buffer->content;
1777 *length = xml_buffer->use;
1778 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1780 /* Shrink buffer */
1781 new_buffer = realloc(*buffer, *length);
1782 if (new_buffer) *buffer = new_buffer;
1784 leave:
1785 if (err) {
1786 zfree(*buffer);
1787 *length = 0;
1790 xmlSaveClose(save_ctx);
1791 xmlBufferFree(xml_buffer);
1792 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1793 return err;
1795 #endif /* HAVE_LIBCURL */
1798 #if 0
1799 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1800 * @context is session context
1801 * @document is original document where @nodeset points to
1802 * @nodeset is XPath node set to dump (recursively)
1803 * @buffer is automatically reallocated buffer where serialize to
1804 * @length is size of serialized stream in bytes
1805 * @return standard error code, free @buffer in case of error */
1806 static isds_error dump_nodeset(struct isds_ctx *context,
1807 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1808 void **buffer, size_t *length) {
1809 isds_error err = IE_SUCCESS;
1810 xmlBufferPtr xml_buffer = NULL;
1811 void *new_buffer;
1813 if (!context) return IE_INVALID_CONTEXT;
1814 if (!buffer) return IE_INVAL;
1815 zfree(*buffer);
1816 if (!document || !nodeset || !length) return IE_INVAL;
1817 *length = 0;
1819 /* Empty node set results into NULL buffer */
1820 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1821 goto leave;
1824 /* Resulting the document into buffer */
1825 xml_buffer = xmlBufferCreate();
1826 if (!xml_buffer) {
1827 isds_log_message(context, _("Could not create xmlBuffer"));
1828 err = IE_ERROR;
1829 goto leave;
1832 /* Iterate over all nodes */
1833 for (int i = 0; i < nodeset->nodeNr; i++) {
1834 /* Serialize node.
1835 * XXX: xmlNodeDump() appends to xml_buffer. */
1836 if (-1 ==
1837 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1838 isds_log_message(context, _("Could not dump XML node"));
1839 err = IE_ERROR;
1840 goto leave;
1844 /* Store and detach buffer from xml_buffer */
1845 *buffer = xml_buffer->content;
1846 *length = xml_buffer->use;
1847 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1849 /* Shrink buffer */
1850 new_buffer = realloc(*buffer, *length);
1851 if (new_buffer) *buffer = new_buffer;
1854 leave:
1855 if (err) {
1856 zfree(*buffer);
1857 *length = 0;
1860 xmlBufferFree(xml_buffer);
1861 return err;
1863 #endif
1865 #if 0
1866 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1867 * @context is session context
1868 * @document is original document where @nodeset points to
1869 * @nodeset is XPath node set to dump (recursively)
1870 * @buffer is automatically reallocated buffer where serialize to
1871 * @length is size of serialized stream in bytes
1872 * @return standard error code, free @buffer in case of error */
1873 static isds_error dump_nodeset(struct isds_ctx *context,
1874 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1875 void **buffer, size_t *length) {
1876 isds_error err = IE_SUCCESS;
1877 xmlBufferPtr xml_buffer = NULL;
1878 xmlSaveCtxtPtr save_ctx = NULL;
1879 void *new_buffer;
1881 if (!context) return IE_INVALID_CONTEXT;
1882 if (!buffer) return IE_INVAL;
1883 zfree(*buffer);
1884 if (!document || !nodeset || !length) return IE_INVAL;
1885 *length = 0;
1887 /* Empty node set results into NULL buffer */
1888 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1889 goto leave;
1892 /* Resulting the document into buffer */
1893 xml_buffer = xmlBufferCreate();
1894 if (!xml_buffer) {
1895 isds_log_message(context, _("Could not create xmlBuffer"));
1896 err = IE_ERROR;
1897 goto leave;
1899 if (xmlSubstituteEntitiesDefault(1)) {
1900 isds_log_message(context, _("Could not disable attribute escaping"));
1901 err = IE_ERROR;
1902 goto leave;
1904 /* Last argument means:
1905 * 0 to not format the XML tree
1906 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1907 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1908 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1909 if (!save_ctx) {
1910 isds_log_message(context, _("Could not create XML serializer"));
1911 err = IE_ERROR;
1912 goto leave;
1914 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1915 isds_log_message(context, _("Could not disable attribute escaping"));
1916 err = IE_ERROR;
1917 goto leave;
1921 /* Iterate over all nodes */
1922 for (int i = 0; i < nodeset->nodeNr; i++) {
1923 /* Serialize node.
1924 * XXX: xmlNodeDump() appends to xml_buffer. */
1925 /*if (-1 ==
1926 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1928 /* XXX: According LibXML documentation, this function does not return
1929 * meaningful value yet */
1930 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1931 if (-1 == xmlSaveFlush(save_ctx)) {
1932 isds_log_message(context,
1933 _("Could not serialize XML subtree"));
1934 err = IE_ERROR;
1935 goto leave;
1939 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1940 * even after xmlSaveFlush(). Thus close it here */
1941 xmlSaveClose(save_ctx); save_ctx = NULL;
1943 /* Store and detach buffer from xml_buffer */
1944 *buffer = xml_buffer->content;
1945 *length = xml_buffer->use;
1946 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1948 /* Shrink buffer */
1949 new_buffer = realloc(*buffer, *length);
1950 if (new_buffer) *buffer = new_buffer;
1952 leave:
1953 if (err) {
1954 zfree(*buffer);
1955 *length = 0;
1958 xmlSaveClose(save_ctx);
1959 xmlBufferFree(xml_buffer);
1960 return err;
1962 #endif
1965 #if HAVE_LIBCURL
1966 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1967 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1968 if (!string || !type) return IE_INVAL;
1970 if (!xmlStrcmp(string, BAD_CAST "FO"))
1971 *type = DBTYPE_FO;
1972 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1973 *type = DBTYPE_PFO;
1974 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1975 *type = DBTYPE_PFO_ADVOK;
1976 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1977 *type = DBTYPE_PFO_DANPOR;
1978 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1979 *type = DBTYPE_PFO_INSSPR;
1980 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1981 *type = DBTYPE_PO;
1982 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1983 *type = DBTYPE_PO_ZAK;
1984 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1985 *type = DBTYPE_PO_REQ;
1986 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1987 *type = DBTYPE_OVM;
1988 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1989 *type = DBTYPE_OVM_NOTAR;
1990 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1991 *type = DBTYPE_OVM_EXEKUT;
1992 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1993 *type = DBTYPE_OVM_REQ;
1994 else
1995 return IE_ENUM;
1996 return IE_SUCCESS;
2000 /* Convert ISDS dbType enum @type to UTF-8 string.
2001 * @Return pointer to static string, or NULL if unknown enum value */
2002 static const xmlChar *isds_DbType2string(const isds_DbType type) {
2003 switch(type) {
2004 /* DBTYPE_SYSTEM is invalid value from point of view of public
2005 * SOAP interface. */
2006 case DBTYPE_FO: return(BAD_CAST "FO"); break;
2007 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
2008 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
2009 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
2010 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
2011 case DBTYPE_PO: return(BAD_CAST "PO"); break;
2012 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
2013 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
2014 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
2015 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
2016 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
2017 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
2018 default: return NULL; break;
2023 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2024 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
2025 if (!string || !type) return IE_INVAL;
2027 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2028 *type = USERTYPE_PRIMARY;
2029 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2030 *type = USERTYPE_ENTRUSTED;
2031 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2032 *type = USERTYPE_ADMINISTRATOR;
2033 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2034 *type = USERTYPE_OFFICIAL;
2035 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2036 *type = USERTYPE_OFFICIAL_CERT;
2037 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2038 *type = USERTYPE_LIQUIDATOR;
2039 else
2040 return IE_ENUM;
2041 return IE_SUCCESS;
2045 /* Convert ISDS userType enum @type to UTF-8 string.
2046 * @Return pointer to static string, or NULL if unknown enum value */
2047 static const xmlChar *isds_UserType2string(const isds_UserType type) {
2048 switch(type) {
2049 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
2050 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
2051 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
2052 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
2053 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
2054 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
2055 default: return NULL; break;
2060 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2061 static isds_error string2isds_sender_type(const xmlChar *string,
2062 isds_sender_type *type) {
2063 if (!string || !type) return IE_INVAL;
2065 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2066 *type = SENDERTYPE_PRIMARY;
2067 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2068 *type = SENDERTYPE_ENTRUSTED;
2069 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2070 *type = SENDERTYPE_ADMINISTRATOR;
2071 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2072 *type = SENDERTYPE_OFFICIAL;
2073 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2074 *type = SENDERTYPE_VIRTUAL;
2075 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2076 *type = SENDERTYPE_OFFICIAL_CERT;
2077 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2078 *type = SENDERTYPE_LIQUIDATOR;
2079 else
2080 return IE_ENUM;
2081 return IE_SUCCESS;
2085 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2086 static isds_error string2isds_payment_type(const xmlChar *string,
2087 isds_payment_type *type) {
2088 if (!string || !type) return IE_INVAL;
2090 if (!xmlStrcmp(string, BAD_CAST "K"))
2091 *type = PAYMENT_SENDER;
2092 else if (!xmlStrcmp(string, BAD_CAST "O"))
2093 *type = PAYMENT_RESPONSE;
2094 else if (!xmlStrcmp(string, BAD_CAST "G"))
2095 *type = PAYMENT_SPONSOR;
2096 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2097 *type = PAYMENT_SPONSOR_LIMITED;
2098 else if (!xmlStrcmp(string, BAD_CAST "D"))
2099 *type = PAYMENT_SPONSOR_EXTERNAL;
2100 else if (!xmlStrcmp(string, BAD_CAST "E"))
2101 *type = PAYMENT_STAMP;
2102 else
2103 return IE_ENUM;
2104 return IE_SUCCESS;
2108 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2109 * ciEventType is integer but we convert it from string representation
2110 * directly. */
2111 static isds_error string2isds_credit_event_type(const xmlChar *string,
2112 isds_credit_event_type *type) {
2113 if (!string || !type) return IE_INVAL;
2115 if (!xmlStrcmp(string, BAD_CAST "1"))
2116 *type = ISDS_CREDIT_CHARGED;
2117 else if (!xmlStrcmp(string, BAD_CAST "2"))
2118 *type = ISDS_CREDIT_DISCHARGED;
2119 else if (!xmlStrcmp(string, BAD_CAST "3"))
2120 *type = ISDS_CREDIT_MESSAGE_SENT;
2121 else if (!xmlStrcmp(string, BAD_CAST "4"))
2122 *type = ISDS_CREDIT_STORAGE_SET;
2123 else if (!xmlStrcmp(string, BAD_CAST "5"))
2124 *type = ISDS_CREDIT_EXPIRED;
2125 else
2126 return IE_ENUM;
2127 return IE_SUCCESS;
2131 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2132 * @Return pointer to static string, or NULL if unknown enum value */
2133 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2134 switch(type) {
2135 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2136 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2137 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2138 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2139 default: return NULL; break;
2144 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2145 * ISDSSearch2/searchType value.
2146 * @Return pointer to static string, or NULL if unknown enum value */
2147 static const xmlChar *isds_fulltext_target2string(
2148 const isds_fulltext_target type) {
2149 switch(type) {
2150 case FULLTEXT_ALL: return(BAD_CAST "GENERAL"); break;
2151 case FULLTEXT_ADDRESS: return(BAD_CAST "ADDRESS"); break;
2152 case FULLTEXT_IC: return(BAD_CAST "ICO"); break;
2153 case FULLTEXT_BOX_ID: return(BAD_CAST "DBID"); break;
2154 default: return NULL; break;
2157 #endif /* HAVE_LIBCURL */
2160 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2161 * @Return IE_ENUM if @string is not valid enum member */
2162 static isds_error string2isds_FileMetaType(const xmlChar *string,
2163 isds_FileMetaType *type) {
2164 if (!string || !type) return IE_INVAL;
2166 if (!xmlStrcmp(string, BAD_CAST "main"))
2167 *type = FILEMETATYPE_MAIN;
2168 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2169 *type = FILEMETATYPE_ENCLOSURE;
2170 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2171 *type = FILEMETATYPE_SIGNATURE;
2172 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2173 *type = FILEMETATYPE_META;
2174 else
2175 return IE_ENUM;
2176 return IE_SUCCESS;
2180 /* Convert UTF-8 @string to ISDS hash @algorithm.
2181 * @Return IE_ENUM if @string is not valid enum member */
2182 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2183 isds_hash_algorithm *algorithm) {
2184 if (!string || !algorithm) return IE_INVAL;
2186 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2187 *algorithm = HASH_ALGORITHM_MD5;
2188 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2189 *algorithm = HASH_ALGORITHM_SHA_1;
2190 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2191 *algorithm = HASH_ALGORITHM_SHA_224;
2192 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2193 *algorithm = HASH_ALGORITHM_SHA_256;
2194 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2195 *algorithm = HASH_ALGORITHM_SHA_384;
2196 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2197 *algorithm = HASH_ALGORITHM_SHA_512;
2198 else
2199 return IE_ENUM;
2200 return IE_SUCCESS;
2204 #if HAVE_LIBCURL
2205 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2206 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2207 if (!time || !string) return IE_INVAL;
2209 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2210 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2211 return IE_ERROR;
2213 return IE_SUCCESS;
2217 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2218 * respects the @time microseconds too. */
2219 static isds_error timeval2timestring(const struct timeval *time,
2220 xmlChar **string) {
2221 struct tm broken;
2223 if (!time || !string) return IE_INVAL;
2225 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
2226 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2228 /* TODO: small negative year should be formatted as "-0012". This is not
2229 * true for glibc "%04d". We should implement it.
2230 * time->tv_usec type is su_seconds_t which is required to be signed
2231 * integer to accomodate values from range [-1, 1000000].
2232 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2233 if (-1 == isds_asprintf((char **) string,
2234 "%04d-%02d-%02dT%02d:%02d:%02d.%06jd",
2235 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2236 broken.tm_hour, broken.tm_min, broken.tm_sec,
2237 (intmax_t)time->tv_usec))
2238 return IE_ERROR;
2240 return IE_SUCCESS;
2242 #endif /* HAVE_LIBCURL */
2245 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2246 * It respects microseconds too.
2247 * In case of error, @time will be freed. */
2248 static isds_error timestring2timeval(const xmlChar *string,
2249 struct timeval **time) {
2250 struct tm broken;
2251 char *offset, *delim, *endptr;
2252 char subseconds[7];
2253 int offset_hours, offset_minutes;
2254 int i;
2255 long int long_number;
2256 #ifdef _WIN32
2257 int tmp;
2258 #endif
2260 if (!time) return IE_INVAL;
2261 if (!string) {
2262 zfree(*time);
2263 return IE_INVAL;
2266 memset(&broken, 0, sizeof(broken));
2268 if (!*time) {
2269 *time = calloc(1, sizeof(**time));
2270 if (!*time) return IE_NOMEM;
2271 } else {
2272 memset(*time, 0, sizeof(**time));
2276 /* xsd:date is ISO 8601 string, thus ASCII */
2277 /*TODO: negative year */
2279 #ifdef _WIN32
2280 i = 0;
2281 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2282 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2283 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2284 &i)) < 6) {
2285 zfree(*time);
2286 return IE_DATE;
2289 broken.tm_year -= 1900;
2290 broken.tm_mon--;
2291 offset = (char*)string + i;
2292 #else
2293 /* Parse date and time without subseconds and offset */
2294 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2295 if (!offset) {
2296 zfree(*time);
2297 return IE_DATE;
2299 #endif
2301 /* Get subseconds */
2302 if (*offset == '.' ) {
2303 offset++;
2305 /* Copy first 6 digits, pad it with zeros.
2306 * XXX: It truncates longer number, no round.
2307 * Current server implementation uses only millisecond resolution. */
2308 /* TODO: isdigit() is locale sensitive */
2309 for (i = 0;
2310 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
2311 i++, offset++) {
2312 subseconds[i] = *offset;
2314 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
2315 subseconds[i] = '0';
2317 subseconds[6] = '\0';
2319 /* Convert it into integer */
2320 long_number = strtol(subseconds, &endptr, 10);
2321 if (*endptr != '\0' || long_number == LONG_MIN ||
2322 long_number == LONG_MAX) {
2323 zfree(*time);
2324 return IE_DATE;
2326 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2327 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2328 * microseconds" and "the type shall be a signed integer capable of
2329 * storing values at least in the range [-1, 1000000]. */
2330 if (long_number < -1 || long_number >= 1000000) {
2331 zfree(*time);
2332 return IE_DATE;
2334 (*time)->tv_usec = long_number;
2336 /* move to the zone offset delimiter or signal NULL*/
2337 delim = strchr(offset, '-');
2338 if (!delim)
2339 delim = strchr(offset, '+');
2340 if (!delim)
2341 delim = strchr(offset, 'Z');
2342 offset = delim;
2345 /* Get zone offset */
2346 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2347 * "" equals to "Z" and it means UTC zone. */
2348 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2349 * colon separator */
2350 if (offset && (*offset == '-' || *offset == '+')) {
2351 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2352 zfree(*time);
2353 return IE_DATE;
2355 if (*offset == '+') {
2356 broken.tm_hour -= offset_hours;
2357 broken.tm_min -= offset_minutes;
2358 } else {
2359 broken.tm_hour += offset_hours;
2360 broken.tm_min += offset_minutes;
2364 /* Convert to time_t */
2365 (*time)->tv_sec = _isds_timegm(&broken);
2366 if ((*time)->tv_sec == (time_t) -1) {
2367 zfree(*time);
2368 return IE_DATE;
2371 return IE_SUCCESS;
2375 /* Convert unsigned int into isds_message_status.
2376 * @context is session context
2377 * @number is pointer to number value. NULL will be treated as invalid value.
2378 * @status is automatically reallocated status
2379 * @return IE_SUCCESS, or error code and free status */
2380 static isds_error uint2isds_message_status(struct isds_ctx *context,
2381 const unsigned long int *number, isds_message_status **status) {
2382 if (!context) return IE_INVALID_CONTEXT;
2383 if (!status) return IE_INVAL;
2385 free(*status); *status = NULL;
2386 if (!number) return IE_INVAL;
2388 if (*number < 1 || *number > 10) {
2389 isds_printf_message(context, _("Invalid message status value: %lu"),
2390 *number);
2391 return IE_ENUM;
2394 *status = malloc(sizeof(**status));
2395 if (!*status) return IE_NOMEM;
2397 **status = 1 << *number;
2398 return IE_SUCCESS;
2402 /* Convert event description string into isds_event members type and
2403 * description
2404 * @string is raw event description starting with event prefix
2405 * @event is structure where to store type and stripped description to
2406 * @return standard error code, unknown prefix is not classified as an error.
2407 * */
2408 static isds_error eventstring2event(const xmlChar *string,
2409 struct isds_event* event) {
2410 const xmlChar *known_prefixes[] = {
2411 BAD_CAST "EV0:",
2412 BAD_CAST "EV1:",
2413 BAD_CAST "EV2:",
2414 BAD_CAST "EV3:",
2415 BAD_CAST "EV4:",
2416 BAD_CAST "EV5:",
2417 BAD_CAST "EV11:",
2418 BAD_CAST "EV12:",
2419 BAD_CAST "EV13:"
2421 const isds_event_type types[] = {
2422 EVENT_ENTERED_SYSTEM,
2423 EVENT_ACCEPTED_BY_RECIPIENT,
2424 EVENT_ACCEPTED_BY_FICTION,
2425 EVENT_UNDELIVERABLE,
2426 EVENT_COMMERCIAL_ACCEPTED,
2427 EVENT_DELIVERED,
2428 EVENT_PRIMARY_LOGIN,
2429 EVENT_ENTRUSTED_LOGIN,
2430 EVENT_SYSCERT_LOGIN
2432 unsigned int index;
2433 size_t length;
2435 if (!string || !event) return IE_INVAL;
2437 if (!event->type) {
2438 event->type = malloc(sizeof(*event->type));
2439 if (!(event->type)) return IE_NOMEM;
2441 zfree(event->description);
2443 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2444 index++) {
2445 length = xmlUTF8Strlen(known_prefixes[index]);
2447 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2448 /* Prefix is known */
2449 *event->type = types[index];
2451 /* Strip prefix from description and spaces */
2452 /* TODO: Recognize all white spaces from UCS blank class and
2453 * operate on UTF-8 chars. */
2454 for (; string[length] != '\0' && string[length] == ' '; length++);
2455 event->description = strdup((char *) (string + length));
2456 if (!(event->description)) return IE_NOMEM;
2458 return IE_SUCCESS;
2462 /* Unknown event prefix.
2463 * XSD allows any string */
2464 char *string_locale = _isds_utf82locale((char *) string);
2465 isds_log(ILF_ISDS, ILL_WARNING,
2466 _("Unknown delivery info event prefix: %s\n"), string_locale);
2467 free(string_locale);
2469 *event->type = EVENT_UKNOWN;
2470 event->description = strdup((char *) string);
2471 if (!(event->description)) return IE_NOMEM;
2473 return IE_SUCCESS;
2477 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2478 * and leave label */
2479 #define EXTRACT_STRING(element, string) { \
2480 xmlXPathFreeObject(result); \
2481 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2482 if (NULL == (result)) { \
2483 err = IE_ERROR; \
2484 goto leave; \
2486 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2487 if (result->nodesetval->nodeNr > 1) { \
2488 isds_printf_message(context, _("Multiple %s element"), element); \
2489 err = IE_ERROR; \
2490 goto leave; \
2492 (string) = (char *) \
2493 xmlXPathCastNodeSetToString(result->nodesetval); \
2494 if (NULL == (string)) { \
2495 err = IE_ERROR; \
2496 goto leave; \
2501 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2503 char *string = NULL; \
2504 EXTRACT_STRING(element, string); \
2506 if (string) { \
2507 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2508 if (!(booleanPtr)) { \
2509 free(string); \
2510 err = IE_NOMEM; \
2511 goto leave; \
2514 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2515 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2516 *(booleanPtr) = 1; \
2517 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2518 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2519 *(booleanPtr) = 0; \
2520 else { \
2521 char *string_locale = _isds_utf82locale((char*)string); \
2522 isds_printf_message(context, \
2523 _("%s value is not valid boolean: %s"), \
2524 element, string_locale); \
2525 free(string_locale); \
2526 free(string); \
2527 err = IE_ERROR; \
2528 goto leave; \
2531 free(string); \
2535 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2537 char *string = NULL; \
2538 EXTRACT_STRING(element, string); \
2540 if (NULL == string) { \
2541 isds_printf_message(context, _("%s element is empty"), element); \
2542 err = IE_ERROR; \
2543 goto leave; \
2545 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2546 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2547 (boolean) = 1; \
2548 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2549 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2550 (boolean) = 0; \
2551 else { \
2552 char *string_locale = _isds_utf82locale((char*)string); \
2553 isds_printf_message(context, \
2554 _("%s value is not valid boolean: %s"), \
2555 element, string_locale); \
2556 free(string_locale); \
2557 free(string); \
2558 err = IE_ERROR; \
2559 goto leave; \
2562 free(string); \
2565 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2567 char *string = NULL; \
2568 EXTRACT_STRING(element, string); \
2569 if (string) { \
2570 long int number; \
2571 char *endptr; \
2573 number = strtol((char*)string, &endptr, 10); \
2575 if (*endptr != '\0') { \
2576 char *string_locale = _isds_utf82locale((char *)string); \
2577 isds_printf_message(context, \
2578 _("%s is not valid integer: %s"), \
2579 element, string_locale); \
2580 free(string_locale); \
2581 free(string); \
2582 err = IE_ISDS; \
2583 goto leave; \
2586 if (number == LONG_MIN || number == LONG_MAX) { \
2587 char *string_locale = _isds_utf82locale((char *)string); \
2588 isds_printf_message(context, \
2589 _("%s value out of range of long int: %s"), \
2590 element, string_locale); \
2591 free(string_locale); \
2592 free(string); \
2593 err = IE_ERROR; \
2594 goto leave; \
2597 free(string); string = NULL; \
2599 if (!(preallocated)) { \
2600 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2601 if (!(longintPtr)) { \
2602 err = IE_NOMEM; \
2603 goto leave; \
2606 *(longintPtr) = number; \
2610 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2612 char *string = NULL; \
2613 EXTRACT_STRING(element, string); \
2614 if (string) { \
2615 long int number; \
2616 char *endptr; \
2618 number = strtol((char*)string, &endptr, 10); \
2620 if (*endptr != '\0') { \
2621 char *string_locale = _isds_utf82locale((char *)string); \
2622 isds_printf_message(context, \
2623 _("%s is not valid integer: %s"), \
2624 element, string_locale); \
2625 free(string_locale); \
2626 free(string); \
2627 err = IE_ISDS; \
2628 goto leave; \
2631 if (number == LONG_MIN || number == LONG_MAX) { \
2632 char *string_locale = _isds_utf82locale((char *)string); \
2633 isds_printf_message(context, \
2634 _("%s value out of range of long int: %s"), \
2635 element, string_locale); \
2636 free(string_locale); \
2637 free(string); \
2638 err = IE_ERROR; \
2639 goto leave; \
2642 free(string); string = NULL; \
2643 if (number < 0) { \
2644 isds_printf_message(context, \
2645 _("%s value is negative: %ld"), element, number); \
2646 err = IE_ERROR; \
2647 goto leave; \
2650 if (!(preallocated)) { \
2651 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2652 if (!(ulongintPtr)) { \
2653 err = IE_NOMEM; \
2654 goto leave; \
2657 *(ulongintPtr) = number; \
2661 #define EXTRACT_DATE(element, tmPtr) { \
2662 char *string = NULL; \
2663 EXTRACT_STRING(element, string); \
2664 if (NULL != string) { \
2665 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2666 if (NULL == (tmPtr)) { \
2667 free(string); \
2668 err = IE_NOMEM; \
2669 goto leave; \
2671 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2672 if (err) { \
2673 if (err == IE_NOTSUP) { \
2674 err = IE_ISDS; \
2675 char *string_locale = _isds_utf82locale(string); \
2676 char *element_locale = _isds_utf82locale(element); \
2677 isds_printf_message(context, _("Invalid %s value: %s"), \
2678 element_locale, string_locale); \
2679 free(string_locale); \
2680 free(element_locale); \
2682 free(string); \
2683 goto leave; \
2685 free(string); \
2689 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2690 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2691 NULL); \
2692 if ((required) && (!string)) { \
2693 char *attribute_locale = _isds_utf82locale(attribute); \
2694 char *element_locale = \
2695 _isds_utf82locale((char *)xpath_ctx->node->name); \
2696 isds_printf_message(context, \
2697 _("Could not extract required %s attribute value from " \
2698 "%s element"), attribute_locale, element_locale); \
2699 free(element_locale); \
2700 free(attribute_locale); \
2701 err = IE_ERROR; \
2702 goto leave; \
2707 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2709 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2710 (xmlChar *) (string)); \
2711 if (!node) { \
2712 isds_printf_message(context, \
2713 _("Could not add %s child to %s element"), \
2714 element, (parent)->name); \
2715 err = IE_ERROR; \
2716 goto leave; \
2720 #define INSERT_STRING(parent, element, string) \
2721 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2723 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2725 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2726 else { INSERT_STRING(parent, element, "false"); } \
2729 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2731 if (booleanPtr) { \
2732 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2733 } else { \
2734 INSERT_STRING(parent, element, NULL); \
2738 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2739 if ((longintPtr)) { \
2740 /* FIXME: locale sensitive */ \
2741 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2742 err = IE_NOMEM; \
2743 goto leave; \
2745 INSERT_STRING(parent, element, buffer) \
2746 free(buffer); (buffer) = NULL; \
2747 } else { INSERT_STRING(parent, element, NULL) } \
2750 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2751 if ((ulongintPtr)) { \
2752 /* FIXME: locale sensitive */ \
2753 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2754 err = IE_NOMEM; \
2755 goto leave; \
2757 INSERT_STRING(parent, element, buffer) \
2758 free(buffer); (buffer) = NULL; \
2759 } else { INSERT_STRING(parent, element, NULL) } \
2762 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2764 /* FIXME: locale sensitive */ \
2765 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2766 err = IE_NOMEM; \
2767 goto leave; \
2769 INSERT_STRING(parent, element, buffer) \
2770 free(buffer); (buffer) = NULL; \
2773 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2774 * new attribute. */
2775 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2777 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2778 (xmlChar *) (string)); \
2779 if (!attribute_node) { \
2780 isds_printf_message(context, _("Could not add %s " \
2781 "attribute to %s element"), \
2782 (attribute), (parent)->name); \
2783 err = IE_ERROR; \
2784 goto leave; \
2788 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2789 if (string) { \
2790 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2791 if (length > (maximum)) { \
2792 isds_printf_message(context, \
2793 ngettext("%s has more than %d characters", \
2794 "%s has more than %d characters", (maximum)), \
2795 (name), (maximum)); \
2796 err = IE_2BIG; \
2797 goto leave; \
2799 if (length < (minimum)) { \
2800 isds_printf_message(context, \
2801 ngettext("%s has less than %d characters", \
2802 "%s has less than %d characters", (minimum)), \
2803 (name), (minimum)); \
2804 err = IE_2SMALL; \
2805 goto leave; \
2810 #define INSERT_ELEMENT(child, parent, element) \
2812 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2813 if (!(child)) { \
2814 isds_printf_message(context, \
2815 _("Could not add %s child to %s element"), \
2816 (element), (parent)->name); \
2817 err = IE_ERROR; \
2818 goto leave; \
2823 /* Find child element by name in given XPath context and switch context onto
2824 * it. The child must be uniq and must exist. Otherwise fails.
2825 * @context is ISDS context
2826 * @child is child element name
2827 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2828 * into it child. In error case, the @xpath_ctx keeps original value. */
2829 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2830 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2831 isds_error err = IE_SUCCESS;
2832 xmlXPathObjectPtr result = NULL;
2834 if (!context) return IE_INVALID_CONTEXT;
2835 if (!child || !xpath_ctx) return IE_INVAL;
2837 /* Find child */
2838 result = xmlXPathEvalExpression(child, xpath_ctx);
2839 if (!result) {
2840 err = IE_XML;
2841 goto leave;
2844 /* No match */
2845 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2846 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2847 char *child_locale = _isds_utf82locale((char*) child);
2848 isds_printf_message(context,
2849 _("%s element does not contain %s child"),
2850 parent_locale, child_locale);
2851 free(child_locale);
2852 free(parent_locale);
2853 err = IE_NOEXIST;
2854 goto leave;
2857 /* More matches */
2858 if (result->nodesetval->nodeNr > 1) {
2859 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2860 char *child_locale = _isds_utf82locale((char*) child);
2861 isds_printf_message(context,
2862 _("%s element contains multiple %s children"),
2863 parent_locale, child_locale);
2864 free(child_locale);
2865 free(parent_locale);
2866 err = IE_NOTUNIQ;
2867 goto leave;
2870 /* Switch context */
2871 xpath_ctx->node = result->nodesetval->nodeTab[0];
2873 leave:
2874 xmlXPathFreeObject(result);
2875 return err;
2880 #if HAVE_LIBCURL
2881 /* Find and convert XSD:gPersonName group in current node into structure
2882 * @context is ISDS context
2883 * @personName is automatically reallocated person name structure. If no member
2884 * value is found, will be freed.
2885 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2886 * elements
2887 * In case of error @personName will be freed. */
2888 static isds_error extract_gPersonName(struct isds_ctx *context,
2889 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2890 isds_error err = IE_SUCCESS;
2891 xmlXPathObjectPtr result = NULL;
2893 if (!context) return IE_INVALID_CONTEXT;
2894 if (!personName) return IE_INVAL;
2895 isds_PersonName_free(personName);
2896 if (!xpath_ctx) return IE_INVAL;
2899 *personName = calloc(1, sizeof(**personName));
2900 if (!*personName) {
2901 err = IE_NOMEM;
2902 goto leave;
2905 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2906 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2907 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2908 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2910 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2911 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2912 isds_PersonName_free(personName);
2914 leave:
2915 if (err) isds_PersonName_free(personName);
2916 xmlXPathFreeObject(result);
2917 return err;
2921 /* Find and convert XSD:gAddress group in current node into structure
2922 * @context is ISDS context
2923 * @address is automatically reallocated address structure. If no member
2924 * value is found, will be freed.
2925 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2926 * elements
2927 * In case of error @address will be freed. */
2928 static isds_error extract_gAddress(struct isds_ctx *context,
2929 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2930 isds_error err = IE_SUCCESS;
2931 xmlXPathObjectPtr result = NULL;
2933 if (!context) return IE_INVALID_CONTEXT;
2934 if (!address) return IE_INVAL;
2935 isds_Address_free(address);
2936 if (!xpath_ctx) return IE_INVAL;
2939 *address = calloc(1, sizeof(**address));
2940 if (!*address) {
2941 err = IE_NOMEM;
2942 goto leave;
2945 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2946 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2947 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2948 EXTRACT_STRING("isds:adNumberInMunicipality",
2949 (*address)->adNumberInMunicipality);
2950 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2951 EXTRACT_STRING("isds:adState", (*address)->adState);
2953 if (!(*address)->adCity && !(*address)->adStreet &&
2954 !(*address)->adNumberInStreet &&
2955 !(*address)->adNumberInMunicipality &&
2956 !(*address)->adZipCode && !(*address)->adState)
2957 isds_Address_free(address);
2959 leave:
2960 if (err) isds_Address_free(address);
2961 xmlXPathFreeObject(result);
2962 return err;
2966 /* Find and convert isds:biDate element in current node into structure
2967 * @context is ISDS context
2968 * @biDate is automatically reallocated birth date structure. If no member
2969 * value is found, will be freed.
2970 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2971 * element
2972 * In case of error @biDate will be freed. */
2973 static isds_error extract_BiDate(struct isds_ctx *context,
2974 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2975 isds_error err = IE_SUCCESS;
2976 xmlXPathObjectPtr result = NULL;
2977 char *string = NULL;
2979 if (!context) return IE_INVALID_CONTEXT;
2980 if (!biDate) return IE_INVAL;
2981 zfree(*biDate);
2982 if (!xpath_ctx) return IE_INVAL;
2984 EXTRACT_STRING("isds:biDate", string);
2985 if (string) {
2986 *biDate = calloc(1, sizeof(**biDate));
2987 if (!*biDate) {
2988 err = IE_NOMEM;
2989 goto leave;
2991 err = _isds_datestring2tm((xmlChar *)string, *biDate);
2992 if (err) {
2993 if (err == IE_NOTSUP) {
2994 err = IE_ISDS;
2995 char *string_locale = _isds_utf82locale(string);
2996 isds_printf_message(context,
2997 _("Invalid isds:biDate value: %s"), string_locale);
2998 free(string_locale);
3000 goto leave;
3004 leave:
3005 if (err) zfree(*biDate);
3006 free(string);
3007 xmlXPathFreeObject(result);
3008 return err;
3012 /* Convert isds:dBOwnerInfo XML tree into structure
3013 * @context is ISDS context
3014 * @db_owner_info is automatically reallocated box owner info structure
3015 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
3016 * In case of error @db_owner_info will be freed. */
3017 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
3018 struct isds_DbOwnerInfo **db_owner_info,
3019 xmlXPathContextPtr xpath_ctx) {
3020 isds_error err = IE_SUCCESS;
3021 xmlXPathObjectPtr result = NULL;
3022 char *string = NULL;
3024 if (!context) return IE_INVALID_CONTEXT;
3025 if (!db_owner_info) return IE_INVAL;
3026 isds_DbOwnerInfo_free(db_owner_info);
3027 if (!xpath_ctx) return IE_INVAL;
3030 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3031 if (!*db_owner_info) {
3032 err = IE_NOMEM;
3033 goto leave;
3036 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
3038 EXTRACT_STRING("isds:dbType", string);
3039 if (string) {
3040 (*db_owner_info)->dbType =
3041 calloc(1, sizeof(*((*db_owner_info)->dbType)));
3042 if (!(*db_owner_info)->dbType) {
3043 err = IE_NOMEM;
3044 goto leave;
3046 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
3047 if (err) {
3048 zfree((*db_owner_info)->dbType);
3049 if (err == IE_ENUM) {
3050 err = IE_ISDS;
3051 char *string_locale = _isds_utf82locale(string);
3052 isds_printf_message(context, _("Unknown isds:dbType: %s"),
3053 string_locale);
3054 free(string_locale);
3056 goto leave;
3058 zfree(string);
3061 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
3063 err = extract_gPersonName(context, &(*db_owner_info)->personName,
3064 xpath_ctx);
3065 if (err) goto leave;
3067 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
3069 (*db_owner_info)->birthInfo =
3070 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
3071 if (!(*db_owner_info)->birthInfo) {
3072 err = IE_NOMEM;
3073 goto leave;
3075 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
3076 xpath_ctx);
3077 if (err) goto leave;
3078 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3079 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3080 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3081 if (!(*db_owner_info)->birthInfo->biDate &&
3082 !(*db_owner_info)->birthInfo->biCity &&
3083 !(*db_owner_info)->birthInfo->biCounty &&
3084 !(*db_owner_info)->birthInfo->biState)
3085 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3087 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3088 if (err) goto leave;
3090 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3091 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3092 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3093 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3094 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3096 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3098 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3099 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3100 (*db_owner_info)->dbOpenAddressing);
3102 leave:
3103 if (err) isds_DbOwnerInfo_free(db_owner_info);
3104 free(string);
3105 xmlXPathFreeObject(result);
3106 return err;
3110 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3111 * @context is session context
3112 * @owner is libisds structure with box description
3113 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3114 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3115 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
3117 isds_error err = IE_SUCCESS;
3118 xmlNodePtr node;
3119 xmlChar *string = NULL;
3121 if (!context) return IE_INVALID_CONTEXT;
3122 if (!owner || !db_owner_info) return IE_INVAL;
3125 /* Build XSD:tDbOwnerInfo */
3126 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3127 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3129 /* dbType */
3130 if (owner->dbType) {
3131 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
3132 if (!type_string) {
3133 isds_printf_message(context, _("Invalid dbType value: %d"),
3134 *(owner->dbType));
3135 err = IE_ENUM;
3136 goto leave;
3138 INSERT_STRING(db_owner_info, "dbType", type_string);
3140 INSERT_STRING(db_owner_info, "ic", owner->ic);
3141 if (owner->personName) {
3142 INSERT_STRING(db_owner_info, "pnFirstName",
3143 owner->personName->pnFirstName);
3144 INSERT_STRING(db_owner_info, "pnMiddleName",
3145 owner->personName->pnMiddleName);
3146 INSERT_STRING(db_owner_info, "pnLastName",
3147 owner->personName->pnLastName);
3148 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3149 owner->personName->pnLastNameAtBirth);
3151 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3152 if (owner->birthInfo) {
3153 if (owner->birthInfo->biDate) {
3154 if (!tm2datestring(owner->birthInfo->biDate, &string))
3155 INSERT_STRING(db_owner_info, "biDate", string);
3156 free(string); string = NULL;
3158 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
3159 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
3160 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
3162 if (owner->address) {
3163 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
3164 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
3165 INSERT_STRING(db_owner_info, "adNumberInStreet",
3166 owner->address->adNumberInStreet);
3167 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3168 owner->address->adNumberInMunicipality);
3169 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
3170 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
3172 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3173 INSERT_STRING(db_owner_info, "email", owner->email);
3174 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3176 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3177 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3179 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3180 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3182 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3184 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3185 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3186 owner->dbOpenAddressing);
3188 leave:
3189 free(string);
3190 return err;
3194 /* Convert XSD:tDbUserInfo XML tree into structure
3195 * @context is ISDS context
3196 * @db_user_info is automatically reallocated user info structure
3197 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3198 * In case of error @db_user_info will be freed. */
3199 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3200 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3201 isds_error err = IE_SUCCESS;
3202 xmlXPathObjectPtr result = NULL;
3203 char *string = NULL;
3205 if (!context) return IE_INVALID_CONTEXT;
3206 if (!db_user_info) return IE_INVAL;
3207 isds_DbUserInfo_free(db_user_info);
3208 if (!xpath_ctx) return IE_INVAL;
3211 *db_user_info = calloc(1, sizeof(**db_user_info));
3212 if (!*db_user_info) {
3213 err = IE_NOMEM;
3214 goto leave;
3217 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3219 EXTRACT_STRING("isds:userType", string);
3220 if (string) {
3221 (*db_user_info)->userType =
3222 calloc(1, sizeof(*((*db_user_info)->userType)));
3223 if (!(*db_user_info)->userType) {
3224 err = IE_NOMEM;
3225 goto leave;
3227 err = string2isds_UserType((xmlChar *)string,
3228 (*db_user_info)->userType);
3229 if (err) {
3230 zfree((*db_user_info)->userType);
3231 if (err == IE_ENUM) {
3232 err = IE_ISDS;
3233 char *string_locale = _isds_utf82locale(string);
3234 isds_printf_message(context,
3235 _("Unknown isds:userType value: %s"), string_locale);
3236 free(string_locale);
3238 goto leave;
3240 zfree(string);
3243 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3245 (*db_user_info)->personName =
3246 calloc(1, sizeof(*((*db_user_info)->personName)));
3247 if (!(*db_user_info)->personName) {
3248 err = IE_NOMEM;
3249 goto leave;
3252 err = extract_gPersonName(context, &(*db_user_info)->personName,
3253 xpath_ctx);
3254 if (err) goto leave;
3256 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3257 if (err) goto leave;
3259 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3260 if (err) goto leave;
3262 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3263 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3265 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3266 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3267 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3269 /* ???: Default value is "CZ" according specification. Should we provide
3270 * it? */
3271 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3273 leave:
3274 if (err) isds_DbUserInfo_free(db_user_info);
3275 free(string);
3276 xmlXPathFreeObject(result);
3277 return err;
3281 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3282 * @context is session context
3283 * @user is libisds structure with user description
3284 * @db_user_info is XML element of XSD:tDbUserInfo */
3285 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3286 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3288 isds_error err = IE_SUCCESS;
3289 xmlNodePtr node;
3290 xmlChar *string = NULL;
3292 if (!context) return IE_INVALID_CONTEXT;
3293 if (!user || !db_user_info) return IE_INVAL;
3295 /* Build XSD:tDbUserInfo */
3296 if (user->personName) {
3297 INSERT_STRING(db_user_info, "pnFirstName",
3298 user->personName->pnFirstName);
3299 INSERT_STRING(db_user_info, "pnMiddleName",
3300 user->personName->pnMiddleName);
3301 INSERT_STRING(db_user_info, "pnLastName",
3302 user->personName->pnLastName);
3303 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3304 user->personName->pnLastNameAtBirth);
3306 if (user->address) {
3307 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3308 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3309 INSERT_STRING(db_user_info, "adNumberInStreet",
3310 user->address->adNumberInStreet);
3311 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3312 user->address->adNumberInMunicipality);
3313 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3314 INSERT_STRING(db_user_info, "adState", user->address->adState);
3316 if (user->biDate) {
3317 if (!tm2datestring(user->biDate, &string))
3318 INSERT_STRING(db_user_info, "biDate", string);
3319 zfree(string);
3321 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3322 INSERT_STRING(db_user_info, "userID", user->userID);
3324 /* userType */
3325 if (user->userType) {
3326 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3327 if (!type_string) {
3328 isds_printf_message(context, _("Invalid userType value: %d"),
3329 *(user->userType));
3330 err = IE_ENUM;
3331 goto leave;
3333 INSERT_STRING(db_user_info, "userType", type_string);
3336 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3337 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3338 INSERT_STRING(db_user_info, "ic", user->ic);
3339 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3340 INSERT_STRING(db_user_info, "firmName", user->firmName);
3341 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3342 INSERT_STRING(db_user_info, "caCity", user->caCity);
3343 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3344 INSERT_STRING(db_user_info, "caState", user->caState);
3346 leave:
3347 free(string);
3348 return err;
3352 /* Convert XSD:tPDZRec XML tree into structure
3353 * @context is ISDS context
3354 * @permission is automatically reallocated commercial permission structure
3355 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3356 * In case of error @permission will be freed. */
3357 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3358 struct isds_commercial_permission **permission,
3359 xmlXPathContextPtr xpath_ctx) {
3360 isds_error err = IE_SUCCESS;
3361 xmlXPathObjectPtr result = NULL;
3362 char *string = NULL;
3364 if (!context) return IE_INVALID_CONTEXT;
3365 if (!permission) return IE_INVAL;
3366 isds_commercial_permission_free(permission);
3367 if (!xpath_ctx) return IE_INVAL;
3370 *permission = calloc(1, sizeof(**permission));
3371 if (!*permission) {
3372 err = IE_NOMEM;
3373 goto leave;
3376 EXTRACT_STRING("isds:PDZType", string);
3377 if (string) {
3378 err = string2isds_payment_type((xmlChar *)string,
3379 &(*permission)->type);
3380 if (err) {
3381 if (err == IE_ENUM) {
3382 err = IE_ISDS;
3383 char *string_locale = _isds_utf82locale(string);
3384 isds_printf_message(context,
3385 _("Unknown isds:PDZType value: %s"), string_locale);
3386 free(string_locale);
3388 goto leave;
3390 zfree(string);
3393 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3394 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3396 EXTRACT_STRING("isds:PDZExpire", string);
3397 if (string) {
3398 err = timestring2timeval((xmlChar *) string,
3399 &((*permission)->expiration));
3400 if (err) {
3401 char *string_locale = _isds_utf82locale(string);
3402 if (err == IE_DATE) err = IE_ISDS;
3403 isds_printf_message(context,
3404 _("Could not convert PDZExpire as ISO time: %s"),
3405 string_locale);
3406 free(string_locale);
3407 goto leave;
3409 zfree(string);
3412 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3413 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3415 leave:
3416 if (err) isds_commercial_permission_free(permission);
3417 free(string);
3418 xmlXPathFreeObject(result);
3419 return err;
3423 /* Convert XSD:tCiRecord XML tree into structure
3424 * @context is ISDS context
3425 * @event is automatically reallocated commercial credit event structure
3426 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3427 * In case of error @event will be freed. */
3428 static isds_error extract_CiRecord(struct isds_ctx *context,
3429 struct isds_credit_event **event,
3430 xmlXPathContextPtr xpath_ctx) {
3431 isds_error err = IE_SUCCESS;
3432 xmlXPathObjectPtr result = NULL;
3433 char *string = NULL;
3434 long int *number_ptr;
3436 if (!context) return IE_INVALID_CONTEXT;
3437 if (!event) return IE_INVAL;
3438 isds_credit_event_free(event);
3439 if (!xpath_ctx) return IE_INVAL;
3442 *event = calloc(1, sizeof(**event));
3443 if (!*event) {
3444 err = IE_NOMEM;
3445 goto leave;
3448 EXTRACT_STRING("isds:ciEventTime", string);
3449 if (string) {
3450 err = timestring2timeval((xmlChar *) string,
3451 &(*event)->time);
3452 if (err) {
3453 char *string_locale = _isds_utf82locale(string);
3454 if (err == IE_DATE) err = IE_ISDS;
3455 isds_printf_message(context,
3456 _("Could not convert ciEventTime as ISO time: %s"),
3457 string_locale);
3458 free(string_locale);
3459 goto leave;
3461 zfree(string);
3464 EXTRACT_STRING("isds:ciEventType", string);
3465 if (string) {
3466 err = string2isds_credit_event_type((xmlChar *)string,
3467 &(*event)->type);
3468 if (err) {
3469 if (err == IE_ENUM) {
3470 err = IE_ISDS;
3471 char *string_locale = _isds_utf82locale(string);
3472 isds_printf_message(context,
3473 _("Unknown isds:ciEventType value: %s"), string_locale);
3474 free(string_locale);
3476 goto leave;
3478 zfree(string);
3481 number_ptr = &((*event)->credit_change);
3482 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3483 number_ptr = &(*event)->new_credit;
3484 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3486 switch((*event)->type) {
3487 case ISDS_CREDIT_CHARGED:
3488 EXTRACT_STRING("isds:ciTransID",
3489 (*event)->details.charged.transaction);
3490 break;
3491 case ISDS_CREDIT_DISCHARGED:
3492 EXTRACT_STRING("isds:ciTransID",
3493 (*event)->details.discharged.transaction);
3494 break;
3495 case ISDS_CREDIT_MESSAGE_SENT:
3496 EXTRACT_STRING("isds:ciRecipientID",
3497 (*event)->details.message_sent.recipient);
3498 EXTRACT_STRING("isds:ciPDZID",
3499 (*event)->details.message_sent.message_id);
3500 break;
3501 case ISDS_CREDIT_STORAGE_SET:
3502 number_ptr = &((*event)->details.storage_set.new_capacity);
3503 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3504 EXTRACT_DATE("isds:ciNewFrom",
3505 (*event)->details.storage_set.new_valid_from);
3506 EXTRACT_DATE("isds:ciNewTo",
3507 (*event)->details.storage_set.new_valid_to);
3508 EXTRACT_LONGINT("isds:ciOldCapacity",
3509 (*event)->details.storage_set.old_capacity, 0);
3510 EXTRACT_DATE("isds:ciOldFrom",
3511 (*event)->details.storage_set.old_valid_from);
3512 EXTRACT_DATE("isds:ciOldTo",
3513 (*event)->details.storage_set.old_valid_to);
3514 EXTRACT_STRING("isds:ciDoneBy",
3515 (*event)->details.storage_set.initiator);
3516 break;
3517 case ISDS_CREDIT_EXPIRED:
3518 break;
3521 leave:
3522 if (err) isds_credit_event_free(event);
3523 free(string);
3524 xmlXPathFreeObject(result);
3525 return err;
3529 #endif /* HAVE_LIBCURL */
3532 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3533 * isds_envelope structure. The envelope is automatically allocated but not
3534 * reallocated. The date are just appended into envelope structure.
3535 * @context is ISDS context
3536 * @envelope is automatically allocated message envelope structure
3537 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3538 * In case of error @envelope will be freed. */
3539 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3540 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3541 isds_error err = IE_SUCCESS;
3542 xmlXPathObjectPtr result = NULL;
3544 if (!context) return IE_INVALID_CONTEXT;
3545 if (!envelope) return IE_INVAL;
3546 if (!xpath_ctx) return IE_INVAL;
3549 if (!*envelope) {
3550 /* Allocate envelope */
3551 *envelope = calloc(1, sizeof(**envelope));
3552 if (!*envelope) {
3553 err = IE_NOMEM;
3554 goto leave;
3556 } else {
3557 /* Else free former data */
3558 zfree((*envelope)->dmSenderOrgUnit);
3559 zfree((*envelope)->dmSenderOrgUnitNum);
3560 zfree((*envelope)->dbIDRecipient);
3561 zfree((*envelope)->dmRecipientOrgUnit);
3562 zfree((*envelope)->dmRecipientOrgUnitNum);
3563 zfree((*envelope)->dmToHands);
3564 zfree((*envelope)->dmAnnotation);
3565 zfree((*envelope)->dmRecipientRefNumber);
3566 zfree((*envelope)->dmSenderRefNumber);
3567 zfree((*envelope)->dmRecipientIdent);
3568 zfree((*envelope)->dmSenderIdent);
3569 zfree((*envelope)->dmLegalTitleLaw);
3570 zfree((*envelope)->dmLegalTitleYear);
3571 zfree((*envelope)->dmLegalTitleSect);
3572 zfree((*envelope)->dmLegalTitlePar);
3573 zfree((*envelope)->dmLegalTitlePoint);
3574 zfree((*envelope)->dmPersonalDelivery);
3575 zfree((*envelope)->dmAllowSubstDelivery);
3578 /* Extract envelope elements added by sender or ISDS
3579 * (XSD: gMessageEnvelopeSub type) */
3580 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3581 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3582 (*envelope)->dmSenderOrgUnitNum, 0);
3583 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3584 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3585 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3586 (*envelope)->dmRecipientOrgUnitNum, 0);
3587 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3588 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3589 EXTRACT_STRING("isds:dmRecipientRefNumber",
3590 (*envelope)->dmRecipientRefNumber);
3591 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3592 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3593 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3595 /* Extract envelope elements regarding law reference */
3596 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3597 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3598 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3599 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3600 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3602 /* Extract envelope other elements */
3603 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3604 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3605 (*envelope)->dmAllowSubstDelivery);
3607 leave:
3608 if (err) isds_envelope_free(envelope);
3609 xmlXPathFreeObject(result);
3610 return err;
3615 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3616 * isds_envelope structure. The envelope is automatically allocated but not
3617 * reallocated. The date are just appended into envelope structure.
3618 * @context is ISDS context
3619 * @envelope is automatically allocated message envelope structure
3620 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3621 * In case of error @envelope will be freed. */
3622 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3623 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3624 isds_error err = IE_SUCCESS;
3625 xmlXPathObjectPtr result = NULL;
3627 if (!context) return IE_INVALID_CONTEXT;
3628 if (!envelope) return IE_INVAL;
3629 if (!xpath_ctx) return IE_INVAL;
3632 if (!*envelope) {
3633 /* Allocate envelope */
3634 *envelope = calloc(1, sizeof(**envelope));
3635 if (!*envelope) {
3636 err = IE_NOMEM;
3637 goto leave;
3639 } else {
3640 /* Else free former data */
3641 zfree((*envelope)->dmID);
3642 zfree((*envelope)->dbIDSender);
3643 zfree((*envelope)->dmSender);
3644 zfree((*envelope)->dmSenderAddress);
3645 zfree((*envelope)->dmSenderType);
3646 zfree((*envelope)->dmRecipient);
3647 zfree((*envelope)->dmRecipientAddress);
3648 zfree((*envelope)->dmAmbiguousRecipient);
3651 /* Extract envelope elements added by ISDS
3652 * (XSD: gMessageEnvelope type) */
3653 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3654 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3655 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3656 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3657 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3658 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3659 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3660 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3661 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3662 (*envelope)->dmAmbiguousRecipient);
3664 /* Extract envelope elements added by sender and ISDS
3665 * (XSD: gMessageEnvelope type) */
3666 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3667 if (err) goto leave;
3669 leave:
3670 if (err) isds_envelope_free(envelope);
3671 xmlXPathFreeObject(result);
3672 return err;
3676 /* Convert other envelope elements from XML tree into isds_envelope structure:
3677 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3678 * The envelope is automatically allocated but not reallocated.
3679 * The data are just appended into envelope structure.
3680 * @context is ISDS context
3681 * @envelope is automatically allocated message envelope structure
3682 * @xpath_ctx is XPath context with current node as parent desired elements
3683 * In case of error @envelope will be freed. */
3684 static isds_error append_status_size_times(struct isds_ctx *context,
3685 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3686 isds_error err = IE_SUCCESS;
3687 xmlXPathObjectPtr result = NULL;
3688 char *string = NULL;
3689 unsigned long int *unumber = NULL;
3691 if (!context) return IE_INVALID_CONTEXT;
3692 if (!envelope) return IE_INVAL;
3693 if (!xpath_ctx) return IE_INVAL;
3696 if (!*envelope) {
3697 /* Allocate new */
3698 *envelope = calloc(1, sizeof(**envelope));
3699 if (!*envelope) {
3700 err = IE_NOMEM;
3701 goto leave;
3703 } else {
3704 /* Free old data */
3705 zfree((*envelope)->dmMessageStatus);
3706 zfree((*envelope)->dmAttachmentSize);
3707 zfree((*envelope)->dmDeliveryTime);
3708 zfree((*envelope)->dmAcceptanceTime);
3712 /* dmMessageStatus element is mandatory */
3713 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3714 if (!unumber) {
3715 isds_log_message(context,
3716 _("Missing mandatory sisds:dmMessageStatus integer"));
3717 err = IE_ISDS;
3718 goto leave;
3720 err = uint2isds_message_status(context, unumber,
3721 &((*envelope)->dmMessageStatus));
3722 if (err) {
3723 if (err == IE_ENUM) err = IE_ISDS;
3724 goto leave;
3726 free(unumber); unumber = NULL;
3728 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3731 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3732 if (string) {
3733 err = timestring2timeval((xmlChar *) string,
3734 &((*envelope)->dmDeliveryTime));
3735 if (err) {
3736 char *string_locale = _isds_utf82locale(string);
3737 if (err == IE_DATE) err = IE_ISDS;
3738 isds_printf_message(context,
3739 _("Could not convert dmDeliveryTime as ISO time: %s"),
3740 string_locale);
3741 free(string_locale);
3742 goto leave;
3744 zfree(string);
3747 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3748 if (string) {
3749 err = timestring2timeval((xmlChar *) string,
3750 &((*envelope)->dmAcceptanceTime));
3751 if (err) {
3752 char *string_locale = _isds_utf82locale(string);
3753 if (err == IE_DATE) err = IE_ISDS;
3754 isds_printf_message(context,
3755 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3756 string_locale);
3757 free(string_locale);
3758 goto leave;
3760 zfree(string);
3763 leave:
3764 if (err) isds_envelope_free(envelope);
3765 free(unumber);
3766 free(string);
3767 xmlXPathFreeObject(result);
3768 return err;
3772 /* Convert message type attribute of current element into isds_envelope
3773 * structure.
3774 * TODO: This function can be incorporated into append_status_size_times() as
3775 * they are called always together.
3776 * The envelope is automatically allocated but not reallocated.
3777 * The data are just appended into envelope structure.
3778 * @context is ISDS context
3779 * @envelope is automatically allocated message envelope structure
3780 * @xpath_ctx is XPath context with current node as parent of attribute
3781 * carrying message type
3782 * In case of error @envelope will be freed. */
3783 static isds_error append_message_type(struct isds_ctx *context,
3784 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3785 isds_error err = IE_SUCCESS;
3787 if (!context) return IE_INVALID_CONTEXT;
3788 if (!envelope) return IE_INVAL;
3789 if (!xpath_ctx) return IE_INVAL;
3792 if (!*envelope) {
3793 /* Allocate new */
3794 *envelope = calloc(1, sizeof(**envelope));
3795 if (!*envelope) {
3796 err = IE_NOMEM;
3797 goto leave;
3799 } else {
3800 /* Free old data */
3801 zfree((*envelope)->dmType);
3805 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3807 if (!(*envelope)->dmType) {
3808 /* Use default value */
3809 (*envelope)->dmType = strdup("V");
3810 if (!(*envelope)->dmType) {
3811 err = IE_NOMEM;
3812 goto leave;
3814 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3815 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3816 isds_printf_message(context,
3817 _("Message type in dmType attribute is not 1 character long: "
3818 "%s"),
3819 type_locale);
3820 free(type_locale);
3821 err = IE_ISDS;
3822 goto leave;
3825 leave:
3826 if (err) isds_envelope_free(envelope);
3827 return err;
3831 #if HAVE_LIBCURL
3832 /* Convert dmType isds_envelope member into XML attribute and append it to
3833 * current node.
3834 * @context is ISDS context
3835 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3836 * @dm_envelope is XML element the resulting attribute will be appended to.
3837 * @return error code, in case of error context' message is filled. */
3838 static isds_error insert_message_type(struct isds_ctx *context,
3839 const char *type, xmlNodePtr dm_envelope) {
3840 isds_error err = IE_SUCCESS;
3841 xmlAttrPtr attribute_node;
3843 if (!context) return IE_INVALID_CONTEXT;
3844 if (!dm_envelope) return IE_INVAL;
3846 /* Insert optional message type */
3847 if (type) {
3848 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3849 char *type_locale = _isds_utf82locale(type);
3850 isds_printf_message(context,
3851 _("Message type in envelope is not 1 character long: %s"),
3852 type_locale);
3853 free(type_locale);
3854 err = IE_INVAL;
3855 goto leave;
3857 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3860 leave:
3861 return err;
3863 #endif /* HAVE_LIBCURL */
3866 /* Extract message document into reallocated document structure
3867 * @context is ISDS context
3868 * @document is automatically reallocated message documents structure
3869 * @xpath_ctx is XPath context with current node as isds:dmFile
3870 * In case of error @document will be freed. */
3871 static isds_error extract_document(struct isds_ctx *context,
3872 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3873 isds_error err = IE_SUCCESS;
3874 xmlXPathObjectPtr result = NULL;
3875 xmlNodePtr file_node;
3876 char *string = NULL;
3878 if (!context) return IE_INVALID_CONTEXT;
3879 if (!document) return IE_INVAL;
3880 isds_document_free(document);
3881 if (!xpath_ctx) return IE_INVAL;
3882 file_node = xpath_ctx->node;
3884 *document = calloc(1, sizeof(**document));
3885 if (!*document) {
3886 err = IE_NOMEM;
3887 goto leave;
3890 /* Extract document meta data */
3891 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3892 if (context->normalize_mime_type) {
3893 const char *normalized_type =
3894 isds_normalize_mime_type((*document)->dmMimeType);
3895 if (NULL != normalized_type &&
3896 normalized_type != (*document)->dmMimeType) {
3897 char *new_type = strdup(normalized_type);
3898 if (NULL == new_type) {
3899 isds_printf_message(context,
3900 _("Not enough memory to normalize document MIME type"));
3901 err = IE_NOMEM;
3902 goto leave;
3904 free((*document)->dmMimeType);
3905 (*document)->dmMimeType = new_type;
3909 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3910 err = string2isds_FileMetaType((xmlChar*)string,
3911 &((*document)->dmFileMetaType));
3912 if (err) {
3913 char *meta_type_locale = _isds_utf82locale(string);
3914 isds_printf_message(context,
3915 _("Document has invalid dmFileMetaType attribute value: %s"),
3916 meta_type_locale);
3917 free(meta_type_locale);
3918 err = IE_ISDS;
3919 goto leave;
3921 zfree(string);
3923 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3924 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3925 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3926 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3929 /* Extract document data.
3930 * Base64 encoded blob or XML subtree must be presented. */
3932 /* Check for dmEncodedContent */
3933 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3934 xpath_ctx);
3935 if (!result) {
3936 err = IE_XML;
3937 goto leave;
3940 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3941 /* Here we have Base64 blob */
3942 (*document)->is_xml = 0;
3944 if (result->nodesetval->nodeNr > 1) {
3945 isds_printf_message(context,
3946 _("Document has more dmEncodedContent elements"));
3947 err = IE_ISDS;
3948 goto leave;
3951 xmlXPathFreeObject(result); result = NULL;
3952 EXTRACT_STRING("isds:dmEncodedContent", string);
3954 /* Decode non-empty document */
3955 if (string && string[0] != '\0') {
3956 (*document)->data_length =
3957 _isds_b64decode(string, &((*document)->data));
3958 if ((*document)->data_length == (size_t) -1) {
3959 isds_printf_message(context,
3960 _("Error while Base64-decoding document content"));
3961 err = IE_ERROR;
3962 goto leave;
3965 } else {
3966 /* No Base64 blob, try XML document */
3967 xmlXPathFreeObject(result); result = NULL;
3968 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3969 xpath_ctx);
3970 if (!result) {
3971 err = IE_XML;
3972 goto leave;
3975 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3976 /* Here we have XML document */
3977 (*document)->is_xml = 1;
3979 if (result->nodesetval->nodeNr > 1) {
3980 isds_printf_message(context,
3981 _("Document has more dmXMLContent elements"));
3982 err = IE_ISDS;
3983 goto leave;
3986 /* XXX: We cannot serialize the content simply because:
3987 * - XML document may point out of its scope (e.g. to message
3988 * envelope)
3989 * - isds:dmXMLContent can contain more elements, no element,
3990 * a text node only
3991 * - it's not the XML way
3992 * Thus we provide the only right solution: XML DOM. Let's
3993 * application to cope with this hot potato :) */
3994 (*document)->xml_node_list =
3995 result->nodesetval->nodeTab[0]->children;
3996 } else {
3997 /* No base64 blob, nor XML document */
3998 isds_printf_message(context,
3999 _("Document has no dmEncodedContent, nor dmXMLContent "
4000 "element"));
4001 err = IE_ISDS;
4002 goto leave;
4007 leave:
4008 if (err) isds_document_free(document);
4009 free(string);
4010 xmlXPathFreeObject(result);
4011 xpath_ctx->node = file_node;
4012 return err;
4017 /* Extract message documents into reallocated list of documents
4018 * @context is ISDS context
4019 * @documents is automatically reallocated message documents list structure
4020 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4021 * In case of error @documents will be freed. */
4022 static isds_error extract_documents(struct isds_ctx *context,
4023 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
4024 isds_error err = IE_SUCCESS;
4025 xmlXPathObjectPtr result = NULL;
4026 xmlNodePtr files_node;
4027 struct isds_list *document, *prev_document = NULL;
4029 if (!context) return IE_INVALID_CONTEXT;
4030 if (!documents) return IE_INVAL;
4031 isds_list_free(documents);
4032 if (!xpath_ctx) return IE_INVAL;
4033 files_node = xpath_ctx->node;
4035 /* Find documents */
4036 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
4037 if (!result) {
4038 err = IE_XML;
4039 goto leave;
4042 /* No match */
4043 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4044 isds_printf_message(context,
4045 _("Message does not contain any document"));
4046 err = IE_ISDS;
4047 goto leave;
4051 /* Iterate over documents */
4052 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4054 /* Allocate and append list item */
4055 document = calloc(1, sizeof(*document));
4056 if (!document) {
4057 err = IE_NOMEM;
4058 goto leave;
4060 document->destructor = (void (*)(void **))isds_document_free;
4061 if (i == 0) *documents = document;
4062 else prev_document->next = document;
4063 prev_document = document;
4065 /* Extract document */
4066 xpath_ctx->node = result->nodesetval->nodeTab[i];
4067 err = extract_document(context,
4068 (struct isds_document **) &(document->data), xpath_ctx);
4069 if (err) goto leave;
4073 leave:
4074 if (err) isds_list_free(documents);
4075 xmlXPathFreeObject(result);
4076 xpath_ctx->node = files_node;
4077 return err;
4081 #if HAVE_LIBCURL
4082 /* Convert isds:dmRecord XML tree into structure
4083 * @context is ISDS context
4084 * @envelope is automatically reallocated message envelope structure
4085 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4086 * In case of error @envelope will be freed. */
4087 static isds_error extract_DmRecord(struct isds_ctx *context,
4088 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4089 isds_error err = IE_SUCCESS;
4090 xmlXPathObjectPtr result = NULL;
4092 if (!context) return IE_INVALID_CONTEXT;
4093 if (!envelope) return IE_INVAL;
4094 isds_envelope_free(envelope);
4095 if (!xpath_ctx) return IE_INVAL;
4098 *envelope = calloc(1, sizeof(**envelope));
4099 if (!*envelope) {
4100 err = IE_NOMEM;
4101 goto leave;
4105 /* Extract tRecord data */
4106 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4108 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4109 * dmAcceptanceTime. */
4110 err = append_status_size_times(context, envelope, xpath_ctx);
4111 if (err) goto leave;
4113 /* Extract envelope elements added by sender and ISDS
4114 * (XSD: gMessageEnvelope type) */
4115 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4116 if (err) goto leave;
4118 /* Get message type */
4119 err = append_message_type(context, envelope, xpath_ctx);
4120 if (err) goto leave;
4123 leave:
4124 if (err) isds_envelope_free(envelope);
4125 xmlXPathFreeObject(result);
4126 return err;
4130 /* Convert XSD:tStateChangesRecord type XML tree into structure
4131 * @context is ISDS context
4132 * @changed_status is automatically reallocated message state change structure
4133 * @xpath_ctx is XPath context with current node as element of
4134 * XSD:tStateChangesRecord type
4135 * In case of error @changed_status will be freed. */
4136 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4137 struct isds_message_status_change **changed_status,
4138 xmlXPathContextPtr xpath_ctx) {
4139 isds_error err = IE_SUCCESS;
4140 xmlXPathObjectPtr result = NULL;
4141 unsigned long int *unumber = NULL;
4142 char *string = NULL;
4144 if (!context) return IE_INVALID_CONTEXT;
4145 if (!changed_status) return IE_INVAL;
4146 isds_message_status_change_free(changed_status);
4147 if (!xpath_ctx) return IE_INVAL;
4150 *changed_status = calloc(1, sizeof(**changed_status));
4151 if (!*changed_status) {
4152 err = IE_NOMEM;
4153 goto leave;
4157 /* Extract tGetStateChangesInput data */
4158 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4160 /* dmEventTime is mandatory */
4161 EXTRACT_STRING("isds:dmEventTime", string);
4162 if (string) {
4163 err = timestring2timeval((xmlChar *) string,
4164 &((*changed_status)->time));
4165 if (err) {
4166 char *string_locale = _isds_utf82locale(string);
4167 if (err == IE_DATE) err = IE_ISDS;
4168 isds_printf_message(context,
4169 _("Could not convert dmEventTime as ISO time: %s"),
4170 string_locale);
4171 free(string_locale);
4172 goto leave;
4174 zfree(string);
4177 /* dmMessageStatus element is mandatory */
4178 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4179 if (!unumber) {
4180 isds_log_message(context,
4181 _("Missing mandatory isds:dmMessageStatus integer"));
4182 err = IE_ISDS;
4183 goto leave;
4185 err = uint2isds_message_status(context, unumber,
4186 &((*changed_status)->dmMessageStatus));
4187 if (err) {
4188 if (err == IE_ENUM) err = IE_ISDS;
4189 goto leave;
4191 zfree(unumber);
4194 leave:
4195 free(unumber);
4196 free(string);
4197 if (err) isds_message_status_change_free(changed_status);
4198 xmlXPathFreeObject(result);
4199 return err;
4201 #endif /* HAVE_LIBCURL */
4204 /* Find and convert isds:dmHash XML tree into structure
4205 * @context is ISDS context
4206 * @envelope is automatically reallocated message hash structure
4207 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4208 * In case of error @hash will be freed. */
4209 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4210 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4211 isds_error err = IE_SUCCESS;
4212 xmlNodePtr old_ctx_node;
4213 xmlXPathObjectPtr result = NULL;
4214 char *string = NULL;
4216 if (!context) return IE_INVALID_CONTEXT;
4217 if (!hash) return IE_INVAL;
4218 isds_hash_free(hash);
4219 if (!xpath_ctx) return IE_INVAL;
4221 old_ctx_node = xpath_ctx->node;
4223 *hash = calloc(1, sizeof(**hash));
4224 if (!*hash) {
4225 err = IE_NOMEM;
4226 goto leave;
4229 /* Locate dmHash */
4230 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4231 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4232 err = IE_ISDS;
4233 goto leave;
4235 if (err) {
4236 err = IE_ERROR;
4237 goto leave;
4240 /* Get hash algorithm */
4241 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4242 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4243 if (err) {
4244 if (err == IE_ENUM) {
4245 char *string_locale = _isds_utf82locale(string);
4246 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4247 string_locale);
4248 free(string_locale);
4250 goto leave;
4252 zfree(string);
4254 /* Get hash value */
4255 EXTRACT_STRING(".", string);
4256 if (!string) {
4257 isds_printf_message(context,
4258 _("sisds:dmHash element is missing hash value"));
4259 err = IE_ISDS;
4260 goto leave;
4262 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4263 if ((*hash)->length == (size_t) -1) {
4264 isds_printf_message(context,
4265 _("Error while Base64-decoding hash value"));
4266 err = IE_ERROR;
4267 goto leave;
4270 leave:
4271 if (err) isds_hash_free(hash);
4272 free(string);
4273 xmlXPathFreeObject(result);
4274 xpath_ctx->node = old_ctx_node;
4275 return err;
4279 /* Find and append isds:dmQTimestamp XML tree into envelope.
4280 * Because one service is allowed to miss time-stamp content, and we think
4281 * other could too (flaw in specification), this function is deliberated and
4282 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4283 * @context is ISDS context
4284 * @envelope is automatically allocated envelope structure
4285 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4286 * child
4287 * In case of error @envelope will be freed. */
4288 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4289 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4290 isds_error err = IE_SUCCESS;
4291 xmlXPathObjectPtr result = NULL;
4292 char *string = NULL;
4294 if (!context) return IE_INVALID_CONTEXT;
4295 if (!envelope) return IE_INVAL;
4296 if (!xpath_ctx) {
4297 isds_envelope_free(envelope);
4298 return IE_INVAL;
4301 if (!*envelope) {
4302 *envelope = calloc(1, sizeof(**envelope));
4303 if (!*envelope) {
4304 err = IE_NOMEM;
4305 goto leave;
4307 } else {
4308 zfree((*envelope)->timestamp);
4309 (*envelope)->timestamp_length = 0;
4312 /* Get dmQTimestamp */
4313 EXTRACT_STRING("sisds:dmQTimestamp", string);
4314 if (!string) {
4315 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4316 goto leave;
4318 (*envelope)->timestamp_length =
4319 _isds_b64decode(string, &((*envelope)->timestamp));
4320 if ((*envelope)->timestamp_length == (size_t) -1) {
4321 isds_printf_message(context,
4322 _("Error while Base64-decoding time stamp value"));
4323 err = IE_ERROR;
4324 goto leave;
4327 leave:
4328 if (err) isds_envelope_free(envelope);
4329 free(string);
4330 xmlXPathFreeObject(result);
4331 return err;
4335 /* Convert XSD tReturnedMessage XML tree into message structure.
4336 * It does not store serialized XML tree into message->raw.
4337 * It does store (pointer to) parsed XML tree into message->xml if needed.
4338 * @context is ISDS context
4339 * @include_documents Use true if documents must be extracted
4340 * (tReturnedMessage XSD type), use false if documents shall be omitted
4341 * (tReturnedMessageEnvelope).
4342 * @message is automatically reallocated message structure
4343 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4344 * type
4345 * In case of error @message will be freed. */
4346 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4347 const _Bool include_documents, struct isds_message **message,
4348 xmlXPathContextPtr xpath_ctx) {
4349 isds_error err = IE_SUCCESS;
4350 xmlNodePtr message_node;
4352 if (!context) return IE_INVALID_CONTEXT;
4353 if (!message) return IE_INVAL;
4354 isds_message_free(message);
4355 if (!xpath_ctx) return IE_INVAL;
4358 *message = calloc(1, sizeof(**message));
4359 if (!*message) {
4360 err = IE_NOMEM;
4361 goto leave;
4364 /* Save message XPATH context node */
4365 message_node = xpath_ctx->node;
4368 /* Extract dmDM */
4369 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4370 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4371 if (err) { err = IE_ERROR; goto leave; }
4372 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4373 if (err) goto leave;
4375 if (include_documents) {
4376 struct isds_list *item;
4378 /* Extract dmFiles */
4379 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4380 xpath_ctx);
4381 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4382 err = IE_ISDS; goto leave;
4384 if (err) { err = IE_ERROR; goto leave; }
4385 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4386 if (err) goto leave;
4388 /* Store xmlDoc of this message if needed */
4389 /* Only if we got a XML document in all the documents. */
4390 for (item = (*message)->documents; item; item = item->next) {
4391 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4392 (*message)->xml = xpath_ctx->doc;
4393 break;
4399 /* Restore context to message */
4400 xpath_ctx->node = message_node;
4402 /* Extract dmHash */
4403 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4404 xpath_ctx);
4405 if (err) goto leave;
4407 /* Extract dmQTimestamp, */
4408 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4409 xpath_ctx);
4410 if (err) goto leave;
4412 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4413 * dmAcceptanceTime. */
4414 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4415 if (err) goto leave;
4417 /* Get message type */
4418 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4419 if (err) goto leave;
4421 leave:
4422 if (err) isds_message_free(message);
4423 return err;
4427 /* Extract message event into reallocated isds_event structure
4428 * @context is ISDS context
4429 * @event is automatically reallocated message event structure
4430 * @xpath_ctx is XPath context with current node as isds:dmEvent
4431 * In case of error @event will be freed. */
4432 static isds_error extract_event(struct isds_ctx *context,
4433 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4434 isds_error err = IE_SUCCESS;
4435 xmlXPathObjectPtr result = NULL;
4436 xmlNodePtr event_node;
4437 char *string = NULL;
4439 if (!context) return IE_INVALID_CONTEXT;
4440 if (!event) return IE_INVAL;
4441 isds_event_free(event);
4442 if (!xpath_ctx) return IE_INVAL;
4443 event_node = xpath_ctx->node;
4445 *event = calloc(1, sizeof(**event));
4446 if (!*event) {
4447 err = IE_NOMEM;
4448 goto leave;
4451 /* Extract event data.
4452 * All elements are optional according XSD. That's funny. */
4453 EXTRACT_STRING("sisds:dmEventTime", string);
4454 if (string) {
4455 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4456 if (err) {
4457 char *string_locale = _isds_utf82locale(string);
4458 if (err == IE_DATE) err = IE_ISDS;
4459 isds_printf_message(context,
4460 _("Could not convert dmEventTime as ISO time: %s"),
4461 string_locale);
4462 free(string_locale);
4463 goto leave;
4465 zfree(string);
4468 /* dmEventDescr element has prefix and the rest */
4469 EXTRACT_STRING("sisds:dmEventDescr", string);
4470 if (string) {
4471 err = eventstring2event((xmlChar *) string, *event);
4472 if (err) goto leave;
4473 zfree(string);
4476 leave:
4477 if (err) isds_event_free(event);
4478 free(string);
4479 xmlXPathFreeObject(result);
4480 xpath_ctx->node = event_node;
4481 return err;
4485 /* Convert element of XSD tEventsArray type from XML tree into
4486 * isds_list of isds_event's structure. The list is automatically reallocated.
4487 * @context is ISDS context
4488 * @events is automatically reallocated list of event structures
4489 * @xpath_ctx is XPath context with current node as tEventsArray
4490 * In case of error @events will be freed. */
4491 static isds_error extract_events(struct isds_ctx *context,
4492 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4493 isds_error err = IE_SUCCESS;
4494 xmlXPathObjectPtr result = NULL;
4495 xmlNodePtr events_node;
4496 struct isds_list *event, *prev_event = NULL;
4498 if (!context) return IE_INVALID_CONTEXT;
4499 if (!events) return IE_INVAL;
4500 if (!xpath_ctx) return IE_INVAL;
4501 events_node = xpath_ctx->node;
4503 /* Free old list */
4504 isds_list_free(events);
4506 /* Find events */
4507 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4508 if (!result) {
4509 err = IE_XML;
4510 goto leave;
4513 /* No match */
4514 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4515 isds_printf_message(context,
4516 _("Delivery info does not contain any event"));
4517 err = IE_ISDS;
4518 goto leave;
4522 /* Iterate over events */
4523 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4525 /* Allocate and append list item */
4526 event = calloc(1, sizeof(*event));
4527 if (!event) {
4528 err = IE_NOMEM;
4529 goto leave;
4531 event->destructor = (void (*)(void **))isds_event_free;
4532 if (i == 0) *events = event;
4533 else prev_event->next = event;
4534 prev_event = event;
4536 /* Extract event */
4537 xpath_ctx->node = result->nodesetval->nodeTab[i];
4538 err = extract_event(context,
4539 (struct isds_event **) &(event->data), xpath_ctx);
4540 if (err) goto leave;
4544 leave:
4545 if (err) isds_list_free(events);
4546 xmlXPathFreeObject(result);
4547 xpath_ctx->node = events_node;
4548 return err;
4552 #if HAVE_LIBCURL
4553 /* Insert Base64 encoded data as element with text child.
4554 * @context is session context
4555 * @parent is XML node to append @element with @data as child
4556 * @ns is XML namespace of @element, use NULL to inherit from @parent
4557 * @element is UTF-8 encoded name of new element
4558 * @data is bit stream to encode into @element
4559 * @length is size of @data in bytes
4560 * @return standard error code and fill long error message if needed */
4561 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4562 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4563 const void *data, size_t length) {
4564 isds_error err = IE_SUCCESS;
4565 xmlNodePtr node;
4567 if (!context) return IE_INVALID_CONTEXT;
4568 if (!data && length > 0) return IE_INVAL;
4569 if (!parent || !element) return IE_INVAL;
4571 xmlChar *base64data = NULL;
4572 base64data = (xmlChar *) _isds_b64encode(data, length);
4573 if (!base64data) {
4574 isds_printf_message(context,
4575 ngettext("Not enough memory to encode %zd byte into Base64",
4576 "Not enough memory to encode %zd bytes into Base64",
4577 length),
4578 length);
4579 err = IE_NOMEM;
4580 goto leave;
4582 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4584 leave:
4585 free(base64data);
4586 return err;
4590 /* Convert isds_document structure into XML tree and append to dmFiles node.
4591 * @context is session context
4592 * @document is ISDS document
4593 * @dm_files is XML element the resulting tree will be appended to as a child.
4594 * @return error code, in case of error context' message is filled. */
4595 static isds_error insert_document(struct isds_ctx *context,
4596 struct isds_document *document, xmlNodePtr dm_files) {
4597 isds_error err = IE_SUCCESS;
4598 xmlNodePtr new_file = NULL, file = NULL, node;
4599 xmlAttrPtr attribute_node;
4601 if (!context) return IE_INVALID_CONTEXT;
4602 if (!document || !dm_files) return IE_INVAL;
4604 /* Allocate new dmFile */
4605 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4606 if (!new_file) {
4607 isds_printf_message(context, _("Could not allocate main dmFile"));
4608 err = IE_ERROR;
4609 goto leave;
4611 /* Append the new dmFile.
4612 * XXX: Main document must go first */
4613 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4614 file = xmlAddPrevSibling(dm_files->children, new_file);
4615 else
4616 file = xmlAddChild(dm_files, new_file);
4618 if (!file) {
4619 xmlFreeNode(new_file); new_file = NULL;
4620 isds_printf_message(context, _("Could not add dmFile child to "
4621 "%s element"), dm_files->name);
4622 err = IE_ERROR;
4623 goto leave;
4626 /* @dmMimeType is required */
4627 if (!document->dmMimeType) {
4628 isds_log_message(context,
4629 _("Document is missing mandatory MIME type definition"));
4630 err = IE_INVAL;
4631 goto leave;
4633 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4635 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4636 if (!string) {
4637 isds_printf_message(context,
4638 _("Document has unknown dmFileMetaType: %ld"),
4639 document->dmFileMetaType);
4640 err = IE_ENUM;
4641 goto leave;
4643 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4645 if (document->dmFileGuid) {
4646 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4648 if (document->dmUpFileGuid) {
4649 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4652 /* @dmFileDescr is required */
4653 if (!document->dmFileDescr) {
4654 isds_log_message(context,
4655 _("Document is missing mandatory description (title)"));
4656 err = IE_INVAL;
4657 goto leave;
4659 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4661 if (document->dmFormat) {
4662 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4666 /* Insert content (body) of the document. */
4667 if (document->is_xml) {
4668 /* XML document requested */
4670 /* Allocate new dmXMLContent */
4671 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4672 if (!xmlcontent) {
4673 isds_printf_message(context,
4674 _("Could not allocate dmXMLContent element"));
4675 err = IE_ERROR;
4676 goto leave;
4678 /* Append it */
4679 node = xmlAddChild(file, xmlcontent);
4680 if (!node) {
4681 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4682 isds_printf_message(context,
4683 _("Could not add dmXMLContent child to %s element"),
4684 file->name);
4685 err = IE_ERROR;
4686 goto leave;
4689 /* Copy non-empty node list */
4690 if (document->xml_node_list) {
4691 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4692 document->xml_node_list);
4693 if (!content) {
4694 isds_printf_message(context,
4695 _("Not enough memory to copy XML document"));
4696 err = IE_NOMEM;
4697 goto leave;
4700 if (!xmlAddChildList(node, content)) {
4701 xmlFreeNodeList(content);
4702 isds_printf_message(context,
4703 _("Error while adding XML document into dmXMLContent"));
4704 err = IE_XML;
4705 goto leave;
4707 /* XXX: We cannot free the content here because it's part of node's
4708 * document since now. It will be freed with it automatically. */
4710 } else {
4711 /* Binary document requested */
4712 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4713 document->data, document->data_length);
4714 if (err) goto leave;
4717 leave:
4718 return err;
4722 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4723 * The copy must be preallocated, the date are just appended into structure.
4724 * @context is ISDS context
4725 * @copy is message copy structure
4726 * @xpath_ctx is XPath context with current node as tMStatus */
4727 static isds_error append_TMStatus(struct isds_ctx *context,
4728 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4729 isds_error err = IE_SUCCESS;
4730 xmlXPathObjectPtr result = NULL;
4731 char *code = NULL, *message = NULL;
4733 if (!context) return IE_INVALID_CONTEXT;
4734 if (!copy || !xpath_ctx) return IE_INVAL;
4736 /* Free old values */
4737 zfree(copy->dmStatus);
4738 zfree(copy->dmID);
4740 /* Get error specific to this copy */
4741 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4742 if (!code) {
4743 isds_log_message(context,
4744 _("Missing isds:dmStatusCode under "
4745 "XSD:tMStatus type element"));
4746 err = IE_ISDS;
4747 goto leave;
4750 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4751 /* This copy failed */
4752 copy->error = IE_ISDS;
4753 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4754 if (message) {
4755 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4756 if (!copy->dmStatus) {
4757 copy->dmStatus = code;
4758 code = NULL;
4760 } else {
4761 copy->dmStatus = code;
4762 code = NULL;
4764 } else {
4765 /* This copy succeeded. In this case only, message ID is valid */
4766 copy->error = IE_SUCCESS;
4768 EXTRACT_STRING("isds:dmID", copy->dmID);
4769 if (!copy->dmID) {
4770 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4771 "but did not returned assigned message ID\n"));
4772 err = IE_ISDS;
4776 leave:
4777 free(code);
4778 free(message);
4779 xmlXPathFreeObject(result);
4780 return err;
4784 /* Insert struct isds_approval data (box approval) into XML tree
4785 * @context is session context
4786 * @approval is libisds structure with approval description. NULL is
4787 * acceptable.
4788 * @parent is XML element to append @approval to */
4789 static isds_error insert_GExtApproval(struct isds_ctx *context,
4790 const struct isds_approval *approval, xmlNodePtr parent) {
4792 isds_error err = IE_SUCCESS;
4793 xmlNodePtr node;
4795 if (!context) return IE_INVALID_CONTEXT;
4796 if (!parent) return IE_INVAL;
4798 if (!approval) return IE_SUCCESS;
4800 /* Build XSD:gExtApproval */
4801 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4802 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4804 leave:
4805 return err;
4809 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4810 * code
4811 * @context is session context
4812 * @service_name is name of SERVICE_DB_ACCESS
4813 * @response is reallocated server SOAP body response as XML document
4814 * @raw_response is reallocated bit stream with response body. Use
4815 * NULL if you don't care
4816 * @raw_response_length is size of @raw_response in bytes
4817 * @code is reallocated ISDS status code
4818 * @status_message is reallocated ISDS status message
4819 * @return error coded from lower layer, context message will be set up
4820 * appropriately. */
4821 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4822 const xmlChar *service_name,
4823 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4824 xmlChar **code, xmlChar **status_message) {
4826 isds_error err = IE_SUCCESS;
4827 char *service_name_locale = NULL;
4828 xmlNodePtr request = NULL, node;
4829 xmlNsPtr isds_ns = NULL;
4831 if (!context) return IE_INVALID_CONTEXT;
4832 if (!service_name) return IE_INVAL;
4833 if (!response || !code || !status_message) return IE_INVAL;
4834 if (!raw_response_length && raw_response) return IE_INVAL;
4836 /* Free output argument */
4837 xmlFreeDoc(*response); *response = NULL;
4838 if (raw_response) zfree(*raw_response);
4839 zfree(*code);
4840 zfree(*status_message);
4843 /* Check if connection is established
4844 * TODO: This check should be done downstairs. */
4845 if (!context->curl) return IE_CONNECTION_CLOSED;
4847 service_name_locale = _isds_utf82locale((char*)service_name);
4848 if (!service_name_locale) {
4849 err = IE_NOMEM;
4850 goto leave;
4853 /* Build request */
4854 request = xmlNewNode(NULL, service_name);
4855 if (!request) {
4856 isds_printf_message(context,
4857 _("Could not build %s request"), service_name_locale);
4858 err = IE_ERROR;
4859 goto leave;
4861 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4862 if(!isds_ns) {
4863 isds_log_message(context, _("Could not create ISDS name space"));
4864 err = IE_ERROR;
4865 goto leave;
4867 xmlSetNs(request, isds_ns);
4870 /* Add XSD:tDummyInput child */
4871 INSERT_STRING(request, "dbDummy", NULL);
4874 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4875 service_name_locale);
4877 /* Send request */
4878 err = _isds(context, SERVICE_DB_ACCESS, request, response,
4879 raw_response, raw_response_length);
4880 xmlFreeNode(request); request = NULL;
4882 if (err) {
4883 isds_log(ILF_ISDS, ILL_DEBUG,
4884 _("Processing ISDS response on %s request failed\n"),
4885 service_name_locale);
4886 goto leave;
4889 /* Check for response status */
4890 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4891 code, status_message, NULL);
4892 if (err) {
4893 isds_log(ILF_ISDS, ILL_DEBUG,
4894 _("ISDS response on %s request is missing status\n"),
4895 service_name_locale);
4896 goto leave;
4899 /* Request processed, but nothing found */
4900 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4901 char *code_locale = _isds_utf82locale((char*) *code);
4902 char *status_message_locale =
4903 _isds_utf82locale((char*) *status_message);
4904 isds_log(ILF_ISDS, ILL_DEBUG,
4905 _("Server refused %s request (code=%s, message=%s)\n"),
4906 service_name_locale, code_locale, status_message_locale);
4907 isds_log_message(context, status_message_locale);
4908 free(code_locale);
4909 free(status_message_locale);
4910 err = IE_ISDS;
4911 goto leave;
4914 leave:
4915 free(service_name_locale);
4916 xmlFreeNode(request);
4917 return err;
4919 #endif
4922 /* Get data about logged in user and his box. */
4923 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4924 struct isds_DbOwnerInfo **db_owner_info) {
4925 isds_error err = IE_SUCCESS;
4926 #if HAVE_LIBCURL
4927 xmlDocPtr response = NULL;
4928 xmlChar *code = NULL, *message = NULL;
4929 xmlXPathContextPtr xpath_ctx = NULL;
4930 xmlXPathObjectPtr result = NULL;
4931 char *string = NULL;
4932 #endif
4934 if (!context) return IE_INVALID_CONTEXT;
4935 zfree(context->long_message);
4936 if (!db_owner_info) return IE_INVAL;
4937 isds_DbOwnerInfo_free(db_owner_info);
4939 #if HAVE_LIBCURL
4940 /* Check if connection is established */
4941 if (!context->curl) return IE_CONNECTION_CLOSED;
4944 /* Do request and check for success */
4945 err = build_send_check_dbdummy_request(context,
4946 BAD_CAST "GetOwnerInfoFromLogin",
4947 &response, NULL, NULL, &code, &message);
4948 if (err) goto leave;
4951 /* Extract data */
4952 /* Prepare structure */
4953 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4954 if (!*db_owner_info) {
4955 err = IE_NOMEM;
4956 goto leave;
4958 xpath_ctx = xmlXPathNewContext(response);
4959 if (!xpath_ctx) {
4960 err = IE_ERROR;
4961 goto leave;
4963 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4964 err = IE_ERROR;
4965 goto leave;
4968 /* Set context node */
4969 result = xmlXPathEvalExpression(BAD_CAST
4970 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4971 if (!result) {
4972 err = IE_ERROR;
4973 goto leave;
4975 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4976 isds_log_message(context, _("Missing dbOwnerInfo element"));
4977 err = IE_ISDS;
4978 goto leave;
4980 if (result->nodesetval->nodeNr > 1) {
4981 isds_log_message(context, _("Multiple dbOwnerInfo element"));
4982 err = IE_ISDS;
4983 goto leave;
4985 xpath_ctx->node = result->nodesetval->nodeTab[0];
4986 xmlXPathFreeObject(result); result = NULL;
4988 /* Extract it */
4989 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
4992 leave:
4993 if (err) {
4994 isds_DbOwnerInfo_free(db_owner_info);
4997 free(string);
4998 xmlXPathFreeObject(result);
4999 xmlXPathFreeContext(xpath_ctx);
5001 free(code);
5002 free(message);
5003 xmlFreeDoc(response);
5005 if (!err)
5006 isds_log(ILF_ISDS, ILL_DEBUG,
5007 _("GetOwnerInfoFromLogin request processed by server "
5008 "successfully.\n"));
5009 #else /* not HAVE_LIBCURL */
5010 err = IE_NOTSUP;
5011 #endif
5013 return err;
5017 /* Get data about logged in user. */
5018 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
5019 struct isds_DbUserInfo **db_user_info) {
5020 isds_error err = IE_SUCCESS;
5021 #if HAVE_LIBCURL
5022 xmlDocPtr response = NULL;
5023 xmlChar *code = NULL, *message = NULL;
5024 xmlXPathContextPtr xpath_ctx = NULL;
5025 xmlXPathObjectPtr result = NULL;
5026 #endif
5028 if (!context) return IE_INVALID_CONTEXT;
5029 zfree(context->long_message);
5030 if (!db_user_info) return IE_INVAL;
5031 isds_DbUserInfo_free(db_user_info);
5033 #if HAVE_LIBCURL
5034 /* Check if connection is established */
5035 if (!context->curl) return IE_CONNECTION_CLOSED;
5038 /* Do request and check for success */
5039 err = build_send_check_dbdummy_request(context,
5040 BAD_CAST "GetUserInfoFromLogin",
5041 &response, NULL, NULL, &code, &message);
5042 if (err) goto leave;
5045 /* Extract data */
5046 /* Prepare structure */
5047 *db_user_info = calloc(1, sizeof(**db_user_info));
5048 if (!*db_user_info) {
5049 err = IE_NOMEM;
5050 goto leave;
5052 xpath_ctx = xmlXPathNewContext(response);
5053 if (!xpath_ctx) {
5054 err = IE_ERROR;
5055 goto leave;
5057 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5058 err = IE_ERROR;
5059 goto leave;
5062 /* Set context node */
5063 result = xmlXPathEvalExpression(BAD_CAST
5064 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
5065 if (!result) {
5066 err = IE_ERROR;
5067 goto leave;
5069 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5070 isds_log_message(context, _("Missing dbUserInfo element"));
5071 err = IE_ISDS;
5072 goto leave;
5074 if (result->nodesetval->nodeNr > 1) {
5075 isds_log_message(context, _("Multiple dbUserInfo element"));
5076 err = IE_ISDS;
5077 goto leave;
5079 xpath_ctx->node = result->nodesetval->nodeTab[0];
5080 xmlXPathFreeObject(result); result = NULL;
5082 /* Extract it */
5083 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5085 leave:
5086 if (err) {
5087 isds_DbUserInfo_free(db_user_info);
5090 xmlXPathFreeObject(result);
5091 xmlXPathFreeContext(xpath_ctx);
5093 free(code);
5094 free(message);
5095 xmlFreeDoc(response);
5097 if (!err)
5098 isds_log(ILF_ISDS, ILL_DEBUG,
5099 _("GetUserInfoFromLogin request processed by server "
5100 "successfully.\n"));
5101 #else /* not HAVE_LIBCURL */
5102 err = IE_NOTSUP;
5103 #endif
5105 return err;
5109 /* Get expiration time of current password
5110 * @context is session context
5111 * @expiration is automatically reallocated time when password expires. If
5112 * password expiration is disabled, NULL will be returned. In case of error
5113 * it will be nulled too. */
5114 isds_error isds_get_password_expiration(struct isds_ctx *context,
5115 struct timeval **expiration) {
5116 isds_error err = IE_SUCCESS;
5117 #if HAVE_LIBCURL
5118 xmlDocPtr response = NULL;
5119 xmlChar *code = NULL, *message = NULL;
5120 xmlXPathContextPtr xpath_ctx = NULL;
5121 xmlXPathObjectPtr result = NULL;
5122 char *string = NULL;
5123 #endif
5125 if (!context) return IE_INVALID_CONTEXT;
5126 zfree(context->long_message);
5127 if (!expiration) return IE_INVAL;
5128 zfree(*expiration);
5130 #if HAVE_LIBCURL
5131 /* Check if connection is established */
5132 if (!context->curl) return IE_CONNECTION_CLOSED;
5135 /* Do request and check for success */
5136 err = build_send_check_dbdummy_request(context,
5137 BAD_CAST "GetPasswordInfo",
5138 &response, NULL, NULL, &code, &message);
5139 if (err) goto leave;
5142 /* Extract data */
5143 xpath_ctx = xmlXPathNewContext(response);
5144 if (!xpath_ctx) {
5145 err = IE_ERROR;
5146 goto leave;
5148 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5149 err = IE_ERROR;
5150 goto leave;
5153 /* Set context node */
5154 result = xmlXPathEvalExpression(BAD_CAST
5155 "/isds:GetPasswordInfoResponse", xpath_ctx);
5156 if (!result) {
5157 err = IE_ERROR;
5158 goto leave;
5160 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5161 isds_log_message(context,
5162 _("Missing GetPasswordInfoResponse element"));
5163 err = IE_ISDS;
5164 goto leave;
5166 if (result->nodesetval->nodeNr > 1) {
5167 isds_log_message(context,
5168 _("Multiple GetPasswordInfoResponse element"));
5169 err = IE_ISDS;
5170 goto leave;
5172 xpath_ctx->node = result->nodesetval->nodeTab[0];
5173 xmlXPathFreeObject(result); result = NULL;
5175 /* Extract expiration date */
5176 EXTRACT_STRING("isds:pswExpDate", string);
5177 if (string) {
5178 /* And convert it if any returned. Otherwise expiration is disabled. */
5179 err = timestring2timeval((xmlChar *) string, expiration);
5180 if (err) {
5181 char *string_locale = _isds_utf82locale(string);
5182 if (err == IE_DATE) err = IE_ISDS;
5183 isds_printf_message(context,
5184 _("Could not convert pswExpDate as ISO time: %s"),
5185 string_locale);
5186 free(string_locale);
5187 goto leave;
5191 leave:
5192 if (err) {
5193 if (*expiration) {
5194 zfree(*expiration);
5198 free(string);
5199 xmlXPathFreeObject(result);
5200 xmlXPathFreeContext(xpath_ctx);
5202 free(code);
5203 free(message);
5204 xmlFreeDoc(response);
5206 if (!err)
5207 isds_log(ILF_ISDS, ILL_DEBUG,
5208 _("GetPasswordInfo request processed by server "
5209 "successfully.\n"));
5210 #else /* not HAVE_LIBCURL */
5211 err = IE_NOTSUP;
5212 #endif
5214 return err;
5218 #if HAVE_LIBCURL
5219 /* Request delivering new TOTP code from ISDS through side channel before
5220 * changing password.
5221 * @context is session context
5222 * @password is current password.
5223 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5224 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5225 * function for more details.
5226 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5227 * NULL, if you don't care.
5228 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5229 * error code. */
5230 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5231 const char *password, struct isds_otp *otp, char **refnumber) {
5232 isds_error err = IE_SUCCESS;
5233 char *saved_url = NULL; /* No copy */
5234 #if HAVE_CURL_REAUTHORIZATION_BUG
5235 CURL *saved_curl = NULL; /* No copy */
5236 #endif
5237 xmlNsPtr isds_ns = NULL;
5238 xmlNodePtr request = NULL;
5239 xmlDocPtr response = NULL;
5240 xmlChar *code = NULL, *message = NULL;
5241 const xmlChar *codes[] = {
5242 BAD_CAST "2300",
5243 BAD_CAST "2301",
5244 BAD_CAST "2302"
5246 const char *meanings[] = {
5247 N_("Unexpected error"),
5248 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5249 N_("One-time code could not been sent. Try later again.")
5251 const isds_otp_resolution resolutions[] = {
5252 OTP_RESOLUTION_UNKNOWN,
5253 OTP_RESOLUTION_TO_FAST,
5254 OTP_RESOLUTION_TOTP_NOT_SENT
5257 if (NULL == context) return IE_INVALID_CONTEXT;
5258 zfree(context->long_message);
5259 if (NULL == password) {
5260 isds_log_message(context,
5261 _("Second argument (password) of isds_change_password() "
5262 "is NULL"));
5263 return IE_INVAL;
5266 /* Check if connection is established
5267 * TODO: This check should be done downstairs. */
5268 if (!context->curl) return IE_CONNECTION_CLOSED;
5270 if (!context->otp) {
5271 isds_log_message(context, _("This function requires OTP-authenticated "
5272 "context"));
5273 return IE_INVALID_CONTEXT;
5275 if (NULL == otp) {
5276 isds_log_message(context, _("If one-time password authentication "
5277 "method is in use, requesting new OTP code requires "
5278 "one-time credentials argument either"));
5279 return IE_INVAL;
5281 if (otp->method != OTP_TIME) {
5282 isds_log_message(context, _("Requesting new time-based OTP code from "
5283 "server requires one-time password authentication "
5284 "method"));
5285 return IE_INVAL;
5287 if (otp->otp_code != NULL) {
5288 isds_log_message(context, _("Requesting new time-based OTP code from "
5289 "server requires undefined OTP code member in "
5290 "one-time credentials argument"));
5291 return IE_INVAL;
5295 /* Build request */
5296 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5297 if (!request) {
5298 isds_log_message(context, _("Could not build SendSMSCode request"));
5299 return IE_ERROR;
5301 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5302 if(!isds_ns) {
5303 isds_log_message(context, _("Could not create ISDS name space"));
5304 xmlFreeNode(request);
5305 return IE_ERROR;
5307 xmlSetNs(request, isds_ns);
5309 /* Change URL temporarily for sending this request only */
5311 char *new_url = NULL;
5312 if ((err = _isds_build_url_from_context(context,
5313 "%1$.*2$sasws/changePassword", &new_url))) {
5314 goto leave;
5316 saved_url = context->url;
5317 context->url = new_url;
5320 /* Store credentials for sending this request only */
5321 context->otp_credentials = otp;
5322 _isds_discard_credentials(context, 0);
5323 if ((err = _isds_store_credentials(context, context->saved_username,
5324 password, NULL))) {
5325 _isds_discard_credentials(context, 0);
5326 goto leave;
5328 #if HAVE_CURL_REAUTHORIZATION_BUG
5329 saved_curl = context->curl;
5330 context->curl = curl_easy_init();
5331 if (NULL == context->curl) {
5332 err = IE_ERROR;
5333 goto leave;
5335 if (context->timeout) {
5336 err = isds_set_timeout(context, context->timeout);
5337 if (err) goto leave;
5339 #endif
5341 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5343 /* Sent request */
5344 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5346 /* Remove temporal credentials */
5347 _isds_discard_credentials(context, 0);
5348 /* Detach pointer to OTP credentials from context */
5349 context->otp_credentials = NULL;
5350 /* Keep context->otp true to keep signaling this is OTP session */
5352 /* Destroy request */
5353 xmlFreeNode(request); request = NULL;
5355 if (err) {
5356 isds_log(ILF_ISDS, ILL_DEBUG,
5357 _("Processing ISDS response on SendSMSCode request failed\n"));
5358 goto leave;
5361 /* Check for response status */
5362 err = isds_response_status(context, SERVICE_ASWS, response,
5363 &code, &message, (xmlChar **)refnumber);
5364 if (err) {
5365 isds_log(ILF_ISDS, ILL_DEBUG,
5366 _("ISDS response on SendSMSCode request is missing "
5367 "status\n"));
5368 goto leave;
5371 /* Check for error */
5372 if (xmlStrcmp(code, BAD_CAST "0000")) {
5373 char *code_locale = _isds_utf82locale((char*)code);
5374 char *message_locale = _isds_utf82locale((char*)message);
5375 int i;
5376 isds_log(ILF_ISDS, ILL_DEBUG,
5377 _("Server refused to send new code on SendSMSCode "
5378 "request (code=%s, message=%s)\n"),
5379 code_locale, message_locale);
5381 /* Check for known error codes */
5382 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5383 if (!xmlStrcmp(code, codes[i])) break;
5385 if (i < sizeof(codes)/sizeof(*codes)) {
5386 isds_log_message(context, _(meanings[i]));
5387 /* Mimic otp->resolution according to the code, specification does
5388 * prescribe OTP header to be available. */
5389 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5390 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5391 otp->resolution = resolutions[i];
5392 } else
5393 isds_log_message(context, message_locale);
5395 free(code_locale);
5396 free(message_locale);
5398 err = IE_ISDS;
5399 goto leave;
5402 /* Otherwise new code sent successfully */
5403 /* Mimic otp->resolution according to the code, specification does
5404 * prescribe OTP header to be available. */
5405 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5406 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5408 leave:
5409 if (NULL != saved_url) {
5410 /* Revert URL to original one */
5411 zfree(context->url);
5412 context->url = saved_url;
5414 #if HAVE_CURL_REAUTHORIZATION_BUG
5415 if (NULL != saved_curl) {
5416 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5417 context->curl = saved_curl;
5419 #endif
5421 free(code);
5422 free(message);
5423 xmlFreeDoc(response);
5424 xmlFreeNode(request);
5426 if (!err)
5427 isds_log(ILF_ISDS, ILL_DEBUG,
5428 _("New OTP code has been sent successfully on SendSMSCode "
5429 "request.\n"));
5430 return err;
5434 /* Convert response status code to isds_error code and set long message
5435 * @context is context to save long message to
5436 * @map is mapping from codes to errors and messages. Pass NULL for generic
5437 * handling.
5438 * @code is status code to translate
5439 * @message is non-localized status message to put into long message in case
5440 * of uknown error. It can be NULL if server did not provide any.
5441 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5442 * invalid invocation. */
5443 static isds_error statuscode2isds_error(struct isds_ctx *context,
5444 const struct code_map_isds_error *map,
5445 const xmlChar *code, const xmlChar *message) {
5446 if (NULL == code) {
5447 isds_log_message(context,
5448 _("NULL status code passed to statuscode2isds_error()"));
5449 return IE_INVAL;
5452 if (NULL != map) {
5453 /* Check for known error codes */
5454 for (int i=0; map->codes[i] != NULL; i++) {
5455 if (!xmlStrcmp(code, map->codes[i])) {
5456 isds_log_message(context, _(map->meanings[i]));
5457 return map->errors[i];
5462 /* Other error */
5463 if (xmlStrcmp(code, BAD_CAST "0000")) {
5464 char *message_locale = _isds_utf82locale((char*)message);
5465 if (NULL == message_locale)
5466 isds_log_message(context, _("ISDS server returned unknown error"));
5467 else
5468 isds_log_message(context, message_locale);
5469 free(message_locale);
5470 return IE_ISDS;
5473 return IE_SUCCESS;
5475 #endif
5478 /* Change user password in ISDS.
5479 * User must supply old password, new password will takes effect after some
5480 * time, current session can continue. Password must fulfill some constraints.
5481 * @context is session context
5482 * @old_password is current password.
5483 * @new_password is requested new password
5484 * @otp auxiliary data required if one-time password authentication is in use,
5485 * defines OTP code (if known) and returns fine grade resolution of OTP
5486 * procedure. Pass NULL, if one-time password authentication is not needed.
5487 * Please note the @otp argument must match OTP method used at log-in time. See
5488 * isds_login() function for more details.
5489 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5490 * NULL, if you don't care.
5491 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5492 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5493 * awaiting OTP code that has been delivered by side channel to the user. */
5494 isds_error isds_change_password(struct isds_ctx *context,
5495 const char *old_password, const char *new_password,
5496 struct isds_otp *otp, char **refnumber) {
5497 isds_error err = IE_SUCCESS;
5498 #if HAVE_LIBCURL
5499 char *saved_url = NULL; /* No copy */
5500 #if HAVE_CURL_REAUTHORIZATION_BUG
5501 CURL *saved_curl = NULL; /* No copy */
5502 #endif
5503 xmlNsPtr isds_ns = NULL;
5504 xmlNodePtr request = NULL, node;
5505 xmlDocPtr response = NULL;
5506 xmlChar *code = NULL, *message = NULL;
5507 const xmlChar *codes[] = {
5508 BAD_CAST "1066",
5509 BAD_CAST "1067",
5510 BAD_CAST "1079",
5511 BAD_CAST "1080",
5512 BAD_CAST "1081",
5513 BAD_CAST "1082",
5514 BAD_CAST "1083",
5515 BAD_CAST "1090",
5516 BAD_CAST "1091",
5517 BAD_CAST "2300",
5518 BAD_CAST "9204"
5520 const char *meanings[] = {
5521 N_("Password length must be between 8 and 32 characters"),
5522 N_("Password cannot be reused"), /* Server does not distinguish 1067
5523 and 1091 on ChangePasswordOTP */
5524 N_("Password contains forbidden character"),
5525 N_("Password must contain at least one upper-case letter, "
5526 "one lower-case, and one digit"),
5527 N_("Password cannot contain sequence of three identical characters"),
5528 N_("Password cannot contain user identifier"),
5529 N_("Password is too simmple"),
5530 N_("Old password is not valid"),
5531 N_("Password cannot be reused"),
5532 N_("Unexpected error"),
5533 N_("LDAP update error")
5535 #endif
5537 if (!context) return IE_INVALID_CONTEXT;
5538 zfree(context->long_message);
5539 if (NULL != refnumber)
5540 zfree(*refnumber);
5541 if (NULL == old_password) {
5542 isds_log_message(context,
5543 _("Second argument (old password) of isds_change_password() "
5544 "is NULL"));
5545 return IE_INVAL;
5547 if (NULL == otp && NULL == new_password) {
5548 isds_log_message(context,
5549 _("Third argument (new password) of isds_change_password() "
5550 "is NULL"));
5551 return IE_INVAL;
5554 #if HAVE_LIBCURL
5555 /* Check if connection is established
5556 * TODO: This check should be done downstairs. */
5557 if (!context->curl) return IE_CONNECTION_CLOSED;
5559 if (context->otp && NULL == otp) {
5560 isds_log_message(context, _("If one-time password authentication "
5561 "method is in use, changing password requires one-time "
5562 "credentials either"));
5563 return IE_INVAL;
5566 /* Build ChangeISDSPassword request */
5567 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5568 BAD_CAST "ChangePasswordOTP");
5569 if (!request) {
5570 isds_log_message(context, (NULL == otp) ?
5571 _("Could not build ChangeISDSPassword request") :
5572 _("Could not build ChangePasswordOTP request"));
5573 return IE_ERROR;
5575 isds_ns = xmlNewNs(request,
5576 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5577 NULL);
5578 if(!isds_ns) {
5579 isds_log_message(context, _("Could not create ISDS name space"));
5580 xmlFreeNode(request);
5581 return IE_ERROR;
5583 xmlSetNs(request, isds_ns);
5585 INSERT_STRING(request, "dbOldPassword", old_password);
5586 INSERT_STRING(request, "dbNewPassword", new_password);
5588 if (NULL != otp) {
5589 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5590 switch (otp->method) {
5591 case OTP_HMAC:
5592 isds_log(ILF_SEC, ILL_INFO,
5593 _("Selected authentication method: "
5594 "HMAC-based one-time password\n"));
5595 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5596 break;
5597 case OTP_TIME:
5598 isds_log(ILF_SEC, ILL_INFO,
5599 _("Selected authentication method: "
5600 "Time-based one-time password\n"));
5601 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5602 if (otp->otp_code == NULL) {
5603 isds_log(ILF_SEC, ILL_INFO,
5604 _("OTP code has not been provided by "
5605 "application, requesting server for "
5606 "new one.\n"));
5607 err = _isds_request_totp_code(context, old_password, otp,
5608 refnumber);
5609 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5610 goto leave;
5612 } else {
5613 isds_log(ILF_SEC, ILL_INFO,
5614 _("OTP code has been provided by "
5615 "application, not requesting server "
5616 "for new one.\n"));
5618 break;
5619 default:
5620 isds_log_message(context,
5621 _("Unknown one-time password authentication "
5622 "method requested by application"));
5623 err = IE_ENUM;
5624 goto leave;
5627 /* Change URL temporarily for sending this request only */
5629 char *new_url = NULL;
5630 if ((err = _isds_build_url_from_context(context,
5631 "%1$.*2$sasws/changePassword", &new_url))) {
5632 goto leave;
5634 saved_url = context->url;
5635 context->url = new_url;
5638 /* Store credentials for sending this request only */
5639 context->otp_credentials = otp;
5640 _isds_discard_credentials(context, 0);
5641 if ((err = _isds_store_credentials(context, context->saved_username,
5642 old_password, NULL))) {
5643 _isds_discard_credentials(context, 0);
5644 goto leave;
5646 #if HAVE_CURL_REAUTHORIZATION_BUG
5647 saved_curl = context->curl;
5648 context->curl = curl_easy_init();
5649 if (NULL == context->curl) {
5650 err = IE_ERROR;
5651 goto leave;
5653 if (context->timeout) {
5654 err = isds_set_timeout(context, context->timeout);
5655 if (err) goto leave;
5657 #endif
5660 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5661 _("Sending ChangeISDSPassword request to ISDS\n") :
5662 _("Sending ChangePasswordOTP request to ISDS\n"));
5664 /* Sent request */
5665 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5666 request, &response, NULL, NULL);
5668 if (otp) {
5669 /* Remove temporal credentials */
5670 _isds_discard_credentials(context, 0);
5671 /* Detach pointer to OTP credentials from context */
5672 context->otp_credentials = NULL;
5673 /* Keep context->otp true to keep signaling this is OTP session */
5676 /* Destroy request */
5677 xmlFreeNode(request); request = NULL;
5679 if (err) {
5680 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5681 _("Processing ISDS response on ChangeISDSPassword "
5682 "request failed\n") :
5683 _("Processing ISDS response on ChangePasswordOTP "
5684 "request failed\n"));
5685 goto leave;
5688 /* Check for response status */
5689 err = isds_response_status(context,
5690 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5691 &code, &message, (xmlChar **)refnumber);
5692 if (err) {
5693 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5694 _("ISDS response on ChangeISDSPassword request is missing "
5695 "status\n") :
5696 _("ISDS response on ChangePasswordOTP request is missing "
5697 "status\n"));
5698 goto leave;
5701 /* Check for known error codes */
5702 for (int i=0; i < sizeof(codes)/sizeof(*codes); i++) {
5703 if (!xmlStrcmp(code, codes[i])) {
5704 char *code_locale = _isds_utf82locale((char*)code);
5705 char *message_locale = _isds_utf82locale((char*)message);
5706 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5707 _("Server refused to change password on ChangeISDSPassword "
5708 "request (code=%s, message=%s)\n") :
5709 _("Server refused to change password on ChangePasswordOTP "
5710 "request (code=%s, message=%s)\n"),
5711 code_locale, message_locale);
5712 free(code_locale);
5713 free(message_locale);
5714 isds_log_message(context, _(meanings[i]));
5715 err = IE_INVAL;
5716 goto leave;
5720 /* Other error */
5721 if (xmlStrcmp(code, BAD_CAST "0000")) {
5722 char *code_locale = _isds_utf82locale((char*)code);
5723 char *message_locale = _isds_utf82locale((char*)message);
5724 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5725 _("Server refused to change password on ChangeISDSPassword "
5726 "request (code=%s, message=%s)\n") :
5727 _("Server refused to change password on ChangePasswordOTP "
5728 "request (code=%s, message=%s)\n"),
5729 code_locale, message_locale);
5730 isds_log_message(context, message_locale);
5731 free(code_locale);
5732 free(message_locale);
5733 err = IE_ISDS;
5734 goto leave;
5737 /* Otherwise password changed successfully */
5739 leave:
5740 if (NULL != saved_url) {
5741 /* Revert URL to original one */
5742 zfree(context->url);
5743 context->url = saved_url;
5745 #if HAVE_CURL_REAUTHORIZATION_BUG
5746 if (NULL != saved_curl) {
5747 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5748 context->curl = saved_curl;
5750 #endif
5752 free(code);
5753 free(message);
5754 xmlFreeDoc(response);
5755 xmlFreeNode(request);
5757 if (!err)
5758 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5759 _("Password changed successfully on ChangeISDSPassword "
5760 "request.\n") :
5761 _("Password changed successfully on ChangePasswordOTP "
5762 "request.\n"));
5763 #else /* not HAVE_LIBCURL */
5764 err = IE_NOTSUP;
5765 #endif
5767 return err;
5771 #if HAVE_LIBCURL
5772 /* Generic middle part with request sending and response check.
5773 * It sends prepared request and checks for error code.
5774 * @context is ISDS session context.
5775 * @service is ISDS service handler
5776 * @service_name is name in scope of given @service
5777 * @request is XML tree with request. Will be freed to save memory.
5778 * @response is XML document outputting ISDS response.
5779 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5780 * @map is mapping from status code to library error. Pass NULL if no special
5781 * handling is requested.
5782 * NULL, if you don't care. */
5783 static isds_error send_destroy_request_check_response(
5784 struct isds_ctx *context,
5785 const isds_service service, const xmlChar *service_name,
5786 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5787 const struct code_map_isds_error *map) {
5788 isds_error err = IE_SUCCESS;
5789 char *service_name_locale = NULL;
5790 xmlChar *code = NULL, *message = NULL;
5793 if (!context) return IE_INVALID_CONTEXT;
5794 if (!service_name || *service_name == '\0' || !request || !*request ||
5795 !response)
5796 return IE_INVAL;
5798 /* Check if connection is established
5799 * TODO: This check should be done downstairs. */
5800 if (!context->curl) return IE_CONNECTION_CLOSED;
5802 service_name_locale = _isds_utf82locale((char*) service_name);
5803 if (!service_name_locale) {
5804 err = IE_NOMEM;
5805 goto leave;
5808 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5809 service_name_locale);
5811 /* Send request */
5812 err = _isds(context, service, *request, response, NULL, NULL);
5813 xmlFreeNode(*request); *request = NULL;
5815 if (err) {
5816 isds_log(ILF_ISDS, ILL_DEBUG,
5817 _("Processing ISDS response on %s request failed\n"),
5818 service_name_locale);
5819 goto leave;
5822 /* Check for response status */
5823 err = isds_response_status(context, service, *response,
5824 &code, &message, refnumber);
5825 if (err) {
5826 isds_log(ILF_ISDS, ILL_DEBUG,
5827 _("ISDS response on %s request is missing status\n"),
5828 service_name_locale);
5829 goto leave;
5832 err = statuscode2isds_error(context, map, code, message);
5834 /* Request processed, but server failed */
5835 if (xmlStrcmp(code, BAD_CAST "0000")) {
5836 char *code_locale = _isds_utf82locale((char*) code);
5837 char *message_locale = _isds_utf82locale((char*) message);
5838 isds_log(ILF_ISDS, ILL_DEBUG,
5839 _("Server refused %s request (code=%s, message=%s)\n"),
5840 service_name_locale, code_locale, message_locale);
5841 free(code_locale);
5842 free(message_locale);
5843 goto leave;
5847 leave:
5848 free(code);
5849 free(message);
5850 if (err && *response) {
5851 xmlFreeDoc(*response);
5852 *response = NULL;
5854 if (*request) {
5855 xmlFreeNode(*request);
5856 *request = NULL;
5858 free(service_name_locale);
5860 return err;
5864 /* Generic bottom half with request sending.
5865 * It sends prepared request, checks for error code, destroys response and
5866 * request and log success or failure.
5867 * @context is ISDS session context.
5868 * @service is ISDS service handler
5869 * @service_name is name in scope of given @service
5870 * @request is XML tree with request. Will be freed to save memory.
5871 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5872 * NULL, if you don't care. */
5873 static isds_error send_request_check_drop_response(
5874 struct isds_ctx *context,
5875 const isds_service service, const xmlChar *service_name,
5876 xmlNodePtr *request, xmlChar **refnumber) {
5877 isds_error err = IE_SUCCESS;
5878 xmlDocPtr response = NULL;
5881 if (!context) return IE_INVALID_CONTEXT;
5882 if (!service_name || *service_name == '\0' || !request || !*request)
5883 return IE_INVAL;
5885 /* Send request and check response*/
5886 err = send_destroy_request_check_response(context,
5887 service, service_name, request, &response, refnumber, NULL);
5889 xmlFreeDoc(response);
5891 if (*request) {
5892 xmlFreeNode(*request);
5893 *request = NULL;
5896 if (!err) {
5897 char *service_name_locale = _isds_utf82locale((char *) service_name);
5898 isds_log(ILF_ISDS, ILL_DEBUG,
5899 _("%s request processed by server successfully.\n"),
5900 service_name_locale);
5901 free(service_name_locale);
5904 return err;
5908 /* Insert isds_credentials_delivery structure into XML request if not NULL
5909 * @context is session context
5910 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5911 * credentials delivery. The email field is passed.
5912 * @parent is XML element where to insert */
5913 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5914 const struct isds_credentials_delivery *credentials_delivery,
5915 xmlNodePtr parent) {
5916 isds_error err = IE_SUCCESS;
5917 xmlNodePtr node;
5919 if (!context) return IE_INVALID_CONTEXT;
5920 if (!parent) return IE_INVAL;
5922 if (credentials_delivery) {
5923 /* Following elements are valid only for services:
5924 * NewAccessData, AddDataBoxUser, CreateDataBox */
5925 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5926 INSERT_STRING(parent, "email", credentials_delivery->email);
5929 leave:
5930 return err;
5934 /* Extract credentials delivery from ISDS response.
5935 * @context is session context
5936 * @credentials_delivery is pointer to valid structure to fill in returned
5937 * user's password (and new log-in name). If NULL, do not extract the data.
5938 * @response is pointer to XML document with ISDS response
5939 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5940 * @return IE_SUCCESS even if new user name has not been found because it's not
5941 * clear whether it's returned always. */
5942 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5943 struct isds_credentials_delivery *credentials_delivery,
5944 xmlDocPtr response, const char *request_name) {
5945 isds_error err = IE_SUCCESS;
5946 xmlXPathContextPtr xpath_ctx = NULL;
5947 xmlXPathObjectPtr result = NULL;
5948 char *xpath_query = NULL;
5950 if (!context) return IE_INVALID_CONTEXT;
5951 if (credentials_delivery) {
5952 zfree(credentials_delivery->token);
5953 zfree(credentials_delivery->new_user_name);
5955 if (!response || !request_name || !*request_name) return IE_INVAL;
5958 /* Extract optional token */
5959 if (credentials_delivery) {
5960 xpath_ctx = xmlXPathNewContext(response);
5961 if (!xpath_ctx) {
5962 err = IE_ERROR;
5963 goto leave;
5965 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5966 err = IE_ERROR;
5967 goto leave;
5970 /* Verify root element */
5971 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
5972 request_name)) {
5973 err = IE_NOMEM;
5974 goto leave;
5976 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
5977 if (!result) {
5978 err = IE_ERROR;
5979 goto leave;
5981 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5982 char *request_name_locale = _isds_utf82locale(request_name);
5983 isds_log(ILF_ISDS, ILL_WARNING,
5984 _("Wrong element in ISDS response for %s request "
5985 "while extracting credentials delivery details\n"),
5986 request_name_locale);
5987 free(request_name_locale);
5988 err = IE_ERROR;
5989 goto leave;
5991 xpath_ctx->node = result->nodesetval->nodeTab[0];
5994 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5995 * optional. */
5996 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
5998 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
5999 if (!credentials_delivery->token) {
6000 char *request_name_locale = _isds_utf82locale(request_name);
6001 isds_log(ILF_ISDS, ILL_ERR,
6002 _("ISDS did not return token on %s request "
6003 "even if requested\n"), request_name_locale);
6004 free(request_name_locale);
6005 err = IE_ERROR;
6009 leave:
6010 free(xpath_query);
6011 xmlXPathFreeObject(result);
6012 xmlXPathFreeContext(xpath_ctx);
6014 return err;
6018 /* Build XSD:tCreateDBInput request type for box creating.
6019 * @context is session context
6020 * @request outputs built XML tree
6021 * @service_name is request name of SERVICE_DB_MANIPULATION service
6022 * @box is box description to create including single primary user (in case of
6023 * FO box type)
6024 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6025 * box, or contact address of PFO box owner)
6026 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6027 * @upper_box_id is optional ID of supper box if currently created box is
6028 * subordinated.
6029 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6030 * don't care.
6031 * @credentials_delivery is valid pointer if ISDS should return token that box
6032 * owner can use to obtain his new credentials in on-line way. Then valid email
6033 * member value should be supplied.
6034 * @approval is optional external approval of box manipulation */
6035 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
6036 xmlNodePtr *request, const xmlChar *service_name,
6037 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6038 const xmlChar *former_names, const xmlChar *upper_box_id,
6039 const xmlChar *ceo_label,
6040 const struct isds_credentials_delivery *credentials_delivery,
6041 const struct isds_approval *approval) {
6042 isds_error err = IE_SUCCESS;
6043 xmlNsPtr isds_ns = NULL;
6044 xmlNodePtr node, dbPrimaryUsers;
6045 xmlChar *string = NULL;
6046 const struct isds_list *item;
6049 if (!context) return IE_INVALID_CONTEXT;
6050 if (!request || !service_name || service_name[0] == '\0' || !box)
6051 return IE_INVAL;
6054 /* Build CreateDataBox-similar request */
6055 *request = xmlNewNode(NULL, service_name);
6056 if (!*request) {
6057 char *service_name_locale = _isds_utf82locale((char*) service_name);
6058 isds_printf_message(context, _("Could build %s request"),
6059 service_name_locale);
6060 free(service_name_locale);
6061 return IE_ERROR;
6063 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
6064 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
6065 if (!isds_ns) {
6066 isds_log_message(context, _("Could not create ISDS1 name space"));
6067 xmlFreeNode(*request);
6068 return IE_ERROR;
6070 } else {
6071 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6072 if (!isds_ns) {
6073 isds_log_message(context, _("Could not create ISDS name space"));
6074 xmlFreeNode(*request);
6075 return IE_ERROR;
6078 xmlSetNs(*request, isds_ns);
6080 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6081 err = insert_DbOwnerInfo(context, box, node);
6082 if (err) goto leave;
6084 /* Insert users */
6085 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6086 * verbose documentation allows none dbUserInfo */
6087 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6088 for (item = users; item; item = item->next) {
6089 if (item->data) {
6090 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6091 err = insert_DbUserInfo(context,
6092 (struct isds_DbUserInfo *) item->data, node);
6093 if (err) goto leave;
6097 INSERT_STRING(*request, "dbFormerNames", former_names);
6098 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6099 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6101 err = insert_credentials_delivery(context, credentials_delivery, *request);
6102 if (err) goto leave;
6104 err = insert_GExtApproval(context, approval, *request);
6105 if (err) goto leave;
6107 leave:
6108 if (err) {
6109 xmlFreeNode(*request);
6110 *request = NULL;
6112 free(string);
6113 return err;
6115 #endif /* HAVE_LIBCURL */
6118 /* Create new box.
6119 * @context is session context
6120 * @box is box description to create including single primary user (in case of
6121 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6122 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6123 * box, or contact address of PFO box owner)
6124 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6125 * @upper_box_id is optional ID of supper box if currently created box is
6126 * subordinated.
6127 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6128 * @credentials_delivery is NULL if new password should be delivered off-line
6129 * to box owner. It is valid pointer if owner should obtain new password on-line
6130 * on dedicated web server. Then input @credentials_delivery.email value is
6131 * his e-mail address he must provide to dedicated web server together
6132 * with output reallocated @credentials_delivery.token member. Output
6133 * member @credentials_delivery.new_user_name is unused up on this call.
6134 * @approval is optional external approval of box manipulation
6135 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6136 * NULL, if you don't care.*/
6137 isds_error isds_add_box(struct isds_ctx *context,
6138 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6139 const char *former_names, const char *upper_box_id,
6140 const char *ceo_label,
6141 struct isds_credentials_delivery *credentials_delivery,
6142 const struct isds_approval *approval, char **refnumber) {
6143 isds_error err = IE_SUCCESS;
6144 #if HAVE_LIBCURL
6145 xmlNodePtr request = NULL;
6146 xmlDocPtr response = NULL;
6147 xmlXPathContextPtr xpath_ctx = NULL;
6148 xmlXPathObjectPtr result = NULL;
6149 #endif
6152 if (!context) return IE_INVALID_CONTEXT;
6153 zfree(context->long_message);
6154 if (credentials_delivery) {
6155 zfree(credentials_delivery->token);
6156 zfree(credentials_delivery->new_user_name);
6158 if (!box) return IE_INVAL;
6160 #if HAVE_LIBCURL
6161 /* Scratch box ID */
6162 zfree(box->dbID);
6164 /* Build CreateDataBox request */
6165 err = build_CreateDBInput_request(context,
6166 &request, BAD_CAST "CreateDataBox",
6167 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6168 (xmlChar *) ceo_label, credentials_delivery, approval);
6169 if (err) goto leave;
6171 /* Send it to server and process response */
6172 err = send_destroy_request_check_response(context,
6173 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6174 &response, (xmlChar **) refnumber, NULL);
6176 /* Extract box ID */
6177 xpath_ctx = xmlXPathNewContext(response);
6178 if (!xpath_ctx) {
6179 err = IE_ERROR;
6180 goto leave;
6182 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6183 err = IE_ERROR;
6184 goto leave;
6186 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6188 /* Extract optional token */
6189 err = extract_credentials_delivery(context, credentials_delivery, response,
6190 "CreateDataBox");
6192 leave:
6193 xmlXPathFreeObject(result);
6194 xmlXPathFreeContext(xpath_ctx);
6195 xmlFreeDoc(response);
6196 xmlFreeNode(request);
6198 if (!err) {
6199 isds_log(ILF_ISDS, ILL_DEBUG,
6200 _("CreateDataBox request processed by server successfully.\n"));
6202 #else /* not HAVE_LIBCURL */
6203 err = IE_NOTSUP;
6204 #endif
6206 return err;
6210 /* Notify ISDS about new PFO entity.
6211 * This function has no real effect.
6212 * @context is session context
6213 * @box is PFO description including single primary user.
6214 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6215 * @former_names is optional undocumented string. Pass NULL if you don't care.
6216 * @upper_box_id is optional ID of supper box if currently created box is
6217 * subordinated.
6218 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6219 * @approval is optional external approval of box manipulation
6220 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6221 * NULL, if you don't care.*/
6222 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6223 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6224 const char *former_names, const char *upper_box_id,
6225 const char *ceo_label, const struct isds_approval *approval,
6226 char **refnumber) {
6227 isds_error err = IE_SUCCESS;
6228 #if HAVE_LIBCURL
6229 xmlNodePtr request = NULL;
6230 #endif
6232 if (!context) return IE_INVALID_CONTEXT;
6233 zfree(context->long_message);
6234 if (!box) return IE_INVAL;
6236 #if HAVE_LIBCURL
6237 /* Build CreateDataBoxPFOInfo request */
6238 err = build_CreateDBInput_request(context,
6239 &request, BAD_CAST "CreateDataBoxPFOInfo",
6240 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6241 (xmlChar *) ceo_label, NULL, approval);
6242 if (err) goto leave;
6244 /* Send it to server and process response */
6245 err = send_request_check_drop_response(context,
6246 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6247 (xmlChar **) refnumber);
6248 /* XXX: XML Schema names output dbID element but textual documentation
6249 * states no box identifier is returned. */
6250 leave:
6251 xmlFreeNode(request);
6252 #else /* not HAVE_LIBCURL */
6253 err = IE_NOTSUP;
6254 #endif
6255 return err;
6259 /* Common implementation for removing given box.
6260 * @context is session context
6261 * @service_name is UTF-8 encoded name fo ISDS service
6262 * @box is box description to delete
6263 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6264 * carry sane value. If NULL, do not inject this information into request.
6265 * @approval is optional external approval of box manipulation
6266 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6267 * NULL, if you don't care.*/
6268 isds_error _isds_delete_box_common(struct isds_ctx *context,
6269 const xmlChar *service_name,
6270 const struct isds_DbOwnerInfo *box, const struct tm *since,
6271 const struct isds_approval *approval, char **refnumber) {
6272 isds_error err = IE_SUCCESS;
6273 #if HAVE_LIBCURL
6274 xmlNsPtr isds_ns = NULL;
6275 xmlNodePtr request = NULL;
6276 xmlNodePtr node;
6277 xmlChar *string = NULL;
6278 #endif
6281 if (!context) return IE_INVALID_CONTEXT;
6282 zfree(context->long_message);
6283 if (!service_name || !*service_name || !box) return IE_INVAL;
6286 #if HAVE_LIBCURL
6287 /* Build DeleteDataBox(Promptly) request */
6288 request = xmlNewNode(NULL, service_name);
6289 if (!request) {
6290 char *service_name_locale = _isds_utf82locale((char*)service_name);
6291 isds_printf_message(context,
6292 _("Could build %s request"), service_name_locale);
6293 free(service_name_locale);
6294 return IE_ERROR;
6296 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6297 if(!isds_ns) {
6298 isds_log_message(context, _("Could not create ISDS name space"));
6299 xmlFreeNode(request);
6300 return IE_ERROR;
6302 xmlSetNs(request, isds_ns);
6304 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6305 err = insert_DbOwnerInfo(context, box, node);
6306 if (err) goto leave;
6308 if (since) {
6309 err = tm2datestring(since, &string);
6310 if (err) {
6311 isds_log_message(context,
6312 _("Could not convert `since' argument to ISO date string"));
6313 goto leave;
6315 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6316 zfree(string);
6319 err = insert_GExtApproval(context, approval, request);
6320 if (err) goto leave;
6323 /* Send it to server and process response */
6324 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6325 service_name, &request, (xmlChar **) refnumber);
6327 leave:
6328 xmlFreeNode(request);
6329 free(string);
6330 #else /* not HAVE_LIBCURL */
6331 err = IE_NOTSUP;
6332 #endif
6333 return err;
6337 /* Remove given box permanently.
6338 * @context is session context
6339 * @box is box description to delete
6340 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6341 * carry sane value.
6342 * @approval is optional external approval of box manipulation
6343 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6344 * NULL, if you don't care.*/
6345 isds_error isds_delete_box(struct isds_ctx *context,
6346 const struct isds_DbOwnerInfo *box, const struct tm *since,
6347 const struct isds_approval *approval, char **refnumber) {
6348 if (!context) return IE_INVALID_CONTEXT;
6349 zfree(context->long_message);
6350 if (!box || !since) return IE_INVAL;
6352 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6353 box, since, approval, refnumber);
6357 /* Undocumented function.
6358 * @context is session context
6359 * @box is box description to delete
6360 * @approval is optional external approval of box manipulation
6361 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6362 * NULL, if you don't care.*/
6363 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6364 const struct isds_DbOwnerInfo *box,
6365 const struct isds_approval *approval, char **refnumber) {
6366 if (!context) return IE_INVALID_CONTEXT;
6367 zfree(context->long_message);
6368 if (!box) return IE_INVAL;
6370 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6371 box, NULL, approval, refnumber);
6375 /* Update data about given box.
6376 * @context is session context
6377 * @old_box current box description
6378 * @new_box are updated data about @old_box
6379 * @approval is optional external approval of box manipulation
6380 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6381 * NULL, if you don't care.*/
6382 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6383 const struct isds_DbOwnerInfo *old_box,
6384 const struct isds_DbOwnerInfo *new_box,
6385 const struct isds_approval *approval, char **refnumber) {
6386 isds_error err = IE_SUCCESS;
6387 #if HAVE_LIBCURL
6388 xmlNsPtr isds_ns = NULL;
6389 xmlNodePtr request = NULL;
6390 xmlNodePtr node;
6391 #endif
6394 if (!context) return IE_INVALID_CONTEXT;
6395 zfree(context->long_message);
6396 if (!old_box || !new_box) return IE_INVAL;
6399 #if HAVE_LIBCURL
6400 /* Build UpdateDataBoxDescr request */
6401 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6402 if (!request) {
6403 isds_log_message(context,
6404 _("Could build UpdateDataBoxDescr request"));
6405 return IE_ERROR;
6407 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6408 if(!isds_ns) {
6409 isds_log_message(context, _("Could not create ISDS name space"));
6410 xmlFreeNode(request);
6411 return IE_ERROR;
6413 xmlSetNs(request, isds_ns);
6415 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6416 err = insert_DbOwnerInfo(context, old_box, node);
6417 if (err) goto leave;
6419 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6420 err = insert_DbOwnerInfo(context, new_box, node);
6421 if (err) goto leave;
6423 err = insert_GExtApproval(context, approval, request);
6424 if (err) goto leave;
6427 /* Send it to server and process response */
6428 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6429 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6431 leave:
6432 xmlFreeNode(request);
6433 #else /* not HAVE_LIBCURL */
6434 err = IE_NOTSUP;
6435 #endif
6437 return err;
6441 #if HAVE_LIBCURL
6442 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6443 * code
6444 * @context is session context
6445 * @service is SOAP service
6446 * @service_name is name of request in @service
6447 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6448 * @box_id is box ID of interest
6449 * @approval is optional external approval of box manipulation
6450 * @response is server SOAP body response as XML document
6451 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6452 * NULL, if you don't care.
6453 * @return error coded from lower layer, context message will be set up
6454 * appropriately. */
6455 static isds_error build_send_dbid_request_check_response(
6456 struct isds_ctx *context, const isds_service service,
6457 const xmlChar *service_name, const xmlChar *box_id_element,
6458 const xmlChar *box_id, const struct isds_approval *approval,
6459 xmlDocPtr *response, xmlChar **refnumber) {
6461 isds_error err = IE_SUCCESS;
6462 char *service_name_locale = NULL, *box_id_locale = NULL;
6463 xmlNodePtr request = NULL, node;
6464 xmlNsPtr isds_ns = NULL;
6466 if (!context) return IE_INVALID_CONTEXT;
6467 if (!service_name || !box_id) return IE_INVAL;
6468 if (!response) return IE_INVAL;
6470 /* Free output argument */
6471 xmlFreeDoc(*response); *response = NULL;
6473 /* Prepare strings */
6474 service_name_locale = _isds_utf82locale((char*)service_name);
6475 if (!service_name_locale) {
6476 err = IE_NOMEM;
6477 goto leave;
6479 box_id_locale = _isds_utf82locale((char*)box_id);
6480 if (!box_id_locale) {
6481 err = IE_NOMEM;
6482 goto leave;
6485 /* Build request */
6486 request = xmlNewNode(NULL, service_name);
6487 if (!request) {
6488 isds_printf_message(context,
6489 _("Could not build %s request for %s box"), service_name_locale,
6490 box_id_locale);
6491 err = IE_ERROR;
6492 goto leave;
6494 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6495 if(!isds_ns) {
6496 isds_log_message(context, _("Could not create ISDS name space"));
6497 err = IE_ERROR;
6498 goto leave;
6500 xmlSetNs(request, isds_ns);
6502 /* Add XSD:tIdDbInput children */
6503 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6504 INSERT_STRING(request, box_id_element, box_id);
6505 err = insert_GExtApproval(context, approval, request);
6506 if (err) goto leave;
6508 /* Send request and check response*/
6509 err = send_destroy_request_check_response(context,
6510 service, service_name, &request, response, refnumber, NULL);
6512 leave:
6513 free(service_name_locale);
6514 free(box_id_locale);
6515 xmlFreeNode(request);
6516 return err;
6518 #endif /* HAVE_LIBCURL */
6521 /* Get data about all users assigned to given box.
6522 * @context is session context
6523 * @box_id is box ID
6524 * @users is automatically reallocated list of struct isds_DbUserInfo */
6525 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6526 struct isds_list **users) {
6527 isds_error err = IE_SUCCESS;
6528 #if HAVE_LIBCURL
6529 xmlDocPtr response = NULL;
6530 xmlXPathContextPtr xpath_ctx = NULL;
6531 xmlXPathObjectPtr result = NULL;
6532 int i;
6533 struct isds_list *item, *prev_item = NULL;
6534 #endif
6536 if (!context) return IE_INVALID_CONTEXT;
6537 zfree(context->long_message);
6538 if (!users || !box_id) return IE_INVAL;
6539 isds_list_free(users);
6542 #if HAVE_LIBCURL
6543 /* Do request and check for success */
6544 err = build_send_dbid_request_check_response(context,
6545 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6546 BAD_CAST box_id, NULL, &response, NULL);
6547 if (err) goto leave;
6550 /* Extract data */
6551 /* Prepare structure */
6552 xpath_ctx = xmlXPathNewContext(response);
6553 if (!xpath_ctx) {
6554 err = IE_ERROR;
6555 goto leave;
6557 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6558 err = IE_ERROR;
6559 goto leave;
6562 /* Set context node */
6563 result = xmlXPathEvalExpression(BAD_CAST
6564 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6565 xpath_ctx);
6566 if (!result) {
6567 err = IE_ERROR;
6568 goto leave;
6570 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6571 /* Iterate over all users */
6572 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6574 /* Prepare structure */
6575 item = calloc(1, sizeof(*item));
6576 if (!item) {
6577 err = IE_NOMEM;
6578 goto leave;
6580 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6581 if (i == 0) *users = item;
6582 else prev_item->next = item;
6583 prev_item = item;
6585 /* Extract it */
6586 xpath_ctx->node = result->nodesetval->nodeTab[i];
6587 err = extract_DbUserInfo(context,
6588 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6589 if (err) goto leave;
6593 leave:
6594 if (err) {
6595 isds_list_free(users);
6598 xmlXPathFreeObject(result);
6599 xmlXPathFreeContext(xpath_ctx);
6600 xmlFreeDoc(response);
6602 if (!err)
6603 isds_log(ILF_ISDS, ILL_DEBUG,
6604 _("GetDataBoxUsers request processed by server "
6605 "successfully.\n"));
6606 #else /* not HAVE_LIBCURL */
6607 err = IE_NOTSUP;
6608 #endif
6610 return err;
6614 /* Update data about user assigned to given box.
6615 * @context is session context
6616 * @box is box identification
6617 * @old_user identifies user to update
6618 * @new_user are updated data about @old_user
6619 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6620 * NULL, if you don't care.*/
6621 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6622 const struct isds_DbOwnerInfo *box,
6623 const struct isds_DbUserInfo *old_user,
6624 const struct isds_DbUserInfo *new_user,
6625 char **refnumber) {
6626 isds_error err = IE_SUCCESS;
6627 #if HAVE_LIBCURL
6628 xmlNsPtr isds_ns = NULL;
6629 xmlNodePtr request = NULL;
6630 xmlNodePtr node;
6631 #endif
6634 if (!context) return IE_INVALID_CONTEXT;
6635 zfree(context->long_message);
6636 if (!box || !old_user || !new_user) return IE_INVAL;
6639 #if HAVE_LIBCURL
6640 /* Build UpdateDataBoxUser request */
6641 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6642 if (!request) {
6643 isds_log_message(context,
6644 _("Could build UpdateDataBoxUser request"));
6645 return IE_ERROR;
6647 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6648 if(!isds_ns) {
6649 isds_log_message(context, _("Could not create ISDS name space"));
6650 xmlFreeNode(request);
6651 return IE_ERROR;
6653 xmlSetNs(request, isds_ns);
6655 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6656 err = insert_DbOwnerInfo(context, box, node);
6657 if (err) goto leave;
6659 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6660 err = insert_DbUserInfo(context, old_user, node);
6661 if (err) goto leave;
6663 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6664 err = insert_DbUserInfo(context, new_user, node);
6665 if (err) goto leave;
6667 /* Send it to server and process response */
6668 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6669 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6671 leave:
6672 xmlFreeNode(request);
6673 #else /* not HAVE_LIBCURL */
6674 err = IE_NOTSUP;
6675 #endif
6677 return err;
6681 /* Undocumented function.
6682 * @context is session context
6683 * @box_id is UTF-8 encoded box identifier
6684 * @token is UTF-8 encoded temporary password
6685 * @user_id outputs UTF-8 encoded reallocated user identifier
6686 * @password outpus UTF-8 encoded reallocated user password
6687 * Output arguments will be nulled in case of error */
6688 isds_error isds_activate(struct isds_ctx *context,
6689 const char *box_id, const char *token,
6690 char **user_id, char **password) {
6691 isds_error err = IE_SUCCESS;
6692 #if HAVE_LIBCURL
6693 xmlNsPtr isds_ns = NULL;
6694 xmlNodePtr request = NULL, node;
6695 xmlDocPtr response = NULL;
6696 xmlXPathContextPtr xpath_ctx = NULL;
6697 xmlXPathObjectPtr result = NULL;
6698 #endif
6701 if (!context) return IE_INVALID_CONTEXT;
6702 zfree(context->long_message);
6704 if (user_id) zfree(*user_id);
6705 if (password) zfree(*password);
6707 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6710 #if HAVE_LIBCURL
6711 /* Build Activate request */
6712 request = xmlNewNode(NULL, BAD_CAST "Activate");
6713 if (!request) {
6714 isds_log_message(context, _("Could build Activate request"));
6715 return IE_ERROR;
6717 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6718 if(!isds_ns) {
6719 isds_log_message(context, _("Could not create ISDS name space"));
6720 xmlFreeNode(request);
6721 return IE_ERROR;
6723 xmlSetNs(request, isds_ns);
6725 INSERT_STRING(request, "dbAccessDataId", token);
6726 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6727 INSERT_STRING(request, "dbID", box_id);
6730 /* Send request and check response*/
6731 err = send_destroy_request_check_response(context,
6732 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6733 &response, NULL, NULL);
6734 if (err) goto leave;
6737 /* Extract data */
6738 xpath_ctx = xmlXPathNewContext(response);
6739 if (!xpath_ctx) {
6740 err = IE_ERROR;
6741 goto leave;
6743 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6744 err = IE_ERROR;
6745 goto leave;
6747 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6748 xpath_ctx);
6749 if (!result) {
6750 err = IE_ERROR;
6751 goto leave;
6753 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6754 isds_log_message(context, _("Missing ActivateResponse element"));
6755 err = IE_ISDS;
6756 goto leave;
6758 if (result->nodesetval->nodeNr > 1) {
6759 isds_log_message(context, _("Multiple ActivateResponse element"));
6760 err = IE_ISDS;
6761 goto leave;
6763 xpath_ctx->node = result->nodesetval->nodeTab[0];
6764 xmlXPathFreeObject(result); result = NULL;
6766 EXTRACT_STRING("isds:userId", *user_id);
6767 if (!*user_id)
6768 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6769 "but did not return `userId' element.\n"));
6771 EXTRACT_STRING("isds:password", *password);
6772 if (!*password)
6773 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6774 "but did not return `password' element.\n"));
6776 leave:
6777 xmlXPathFreeObject(result);
6778 xmlXPathFreeContext(xpath_ctx);
6779 xmlFreeDoc(response);
6780 xmlFreeNode(request);
6782 if (!err)
6783 isds_log(ILF_ISDS, ILL_DEBUG,
6784 _("Activate request processed by server successfully.\n"));
6785 #else /* not HAVE_LIBCURL */
6786 err = IE_NOTSUP;
6787 #endif
6789 return err;
6793 /* Reset credentials of user assigned to given box.
6794 * @context is session context
6795 * @box is box identification
6796 * @user identifies user to reset password
6797 * @fee_paid is true if fee has been paid, false otherwise
6798 * @approval is optional external approval of box manipulation
6799 * @credentials_delivery is NULL if new password should be delivered off-line
6800 * to the user. It is valid pointer if user should obtain new password on-line
6801 * on dedicated web server. Then input @credentials_delivery.email value is
6802 * user's e-mail address user must provide to dedicated web server together
6803 * with @credentials_delivery.token. The output reallocated token user needs
6804 * to use to authorize on the web server to view his new password. Output
6805 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6806 * ISDS changed up on this call. (No reason why server could change the name
6807 * is known now.)
6808 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6809 * NULL, if you don't care.*/
6810 isds_error isds_reset_password(struct isds_ctx *context,
6811 const struct isds_DbOwnerInfo *box,
6812 const struct isds_DbUserInfo *user,
6813 const _Bool fee_paid, const struct isds_approval *approval,
6814 struct isds_credentials_delivery *credentials_delivery,
6815 char **refnumber) {
6816 isds_error err = IE_SUCCESS;
6817 #if HAVE_LIBCURL
6818 xmlNsPtr isds_ns = NULL;
6819 xmlNodePtr request = NULL, node;
6820 xmlDocPtr response = NULL;
6821 #endif
6824 if (!context) return IE_INVALID_CONTEXT;
6825 zfree(context->long_message);
6827 if (credentials_delivery) {
6828 zfree(credentials_delivery->token);
6829 zfree(credentials_delivery->new_user_name);
6831 if (!box || !user) return IE_INVAL;
6834 #if HAVE_LIBCURL
6835 /* Build NewAccessData request */
6836 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6837 if (!request) {
6838 isds_log_message(context,
6839 _("Could build NewAccessData request"));
6840 return IE_ERROR;
6842 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6843 if(!isds_ns) {
6844 isds_log_message(context, _("Could not create ISDS name space"));
6845 xmlFreeNode(request);
6846 return IE_ERROR;
6848 xmlSetNs(request, isds_ns);
6850 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6851 err = insert_DbOwnerInfo(context, box, node);
6852 if (err) goto leave;
6854 INSERT_ELEMENT(node, request, "dbUserInfo");
6855 err = insert_DbUserInfo(context, user, node);
6856 if (err) goto leave;
6858 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6860 err = insert_credentials_delivery(context, credentials_delivery, request);
6861 if (err) goto leave;
6863 err = insert_GExtApproval(context, approval, request);
6864 if (err) goto leave;
6866 /* Send request and check response*/
6867 err = send_destroy_request_check_response(context,
6868 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6869 &response, (xmlChar **) refnumber, NULL);
6870 if (err) goto leave;
6873 /* Extract optional token */
6874 err = extract_credentials_delivery(context, credentials_delivery,
6875 response, "NewAccessData");
6877 leave:
6878 xmlFreeDoc(response);
6879 xmlFreeNode(request);
6881 if (!err)
6882 isds_log(ILF_ISDS, ILL_DEBUG,
6883 _("NewAccessData request processed by server "
6884 "successfully.\n"));
6885 #else /* not HAVE_LIBCURL */
6886 err = IE_NOTSUP;
6887 #endif
6889 return err;
6893 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6894 * code, destroy response and log success.
6895 * @context is ISDS session context.
6896 * @service_name is name of SERVICE_DB_MANIPULATION service
6897 * @box is box identification
6898 * @user identifies user to remove
6899 * @credentials_delivery is NULL if new user's password should be delivered
6900 * off-line to the user. It is valid pointer if user should obtain new
6901 * password on-line on dedicated web server. Then input
6902 * @credentials_delivery.email value is user's e-mail address user must
6903 * provide to dedicated web server together with @credentials_delivery.token.
6904 * The output reallocated token user needs to use to authorize on the web
6905 * server to view his new password. Output reallocated
6906 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6907 * assingned or changed up on this call.
6908 * @approval is optional external approval of box manipulation
6909 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6910 * NULL, if you don't care. */
6911 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6912 struct isds_ctx *context, const xmlChar *service_name,
6913 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6914 struct isds_credentials_delivery *credentials_delivery,
6915 const struct isds_approval *approval, xmlChar **refnumber) {
6916 isds_error err = IE_SUCCESS;
6917 #if HAVE_LIBCURL
6918 xmlNsPtr isds_ns = NULL;
6919 xmlNodePtr request = NULL, node;
6920 xmlDocPtr response = NULL;
6921 #endif
6924 if (!context) return IE_INVALID_CONTEXT;
6925 zfree(context->long_message);
6926 if (credentials_delivery) {
6927 zfree(credentials_delivery->token);
6928 zfree(credentials_delivery->new_user_name);
6930 if (!service_name || service_name[0] == '\0' || !box || !user)
6931 return IE_INVAL;
6934 #if HAVE_LIBCURL
6935 /* Build NewAccessData or similar request */
6936 request = xmlNewNode(NULL, service_name);
6937 if (!request) {
6938 char *service_name_locale = _isds_utf82locale((char *) service_name);
6939 isds_printf_message(context, _("Could not build %s request"),
6940 service_name_locale);
6941 free(service_name_locale);
6942 return IE_ERROR;
6944 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6945 if(!isds_ns) {
6946 isds_log_message(context, _("Could not create ISDS name space"));
6947 xmlFreeNode(request);
6948 return IE_ERROR;
6950 xmlSetNs(request, isds_ns);
6952 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6953 err = insert_DbOwnerInfo(context, box, node);
6954 if (err) goto leave;
6956 INSERT_ELEMENT(node, request, "dbUserInfo");
6957 err = insert_DbUserInfo(context, user, node);
6958 if (err) goto leave;
6960 err = insert_credentials_delivery(context, credentials_delivery, request);
6961 if (err) goto leave;
6963 err = insert_GExtApproval(context, approval, request);
6964 if (err) goto leave;
6967 /* Send request and check response*/
6968 err = send_destroy_request_check_response(context,
6969 SERVICE_DB_MANIPULATION, service_name, &request, &response,
6970 refnumber, NULL);
6972 xmlFreeNode(request);
6973 request = NULL;
6975 /* Pick up credentials_delivery if requested */
6976 err = extract_credentials_delivery(context, credentials_delivery, response,
6977 (char *)service_name);
6979 leave:
6980 xmlFreeDoc(response);
6981 if (request) xmlFreeNode(request);
6983 if (!err) {
6984 char *service_name_locale = _isds_utf82locale((char *) service_name);
6985 isds_log(ILF_ISDS, ILL_DEBUG,
6986 _("%s request processed by server successfully.\n"),
6987 service_name_locale);
6988 free(service_name_locale);
6990 #else /* not HAVE_LIBCURL */
6991 err = IE_NOTSUP;
6992 #endif
6994 return err;
6998 /* Assign new user to given box.
6999 * @context is session context
7000 * @box is box identification
7001 * @user defines new user to add
7002 * @credentials_delivery is NULL if new user's password should be delivered
7003 * off-line to the user. It is valid pointer if user should obtain new
7004 * password on-line on dedicated web server. Then input
7005 * @credentials_delivery.email value is user's e-mail address user must
7006 * provide to dedicated web server together with @credentials_delivery.token.
7007 * The output reallocated token user needs to use to authorize on the web
7008 * server to view his new password. Output reallocated
7009 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7010 * assingned up on this call.
7011 * @approval is optional external approval of box manipulation
7012 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7013 * NULL, if you don't care.*/
7014 isds_error isds_add_user(struct isds_ctx *context,
7015 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7016 struct isds_credentials_delivery *credentials_delivery,
7017 const struct isds_approval *approval, char **refnumber) {
7018 return build_send_manipulationboxuser_request_check_drop_response(context,
7019 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
7020 approval, (xmlChar **) refnumber);
7024 /* Remove user assigned to given box.
7025 * @context is session context
7026 * @box is box identification
7027 * @user identifies user to remove
7028 * @approval is optional external approval of box manipulation
7029 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7030 * NULL, if you don't care.*/
7031 isds_error isds_delete_user(struct isds_ctx *context,
7032 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7033 const struct isds_approval *approval, char **refnumber) {
7034 return build_send_manipulationboxuser_request_check_drop_response(context,
7035 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
7036 (xmlChar **) refnumber);
7040 /* Get list of boxes in ZIP archive.
7041 * @context is session context
7042 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7043 * System recognizes following values currently: ALL (all boxes), UPG
7044 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
7045 * receiving commercial messages). This argument is a string because
7046 * specification states new values can appear in the future. Not all list
7047 * types are available to all users.
7048 * @buffer is automatically reallocated memory to store the list of boxes. The
7049 * list is zipped CSV file.
7050 * @buffer_length is size of @buffer data in bytes.
7051 * In case of error @buffer will be freed and @buffer_length will be
7052 * undefined.*/
7053 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7054 const char *list_identifier, void **buffer, size_t *buffer_length) {
7055 isds_error err = IE_SUCCESS;
7056 #if HAVE_LIBCURL
7057 xmlNsPtr isds_ns = NULL;
7058 xmlNodePtr request = NULL, node;
7059 xmlDocPtr response = NULL;
7060 xmlXPathContextPtr xpath_ctx = NULL;
7061 xmlXPathObjectPtr result = NULL;
7062 char *string = NULL;
7063 #endif
7066 if (!context) return IE_INVALID_CONTEXT;
7067 zfree(context->long_message);
7068 if (buffer) zfree(*buffer);
7069 if (!buffer || !buffer_length) return IE_INVAL;
7072 #if HAVE_LIBCURL
7073 /* Check if connection is established
7074 * TODO: This check should be done downstairs. */
7075 if (!context->curl) return IE_CONNECTION_CLOSED;
7078 /* Build AuthenticateMessage request */
7079 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7080 if (!request) {
7081 isds_log_message(context,
7082 _("Could not build GetDataBoxList request"));
7083 return IE_ERROR;
7085 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7086 if(!isds_ns) {
7087 isds_log_message(context, _("Could not create ISDS name space"));
7088 xmlFreeNode(request);
7089 return IE_ERROR;
7091 xmlSetNs(request, isds_ns);
7092 INSERT_STRING(request, "dblType", list_identifier);
7094 /* Send request to server and process response */
7095 err = send_destroy_request_check_response(context,
7096 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7097 &response, NULL, NULL);
7098 if (err) goto leave;
7101 /* Extract Base-64 encoded ZIP file */
7102 xpath_ctx = xmlXPathNewContext(response);
7103 if (!xpath_ctx) {
7104 err = IE_ERROR;
7105 goto leave;
7107 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7108 err = IE_ERROR;
7109 goto leave;
7111 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7113 /* Decode non-empty archive */
7114 if (string && string[0] != '\0') {
7115 *buffer_length = _isds_b64decode(string, buffer);
7116 if (*buffer_length == (size_t) -1) {
7117 isds_printf_message(context,
7118 _("Error while Base64-decoding box list archive"));
7119 err = IE_ERROR;
7120 goto leave;
7125 leave:
7126 free(string);
7127 xmlXPathFreeObject(result);
7128 xmlXPathFreeContext(xpath_ctx);
7129 xmlFreeDoc(response);
7130 xmlFreeNode(request);
7132 if (!err) {
7133 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7134 "processed by server successfully.\n"));
7136 #else /* not HAVE_LIBCURL */
7137 err = IE_NOTSUP;
7138 #endif
7140 return err;
7144 /* Find boxes suiting given criteria.
7145 * @criteria is filter. You should fill in at least some members.
7146 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7147 * possibly empty. Input NULL or valid old structure.
7148 * @return:
7149 * IE_SUCCESS if search succeeded, @boxes contains useful data
7150 * IE_NOEXIST if no such box exists, @boxes will be NULL
7151 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7152 * contains still valid data
7153 * other code if something bad happens. @boxes will be NULL. */
7154 isds_error isds_FindDataBox(struct isds_ctx *context,
7155 const struct isds_DbOwnerInfo *criteria,
7156 struct isds_list **boxes) {
7157 isds_error err = IE_SUCCESS;
7158 #if HAVE_LIBCURL
7159 _Bool truncated = 0;
7160 xmlNsPtr isds_ns = NULL;
7161 xmlNodePtr request = NULL;
7162 xmlDocPtr response = NULL;
7163 xmlChar *code = NULL, *message = NULL;
7164 xmlNodePtr db_owner_info;
7165 xmlXPathContextPtr xpath_ctx = NULL;
7166 xmlXPathObjectPtr result = NULL;
7167 xmlChar *string = NULL;
7168 #endif
7171 if (!context) return IE_INVALID_CONTEXT;
7172 zfree(context->long_message);
7173 if (!boxes) return IE_INVAL;
7174 isds_list_free(boxes);
7176 if (!criteria) {
7177 return IE_INVAL;
7180 #if HAVE_LIBCURL
7181 /* Check if connection is established
7182 * TODO: This check should be done downstairs. */
7183 if (!context->curl) return IE_CONNECTION_CLOSED;
7186 /* Build FindDataBox request */
7187 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
7188 if (!request) {
7189 isds_log_message(context,
7190 _("Could build FindDataBox request"));
7191 return IE_ERROR;
7193 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7194 if(!isds_ns) {
7195 isds_log_message(context, _("Could not create ISDS name space"));
7196 xmlFreeNode(request);
7197 return IE_ERROR;
7199 xmlSetNs(request, isds_ns);
7200 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7201 if (!db_owner_info) {
7202 isds_log_message(context, _("Could not add dbOwnerInfo child to "
7203 "FindDataBox element"));
7204 xmlFreeNode(request);
7205 return IE_ERROR;
7208 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
7209 if (err) goto leave;
7212 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
7214 /* Sent request */
7215 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7217 /* Destroy request */
7218 xmlFreeNode(request); request = NULL;
7220 if (err) {
7221 isds_log(ILF_ISDS, ILL_DEBUG,
7222 _("Processing ISDS response on FindDataBox "
7223 "request failed\n"));
7224 goto leave;
7227 /* Check for response status */
7228 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7229 &code, &message, NULL);
7230 if (err) {
7231 isds_log(ILF_ISDS, ILL_DEBUG,
7232 _("ISDS response on FindDataBox request is missing status\n"));
7233 goto leave;
7236 /* Request processed, but nothing found */
7237 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7238 !xmlStrcmp(code, BAD_CAST "5001")) {
7239 char *code_locale = _isds_utf82locale((char*)code);
7240 char *message_locale = _isds_utf82locale((char*)message);
7241 isds_log(ILF_ISDS, ILL_DEBUG,
7242 _("Server did not found any box on FindDataBox request "
7243 "(code=%s, message=%s)\n"), code_locale, message_locale);
7244 isds_log_message(context, message_locale);
7245 free(code_locale);
7246 free(message_locale);
7247 err = IE_NOEXIST;
7248 goto leave;
7251 /* Warning, not a error */
7252 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7253 char *code_locale = _isds_utf82locale((char*)code);
7254 char *message_locale = _isds_utf82locale((char*)message);
7255 isds_log(ILF_ISDS, ILL_DEBUG,
7256 _("Server truncated response on FindDataBox request "
7257 "(code=%s, message=%s)\n"), code_locale, message_locale);
7258 isds_log_message(context, message_locale);
7259 free(code_locale);
7260 free(message_locale);
7261 truncated = 1;
7264 /* Other error */
7265 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7266 char *code_locale = _isds_utf82locale((char*)code);
7267 char *message_locale = _isds_utf82locale((char*)message);
7268 isds_log(ILF_ISDS, ILL_DEBUG,
7269 _("Server refused FindDataBox request "
7270 "(code=%s, message=%s)\n"), code_locale, message_locale);
7271 isds_log_message(context, message_locale);
7272 free(code_locale);
7273 free(message_locale);
7274 err = IE_ISDS;
7275 goto leave;
7278 xpath_ctx = xmlXPathNewContext(response);
7279 if (!xpath_ctx) {
7280 err = IE_ERROR;
7281 goto leave;
7283 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7284 err = IE_ERROR;
7285 goto leave;
7288 /* Extract boxes if they present */
7289 result = xmlXPathEvalExpression(BAD_CAST
7290 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7291 xpath_ctx);
7292 if (!result) {
7293 err = IE_ERROR;
7294 goto leave;
7296 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7297 struct isds_list *item, *prev_item = NULL;
7298 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7299 item = calloc(1, sizeof(*item));
7300 if (!item) {
7301 err = IE_NOMEM;
7302 goto leave;
7305 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7306 if (i == 0) *boxes = item;
7307 else prev_item->next = item;
7308 prev_item = item;
7310 xpath_ctx->node = result->nodesetval->nodeTab[i];
7311 err = extract_DbOwnerInfo(context,
7312 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7313 if (err) goto leave;
7317 leave:
7318 if (err) {
7319 isds_list_free(boxes);
7320 } else {
7321 if (truncated) err = IE_2BIG;
7324 free(string);
7325 xmlFreeNode(request);
7326 xmlXPathFreeObject(result);
7327 xmlXPathFreeContext(xpath_ctx);
7329 free(code);
7330 free(message);
7331 xmlFreeDoc(response);
7333 if (!err)
7334 isds_log(ILF_ISDS, ILL_DEBUG,
7335 _("FindDataBox request processed by server successfully.\n"));
7336 #else /* not HAVE_LIBCURL */
7337 err = IE_NOTSUP;
7338 #endif
7340 return err;
7344 #if HAVE_LIBCURL
7345 /* Convert a string with match markers into a plain string with list of
7346 * pointers to the matches
7347 * @string is an UTF-8 encoded non-constant string with match markers
7348 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7349 * The markers will be removed from the string.
7350 * @starts is a reallocated list of static pointers into the @string pointing
7351 * to places where match start markers occured.
7352 * @ends is a reallocated list of static pointers into the @string pointing
7353 * to places where match end markers occured.
7354 * @return IE_SUCCESS in case of no failure. */
7355 static isds_error interpret_matches(xmlChar *string,
7356 struct isds_list **starts, struct isds_list **ends) {
7357 isds_error err = IE_SUCCESS;
7358 xmlChar *pointer, *destination, *source;
7359 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7361 isds_list_free(starts);
7362 isds_list_free(ends);
7363 if (NULL == starts || NULL == ends) return IE_INVAL;
7364 if (NULL == string) return IE_SUCCESS;
7366 for (pointer = string; *pointer != '\0';) {
7367 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7368 /* Remove the start marker */
7369 for (source = pointer + 14, destination = pointer;
7370 *source != '\0'; source++, destination++) {
7371 *destination = *source;
7373 *destination = '\0';
7374 /* Append the pointer into the list */
7375 item = calloc(1, sizeof(*item));
7376 if (!item) {
7377 err = IE_NOMEM;
7378 goto leave;
7380 item->destructor = (void (*)(void **))NULL;
7381 item->data = pointer;
7382 if (NULL == prev_start) *starts = item;
7383 else prev_start->next = item;
7384 prev_start = item;
7385 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7386 /* Remove the end marker */
7387 for (source = pointer + 12, destination = pointer;
7388 *source != '\0'; source++, destination++) {
7389 *destination = *source;
7391 *destination = '\0';
7392 /* Append the pointer into the list */
7393 item = calloc(1, sizeof(*item));
7394 if (!item) {
7395 err = IE_NOMEM;
7396 goto leave;
7398 item->destructor = (void (*)(void **))NULL;
7399 item->data = pointer;
7400 if (NULL == prev_end) *ends = item;
7401 else prev_end->next = item;
7402 prev_end = item;
7403 } else {
7404 pointer++;
7408 leave:
7409 if (err) {
7410 isds_list_free(starts);
7411 isds_list_free(ends);
7413 return err;
7417 /* Convert isds:dbResult XML tree into structure
7418 * @context is ISDS context.
7419 * @fulltext_result is automatically reallocated found box structure.
7420 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7421 * @collect_matches is true to interpret match markers.
7422 * In case of error @result will be freed. */
7423 static isds_error extract_dbResult(struct isds_ctx *context,
7424 struct isds_fulltext_result **fulltext_result,
7425 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7426 isds_error err = IE_SUCCESS;
7427 xmlXPathObjectPtr result = NULL;
7428 char *string = NULL;
7430 if (NULL == context) return IE_INVALID_CONTEXT;
7431 if (NULL == fulltext_result) return IE_INVAL;
7432 isds_fulltext_result_free(fulltext_result);
7433 if (!xpath_ctx) return IE_INVAL;
7436 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7437 if (NULL == *fulltext_result) {
7438 err = IE_NOMEM;
7439 goto leave;
7442 /* Extract data */
7443 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7445 EXTRACT_STRING("isds:dbType", string);
7446 if (NULL == string) {
7447 err = IE_ISDS;
7448 isds_log_message(context, _("Empty isds:dbType element"));
7449 goto leave;
7451 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7452 if (err) {
7453 if (err == IE_ENUM) {
7454 err = IE_ISDS;
7455 char *string_locale = _isds_utf82locale(string);
7456 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7457 string_locale);
7458 free(string_locale);
7460 goto leave;
7462 zfree(string);
7464 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7465 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7467 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7468 if (err) goto leave;
7470 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7471 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7472 (*fulltext_result)->dbEffectiveOVM);
7474 EXTRACT_STRING("isds:dbSendOptions", string);
7475 if (NULL == string) {
7476 err = IE_ISDS;
7477 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7478 goto leave;
7480 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7481 (*fulltext_result)->active = 1;
7482 (*fulltext_result)->public_sending = 1;
7483 (*fulltext_result)->commercial_sending = 0;
7484 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7485 (*fulltext_result)->active = 1;
7486 (*fulltext_result)->public_sending = 1;
7487 (*fulltext_result)->commercial_sending = 1;
7488 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7489 (*fulltext_result)->active = 1;
7490 (*fulltext_result)->public_sending = 0;
7491 (*fulltext_result)->commercial_sending = 0;
7492 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7493 (*fulltext_result)->active = 1;
7494 (*fulltext_result)->public_sending = 0;
7495 (*fulltext_result)->commercial_sending = 0;
7496 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7497 (*fulltext_result)->active = 0;
7498 (*fulltext_result)->public_sending = 0;
7499 (*fulltext_result)->commercial_sending = 0;
7500 } else {
7501 err = IE_ISDS;
7502 char *string_locale = _isds_utf82locale(string);
7503 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7504 string_locale);
7505 free(string_locale);
7506 goto leave;
7508 zfree(string);
7510 /* Interpret match marks */
7511 if (collect_matches) {
7512 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7513 &((*fulltext_result)->name_match_start),
7514 &((*fulltext_result)->name_match_end));
7515 if (err) goto leave;
7516 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7517 &((*fulltext_result)->address_match_start),
7518 &((*fulltext_result)->address_match_end));
7519 if (err) goto leave;
7522 leave:
7523 if (err) isds_fulltext_result_free(fulltext_result);
7524 free(string);
7525 xmlXPathFreeObject(result);
7526 return err;
7528 #endif /* HAVE_LIBCURL */
7531 /* Find boxes matching a given full-text criteria.
7532 * @context is a session context
7533 * @query is a non-empty string which consists of words to search
7534 * @target selects box attributes to search for @query words. Pass NULL if you
7535 * don't care.
7536 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7537 * to search in all box types. Pass NULL to let server to use default value
7538 * which is DBTYPE_SYSTEM.
7539 * @page_size defines count of boxes to constitute a response page. It counts
7540 * from zero. Pass NULL to let server to use a default value (50 now).
7541 * @page_number defines ordinar number of the response page to return. It
7542 * counts from zero. Pass NULL to let server to use a default value (0 now).
7543 * @track_matches points to true for marking @query words found in the box
7544 * attributes. It points to false for not marking. Pass NULL to let the server
7545 * to use default value (false now).
7546 * @total_matching_boxes outputs reallocated number of all boxes matching the
7547 * query. Will be pointer to NULL if server did not provide the value.
7548 * Pass NULL if you don't care.
7549 * @current_page_beginning outputs reallocated ordinar number of the first box
7550 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7551 * server did not provide the value. Pass NULL if you don't care.
7552 * @current_page_size outputs reallocated count of boxes in the this @boxes
7553 * page. It will be pointer to NULL if the server did not provide the value. Pass
7554 * NULL if you don't care.
7555 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7556 * is the last one, false if more boxes match, NULL if the server did not
7557 * provude the value. Pass NULL if you don't care.
7558 * @boxes outputs reallocated list of isds_fulltext_result structures,
7559 * possibly empty.
7560 * @return:
7561 * IE_SUCCESS if search succeeded
7562 * IE_2BIG if @page_size is too large
7563 * other code if something bad happens; output arguments will be NULL. */
7564 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7565 const char *query,
7566 const isds_fulltext_target *target,
7567 const isds_DbType *box_type,
7568 const unsigned long int *page_size,
7569 const unsigned long int *page_number,
7570 const _Bool *track_matches,
7571 unsigned long int **total_matching_boxes,
7572 unsigned long int **current_page_beginning,
7573 unsigned long int **current_page_size,
7574 _Bool **last_page,
7575 struct isds_list **boxes) {
7576 isds_error err = IE_SUCCESS;
7577 #if HAVE_LIBCURL
7578 xmlNsPtr isds_ns = NULL;
7579 xmlNodePtr request = NULL;
7580 xmlDocPtr response = NULL;
7581 xmlNodePtr node;
7582 xmlXPathContextPtr xpath_ctx = NULL;
7583 xmlXPathObjectPtr result = NULL;
7584 const xmlChar *static_string = NULL;
7585 xmlChar *string = NULL;
7587 const xmlChar *codes[] = {
7588 BAD_CAST "1004",
7589 BAD_CAST "1152",
7590 BAD_CAST "1153",
7591 BAD_CAST "1154",
7592 BAD_CAST "1155",
7593 BAD_CAST "1156",
7594 BAD_CAST "9002",
7595 NULL
7597 const char *meanings[] = {
7598 N_("You are not allowed to perform the search"),
7599 N_("The query string is empty"),
7600 N_("Searched box ID is malformed"),
7601 N_("Searched organization ID is malformed"),
7602 N_("Invalid input"),
7603 N_("Requested page size is too large"),
7604 N_("Search engine internal error")
7606 const isds_error errors[] = {
7607 IE_ISDS,
7608 IE_INVAL,
7609 IE_INVAL,
7610 IE_INVAL,
7611 IE_INVAL,
7612 IE_2BIG,
7613 IE_ISDS
7615 struct code_map_isds_error map = {
7616 .codes = codes,
7617 .meanings = meanings,
7618 .errors = errors
7620 #endif
7623 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7624 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7625 if (NULL != current_page_size) zfree(*current_page_size);
7626 if (NULL != last_page) zfree(*last_page);
7627 isds_list_free(boxes);
7629 if (NULL == context) return IE_INVALID_CONTEXT;
7630 zfree(context->long_message);
7632 if (NULL == boxes) return IE_INVAL;
7634 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
7635 isds_log_message(context, _("Query string must be non-empty"));
7636 return IE_INVAL;
7639 #if HAVE_LIBCURL
7640 /* Check if connection is established
7641 * TODO: This check should be done downstairs. */
7642 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7644 /* Build FindDataBox request */
7645 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
7646 if (NULL == request) {
7647 isds_log_message(context,
7648 _("Could build ISDSSearch2 request"));
7649 return IE_ERROR;
7651 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7652 if(NULL == isds_ns) {
7653 isds_log_message(context, _("Could not create ISDS name space"));
7654 xmlFreeNode(request);
7655 return IE_ERROR;
7657 xmlSetNs(request, isds_ns);
7659 INSERT_STRING(request, "searchText", query);
7661 if (NULL != target) {
7662 static_string = isds_fulltext_target2string(*(target));
7663 if (NULL == static_string) {
7664 isds_printf_message(context, _("Invalid target value: %d"),
7665 *(target));
7666 err = IE_ENUM;
7667 goto leave;
7670 INSERT_STRING(request, "searchType", static_string);
7671 static_string = NULL;
7673 if (NULL != box_type) {
7674 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7675 if (DBTYPE_SYSTEM == *box_type) {
7676 static_string = BAD_CAST "ALL";
7677 } else {
7678 static_string = isds_DbType2string(*(box_type));
7679 if (NULL == static_string) {
7680 isds_printf_message(context, _("Invalid box type value: %d"),
7681 *(box_type));
7682 err = IE_ENUM;
7683 goto leave;
7687 INSERT_STRING(request, "searchScope", static_string);
7688 static_string = NULL;
7690 INSERT_ULONGINT(request, "page", page_number, string);
7691 INSERT_ULONGINT(request, "pageSize", page_size, string);
7692 INSERT_BOOLEAN(request, "highlighting", track_matches);
7694 /* Send request and check response */
7695 err = send_destroy_request_check_response(context,
7696 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
7697 &request, &response, NULL, &map);
7698 if (err) goto leave;
7700 /* Parse response */
7701 xpath_ctx = xmlXPathNewContext(response);
7702 if (NULL == xpath_ctx) {
7703 err = IE_ERROR;
7704 goto leave;
7706 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7707 err = IE_ERROR;
7708 goto leave;
7710 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
7711 xpath_ctx);
7712 if (!result) {
7713 err = IE_ERROR;
7714 goto leave;
7716 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7717 isds_log_message(context, _("Missing ISDSSearch2 element"));
7718 err = IE_ISDS;
7719 goto leave;
7721 if (result->nodesetval->nodeNr > 1) {
7722 isds_log_message(context, _("Multiple ISDSSearch2 element"));
7723 err = IE_ISDS;
7724 goto leave;
7726 xpath_ctx->node = result->nodesetval->nodeTab[0];
7727 xmlXPathFreeObject(result); result = NULL;
7730 /* Extract counters */
7731 if (NULL != total_matching_boxes) {
7732 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
7734 if (NULL != current_page_size) {
7735 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
7737 if (NULL != current_page_beginning) {
7738 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
7740 if (NULL != last_page) {
7741 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
7743 xmlXPathFreeObject(result); result = NULL;
7745 /* Extract boxes if they present */
7746 result = xmlXPathEvalExpression(BAD_CAST
7747 "isds:dbResults/isds:dbResult", xpath_ctx);
7748 if (NULL == result) {
7749 err = IE_ERROR;
7750 goto leave;
7752 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7753 struct isds_list *item, *prev_item = NULL;
7754 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7755 item = calloc(1, sizeof(*item));
7756 if (!item) {
7757 err = IE_NOMEM;
7758 goto leave;
7761 item->destructor = (void (*)(void **))isds_fulltext_result_free;
7762 if (i == 0) *boxes = item;
7763 else prev_item->next = item;
7764 prev_item = item;
7766 xpath_ctx->node = result->nodesetval->nodeTab[i];
7767 err = extract_dbResult(context,
7768 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
7769 (NULL == track_matches) ? 0 : *track_matches);
7770 if (err) goto leave;
7774 leave:
7775 if (err) {
7776 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7777 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7778 if (NULL != current_page_size) zfree(*current_page_size);
7779 if (NULL != last_page) zfree(*last_page);
7780 isds_list_free(boxes);
7783 free(string);
7784 xmlFreeNode(request);
7785 xmlXPathFreeObject(result);
7786 xmlXPathFreeContext(xpath_ctx);
7787 xmlFreeDoc(response);
7789 if (!err)
7790 isds_log(ILF_ISDS, ILL_DEBUG,
7791 _("ISDSSearch2 request processed by server successfully.\n"));
7792 #else /* not HAVE_LIBCURL */
7793 err = IE_NOTSUP;
7794 #endif
7796 return err;
7800 /* Get status of a box.
7801 * @context is ISDS session context.
7802 * @box_id is UTF-8 encoded box identifier as zero terminated string
7803 * @box_status is return value of box status.
7804 * @return:
7805 * IE_SUCCESS if box has been found and its status retrieved
7806 * IE_NOEXIST if box is not known to ISDS server
7807 * or other appropriate error.
7808 * You can use isds_DbState to enumerate box status. However out of enum
7809 * range value can be returned too. This is feature because ISDS
7810 * specification leaves the set of values open.
7811 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7812 * the box has been deleted, but ISDS still lists its former existence. */
7813 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7814 long int *box_status) {
7815 isds_error err = IE_SUCCESS;
7816 #if HAVE_LIBCURL
7817 xmlNsPtr isds_ns = NULL;
7818 xmlNodePtr request = NULL, db_id;
7819 xmlDocPtr response = NULL;
7820 xmlXPathContextPtr xpath_ctx = NULL;
7821 xmlXPathObjectPtr result = NULL;
7822 xmlChar *string = NULL;
7824 const xmlChar *codes[] = {
7825 BAD_CAST "5001",
7826 BAD_CAST "1007",
7827 BAD_CAST "2011",
7828 NULL
7830 const char *meanings[] = {
7831 "The box does not exist",
7832 "Box ID is malformed",
7833 "Box ID malformed",
7835 const isds_error errors[] = {
7836 IE_NOEXIST,
7837 IE_INVAL,
7838 IE_INVAL,
7840 struct code_map_isds_error map = {
7841 .codes = codes,
7842 .meanings = meanings,
7843 .errors = errors
7845 #endif
7847 if (!context) return IE_INVALID_CONTEXT;
7848 zfree(context->long_message);
7849 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7851 #if HAVE_LIBCURL
7852 /* Check if connection is established
7853 * TODO: This check should be done downstairs. */
7854 if (!context->curl) return IE_CONNECTION_CLOSED;
7857 /* Build CheckDataBox request */
7858 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7859 if (!request) {
7860 isds_log_message(context,
7861 _("Could build CheckDataBox request"));
7862 return IE_ERROR;
7864 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7865 if(!isds_ns) {
7866 isds_log_message(context, _("Could not create ISDS name space"));
7867 xmlFreeNode(request);
7868 return IE_ERROR;
7870 xmlSetNs(request, isds_ns);
7871 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7872 if (!db_id) {
7873 isds_log_message(context, _("Could not add dbID child to "
7874 "CheckDataBox element"));
7875 xmlFreeNode(request);
7876 return IE_ERROR;
7880 /* Send request and check response*/
7881 err = send_destroy_request_check_response(context,
7882 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7883 &request, &response, NULL, &map);
7884 if (err) goto leave;
7887 /* Extract data */
7888 xpath_ctx = xmlXPathNewContext(response);
7889 if (!xpath_ctx) {
7890 err = IE_ERROR;
7891 goto leave;
7893 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7894 err = IE_ERROR;
7895 goto leave;
7897 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7898 xpath_ctx);
7899 if (!result) {
7900 err = IE_ERROR;
7901 goto leave;
7903 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7904 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7905 err = IE_ISDS;
7906 goto leave;
7908 if (result->nodesetval->nodeNr > 1) {
7909 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7910 err = IE_ISDS;
7911 goto leave;
7913 xpath_ctx->node = result->nodesetval->nodeTab[0];
7914 xmlXPathFreeObject(result); result = NULL;
7916 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7919 leave:
7920 free(string);
7921 xmlXPathFreeObject(result);
7922 xmlXPathFreeContext(xpath_ctx);
7924 xmlFreeDoc(response);
7926 if (!err)
7927 isds_log(ILF_ISDS, ILL_DEBUG,
7928 _("CheckDataBox request processed by server successfully.\n"));
7929 #else /* not HAVE_LIBCURL */
7930 err = IE_NOTSUP;
7931 #endif
7933 return err;
7937 /* Get list of permissions to send commercial messages.
7938 * @context is ISDS session context.
7939 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7940 * @permissions is a reallocated list of permissions (struct
7941 * isds_commercial_permission*) to send commercial messages from @box_id. The
7942 * order of permissions is significant as the server applies the permissions
7943 * and associated pre-paid credits in the order. Empty list means no
7944 * permission.
7945 * @return:
7946 * IE_SUCCESS if the list has been obtained correctly,
7947 * or other appropriate error. */
7948 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7949 const char *box_id, struct isds_list **permissions) {
7950 isds_error err = IE_SUCCESS;
7951 #if HAVE_LIBCURL
7952 xmlDocPtr response = NULL;
7953 xmlXPathContextPtr xpath_ctx = NULL;
7954 xmlXPathObjectPtr result = NULL;
7955 #endif
7957 if (!context) return IE_INVALID_CONTEXT;
7958 zfree(context->long_message);
7959 if (NULL == permissions) return IE_INVAL;
7960 isds_list_free(permissions);
7961 if (NULL == box_id) return IE_INVAL;
7963 #if HAVE_LIBCURL
7964 /* Check if connection is established */
7965 if (!context->curl) return IE_CONNECTION_CLOSED;
7967 /* Do request and check for success */
7968 err = build_send_dbid_request_check_response(context,
7969 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7970 BAD_CAST box_id, NULL, &response, NULL);
7971 if (!err) {
7972 isds_log(ILF_ISDS, ILL_DEBUG,
7973 _("PDZInfo request processed by server successfully.\n"));
7976 /* Extract data */
7977 /* Prepare structure */
7978 xpath_ctx = xmlXPathNewContext(response);
7979 if (!xpath_ctx) {
7980 err = IE_ERROR;
7981 goto leave;
7983 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7984 err = IE_ERROR;
7985 goto leave;
7988 /* Set context node */
7989 result = xmlXPathEvalExpression(BAD_CAST
7990 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7991 xpath_ctx);
7992 if (!result) {
7993 err = IE_ERROR;
7994 goto leave;
7996 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7997 struct isds_list *prev_item = NULL;
7999 /* Iterate over all permission records */
8000 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
8001 struct isds_list *item;
8003 /* Prepare structure */
8004 item = calloc(1, sizeof(*item));
8005 if (!item) {
8006 err = IE_NOMEM;
8007 goto leave;
8009 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8010 if (i == 0) *permissions = item;
8011 else prev_item->next = item;
8012 prev_item = item;
8014 /* Extract it */
8015 xpath_ctx->node = result->nodesetval->nodeTab[i];
8016 err = extract_DbPDZRecord(context,
8017 (struct isds_commercial_permission **) (&item->data),
8018 xpath_ctx);
8019 if (err) goto leave;
8023 leave:
8024 if (err) {
8025 isds_list_free(permissions);
8028 xmlXPathFreeObject(result);
8029 xmlXPathFreeContext(xpath_ctx);
8030 xmlFreeDoc(response);
8032 #else /* not HAVE_LIBCURL */
8033 err = IE_NOTSUP;
8034 #endif
8036 return err;
8040 /* Get details about credit for sending pre-paid commercial messages.
8041 * @context is ISDS session context.
8042 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8043 * @from_date is first day of credit history to return in @history. Only
8044 * tm_year, tm_mon and tm_mday carry sane value.
8045 * @to_date is last day of credit history to return in @history. Only
8046 * tm_year, tm_mon and tm_mday carry sane value.
8047 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8048 * if you don't care. This and all other credit values are integers in
8049 * hundredths of Czech Crowns.
8050 * @email outputs notification e-mail address where notifications about credit
8051 * are sent. This is automatically reallocated string. Pass NULL if you don't
8052 * care. It can return NULL if no address is defined.
8053 * @history outputs auto-reallocated list of pointers to struct
8054 * isds_credit_event. Events in closed interval @from_time to @to_time are
8055 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8056 * are sorted by time.
8057 * @return:
8058 * IE_SUCCESS if the credit details have been obtained correctly,
8059 * or other appropriate error. Please note that server allows to retrieve
8060 * only limited history of events. */
8061 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8062 const char *box_id,
8063 const struct tm *from_date, const struct tm *to_date,
8064 long int *credit, char **email, struct isds_list **history) {
8065 isds_error err = IE_SUCCESS;
8066 #if HAVE_LIBCURL
8067 char *box_id_locale = NULL;
8068 xmlNodePtr request = NULL, node;
8069 xmlNsPtr isds_ns = NULL;
8070 xmlChar *string = NULL;
8072 xmlDocPtr response = NULL;
8073 xmlXPathContextPtr xpath_ctx = NULL;
8074 xmlXPathObjectPtr result = NULL;
8076 const xmlChar *codes[] = {
8077 BAD_CAST "1004",
8078 BAD_CAST "2011",
8079 BAD_CAST "1093",
8080 BAD_CAST "1137",
8081 BAD_CAST "1058",
8082 NULL
8084 const char *meanings[] = {
8085 "Insufficient priviledges for the box",
8086 "The box does not exist",
8087 "Date is too long (history is not available after 15 months)",
8088 "Interval is too long (limit is 3 months)",
8089 "Invalid date"
8091 const isds_error errors[] = {
8092 IE_ISDS,
8093 IE_NOEXIST,
8094 IE_DATE,
8095 IE_DATE,
8096 IE_DATE,
8098 struct code_map_isds_error map = {
8099 .codes = codes,
8100 .meanings = meanings,
8101 .errors = errors
8103 #endif
8105 if (!context) return IE_INVALID_CONTEXT;
8106 zfree(context->long_message);
8108 /* Free output argument */
8109 if (NULL != credit) *credit = 0;
8110 if (NULL != email) zfree(*email);
8111 isds_list_free(history);
8113 if (NULL == box_id) return IE_INVAL;
8115 #if HAVE_LIBCURL
8116 /* Check if connection is established */
8117 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8119 box_id_locale = _isds_utf82locale((char*)box_id);
8120 if (NULL == box_id_locale) {
8121 err = IE_NOMEM;
8122 goto leave;
8125 /* Build request */
8126 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8127 if (NULL == request) {
8128 isds_printf_message(context,
8129 _("Could not build DataBoxCreditInfo request for %s box"),
8130 box_id_locale);
8131 err = IE_ERROR;
8132 goto leave;
8134 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8135 if(!isds_ns) {
8136 isds_log_message(context, _("Could not create ISDS name space"));
8137 err = IE_ERROR;
8138 goto leave;
8140 xmlSetNs(request, isds_ns);
8142 /* Add mandatory XSD:tIdDbInput child */
8143 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8144 /* Add mandatory dates elements with optional values */
8145 if (from_date) {
8146 err = tm2datestring(from_date, &string);
8147 if (err) {
8148 isds_log_message(context,
8149 _("Could not convert `from_date' argument to ISO date "
8150 "string"));
8151 goto leave;
8153 INSERT_STRING(request, "ciFromDate", string);
8154 zfree(string);
8155 } else {
8156 INSERT_STRING(request, "ciFromDate", NULL);
8158 if (to_date) {
8159 err = tm2datestring(to_date, &string);
8160 if (err) {
8161 isds_log_message(context,
8162 _("Could not convert `to_date' argument to ISO date "
8163 "string"));
8164 goto leave;
8166 INSERT_STRING(request, "ciTodate", string);
8167 zfree(string);
8168 } else {
8169 INSERT_STRING(request, "ciTodate", NULL);
8172 /* Send request and check response*/
8173 err = send_destroy_request_check_response(context,
8174 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8175 &request, &response, NULL, &map);
8176 if (err) goto leave;
8179 /* Extract data */
8180 /* Set context to the root */
8181 xpath_ctx = xmlXPathNewContext(response);
8182 if (!xpath_ctx) {
8183 err = IE_ERROR;
8184 goto leave;
8186 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8187 err = IE_ERROR;
8188 goto leave;
8190 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8191 xpath_ctx);
8192 if (!result) {
8193 err = IE_ERROR;
8194 goto leave;
8196 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8197 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8198 err = IE_ISDS;
8199 goto leave;
8201 if (result->nodesetval->nodeNr > 1) {
8202 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8203 err = IE_ISDS;
8204 goto leave;
8206 xpath_ctx->node = result->nodesetval->nodeTab[0];
8207 xmlXPathFreeObject(result); result = NULL;
8209 /* Extract common data */
8210 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8211 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8213 /* Extract records */
8214 if (NULL == history) goto leave;
8215 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8216 xpath_ctx);
8217 if (!result) {
8218 err = IE_ERROR;
8219 goto leave;
8221 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8222 struct isds_list *prev_item = NULL;
8224 /* Iterate over all records */
8225 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
8226 struct isds_list *item;
8228 /* Prepare structure */
8229 item = calloc(1, sizeof(*item));
8230 if (!item) {
8231 err = IE_NOMEM;
8232 goto leave;
8234 item->destructor = (void(*)(void**))isds_credit_event_free;
8235 if (i == 0) *history = item;
8236 else prev_item->next = item;
8237 prev_item = item;
8239 /* Extract it */
8240 xpath_ctx->node = result->nodesetval->nodeTab[i];
8241 err = extract_CiRecord(context,
8242 (struct isds_credit_event **) (&item->data),
8243 xpath_ctx);
8244 if (err) goto leave;
8248 leave:
8249 if (!err) {
8250 isds_log(ILF_ISDS, ILL_DEBUG,
8251 _("DataBoxCreditInfo request processed by server successfully.\n"));
8253 if (err) {
8254 isds_list_free(history);
8255 if (NULL != email) zfree(*email)
8258 free(box_id_locale);
8259 xmlXPathFreeObject(result);
8260 xmlXPathFreeContext(xpath_ctx);
8261 xmlFreeDoc(response);
8263 #else /* not HAVE_LIBCURL */
8264 err = IE_NOTSUP;
8265 #endif
8267 return err;
8271 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8272 * code, destroy response and log success.
8273 * @context is ISDS session context.
8274 * @service_name is name of SERVICE_DB_MANIPULATION service
8275 * @box_id is UTF-8 encoded box identifier as zero terminated string
8276 * @approval is optional external approval of box manipulation
8277 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8278 * NULL, if you don't care. */
8279 static isds_error build_send_manipulationdbid_request_check_drop_response(
8280 struct isds_ctx *context, const xmlChar *service_name,
8281 const xmlChar *box_id, const struct isds_approval *approval,
8282 xmlChar **refnumber) {
8283 isds_error err = IE_SUCCESS;
8284 #if HAVE_LIBCURL
8285 xmlDocPtr response = NULL;
8286 #endif
8288 if (!context) return IE_INVALID_CONTEXT;
8289 zfree(context->long_message);
8290 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8292 #if HAVE_LIBCURL
8293 /* Check if connection is established */
8294 if (!context->curl) return IE_CONNECTION_CLOSED;
8296 /* Do request and check for success */
8297 err = build_send_dbid_request_check_response(context,
8298 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8299 &response, refnumber);
8300 xmlFreeDoc(response);
8302 if (!err) {
8303 char *service_name_locale = _isds_utf82locale((char *) service_name);
8304 isds_log(ILF_ISDS, ILL_DEBUG,
8305 _("%s request processed by server successfully.\n"),
8306 service_name_locale);
8307 free(service_name_locale);
8309 #else /* not HAVE_LIBCURL */
8310 err = IE_NOTSUP;
8311 #endif
8313 return err;
8317 /* Switch box into state where box can receive commercial messages (off by
8318 * default)
8319 * @context is ISDS session context.
8320 * @box_id is UTF-8 encoded box identifier as zero terminated string
8321 * @allow is true for enable, false for disable commercial messages income
8322 * @approval is optional external approval of box manipulation
8323 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8324 * NULL, if you don't care. */
8325 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8326 const char *box_id, const _Bool allow,
8327 const struct isds_approval *approval, char **refnumber) {
8328 return build_send_manipulationdbid_request_check_drop_response(context,
8329 (allow) ? BAD_CAST "SetOpenAddressing" :
8330 BAD_CAST "ClearOpenAddressing",
8331 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8335 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8336 * message acceptance). This is just a box permission. Sender must apply
8337 * such role by sending each message.
8338 * @context is ISDS session context.
8339 * @box_id is UTF-8 encoded box identifier as zero terminated string
8340 * @allow is true for enable, false for disable OVM role permission
8341 * @approval is optional external approval of box manipulation
8342 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8343 * NULL, if you don't care. */
8344 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
8345 const char *box_id, const _Bool allow,
8346 const struct isds_approval *approval, char **refnumber) {
8347 return build_send_manipulationdbid_request_check_drop_response(context,
8348 (allow) ? BAD_CAST "SetEffectiveOVM" :
8349 BAD_CAST "ClearEffectiveOVM",
8350 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8354 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8355 * code, destroy response and log success.
8356 * @context is ISDS session context.
8357 * @service_name is name of SERVICE_DB_MANIPULATION service
8358 * @owner is structure describing box
8359 * @approval is optional external approval of box manipulation
8360 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8361 * NULL, if you don't care. */
8362 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8363 struct isds_ctx *context, const xmlChar *service_name,
8364 const struct isds_DbOwnerInfo *owner,
8365 const struct isds_approval *approval, xmlChar **refnumber) {
8366 isds_error err = IE_SUCCESS;
8367 #if HAVE_LIBCURL
8368 char *service_name_locale = NULL;
8369 xmlNodePtr request = NULL, db_owner_info;
8370 xmlNsPtr isds_ns = NULL;
8371 #endif
8374 if (!context) return IE_INVALID_CONTEXT;
8375 zfree(context->long_message);
8376 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8378 #if HAVE_LIBCURL
8379 service_name_locale = _isds_utf82locale((char*)service_name);
8380 if (!service_name_locale) {
8381 err = IE_NOMEM;
8382 goto leave;
8385 /* Build request */
8386 request = xmlNewNode(NULL, service_name);
8387 if (!request) {
8388 isds_printf_message(context,
8389 _("Could not build %s request"), service_name_locale);
8390 err = IE_ERROR;
8391 goto leave;
8393 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8394 if(!isds_ns) {
8395 isds_log_message(context, _("Could not create ISDS name space"));
8396 err = IE_ERROR;
8397 goto leave;
8399 xmlSetNs(request, isds_ns);
8402 /* Add XSD:tOwnerInfoInput child*/
8403 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8404 err = insert_DbOwnerInfo(context, owner, db_owner_info);
8405 if (err) goto leave;
8407 /* Add XSD:gExtApproval*/
8408 err = insert_GExtApproval(context, approval, request);
8409 if (err) goto leave;
8411 /* Send it to server and process response */
8412 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8413 service_name, &request, refnumber);
8415 leave:
8416 xmlFreeNode(request);
8417 free(service_name_locale);
8418 #else /* not HAVE_LIBCURL */
8419 err = IE_NOTSUP;
8420 #endif
8422 return err;
8426 /* Switch box accessibility state on request of box owner.
8427 * Despite the name, owner must do the request off-line. This function is
8428 * designed for such off-line meeting points (e.g. Czech POINT).
8429 * @context is ISDS session context.
8430 * @box identifies box to switch accessibility state.
8431 * @allow is true for making accessible, false to disallow access.
8432 * @approval is optional external approval of box manipulation
8433 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8434 * NULL, if you don't care. */
8435 isds_error isds_switch_box_accessibility_on_owner_request(
8436 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8437 const _Bool allow, const struct isds_approval *approval,
8438 char **refnumber) {
8439 return build_send_manipulationdbowner_request_check_drop_response(context,
8440 (allow) ? BAD_CAST "EnableOwnDataBox" :
8441 BAD_CAST "DisableOwnDataBox",
8442 box, approval, (xmlChar **) refnumber);
8446 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8447 * date.
8448 * @context is ISDS session context.
8449 * @box identifies box to switch accessibility state.
8450 * @since is date since accessibility has been denied. This can be past too.
8451 * Only tm_year, tm_mon and tm_mday carry sane value.
8452 * @approval is optional external approval of box manipulation
8453 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8454 * NULL, if you don't care. */
8455 isds_error isds_disable_box_accessibility_externaly(
8456 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8457 const struct tm *since, const struct isds_approval *approval,
8458 char **refnumber) {
8459 isds_error err = IE_SUCCESS;
8460 #if HAVE_LIBCURL
8461 char *service_name_locale = NULL;
8462 xmlNodePtr request = NULL, node;
8463 xmlNsPtr isds_ns = NULL;
8464 xmlChar *string = NULL;
8465 #endif
8468 if (!context) return IE_INVALID_CONTEXT;
8469 zfree(context->long_message);
8470 if (!box || !since) return IE_INVAL;
8472 #if HAVE_LIBCURL
8473 /* Build request */
8474 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
8475 if (!request) {
8476 isds_printf_message(context,
8477 _("Could not build %s request"), "DisableDataBoxExternally");
8478 err = IE_ERROR;
8479 goto leave;
8481 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8482 if(!isds_ns) {
8483 isds_log_message(context, _("Could not create ISDS name space"));
8484 err = IE_ERROR;
8485 goto leave;
8487 xmlSetNs(request, isds_ns);
8490 /* Add @box identification */
8491 INSERT_ELEMENT(node, request, "dbOwnerInfo");
8492 err = insert_DbOwnerInfo(context, box, node);
8493 if (err) goto leave;
8495 /* Add @since date */
8496 err = tm2datestring(since, &string);
8497 if(err) {
8498 isds_log_message(context,
8499 _("Could not convert `since' argument to ISO date string"));
8500 goto leave;
8502 INSERT_STRING(request, "dbOwnerDisableDate", string);
8503 zfree(string);
8505 /* Add @approval */
8506 err = insert_GExtApproval(context, approval, request);
8507 if (err) goto leave;
8509 /* Send it to server and process response */
8510 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8511 BAD_CAST "DisableDataBoxExternally", &request,
8512 (xmlChar **) refnumber);
8514 leave:
8515 free(string);
8516 xmlFreeNode(request);
8517 free(service_name_locale);
8518 #else /* not HAVE_LIBCURL */
8519 err = IE_NOTSUP;
8520 #endif
8522 return err;
8526 #if HAVE_LIBCURL
8527 /* Insert struct isds_message data (envelope (recipient data optional) and
8528 * documents into XML tree
8529 * @context is session context
8530 * @outgoing_message is libisds structure with message data
8531 * @create_message is XML CreateMessage or CreateMultipleMessage element
8532 * @process_recipient true for recipient data serialization, false for no
8533 * serialization */
8534 static isds_error insert_envelope_files(struct isds_ctx *context,
8535 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8536 const _Bool process_recipient) {
8538 isds_error err = IE_SUCCESS;
8539 xmlNodePtr envelope, dm_files, node;
8540 xmlChar *string = NULL;
8542 if (!context) return IE_INVALID_CONTEXT;
8543 if (!outgoing_message || !create_message) return IE_INVAL;
8546 /* Build envelope */
8547 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8548 if (!envelope) {
8549 isds_printf_message(context, _("Could not add dmEnvelope child to "
8550 "%s element"), create_message->name);
8551 return IE_ERROR;
8554 if (!outgoing_message->envelope) {
8555 isds_log_message(context, _("Outgoing message is missing envelope"));
8556 err = IE_INVAL;
8557 goto leave;
8560 /* Insert optional message type */
8561 err = insert_message_type(context, outgoing_message->envelope->dmType,
8562 envelope);
8563 if (err) goto leave;
8565 INSERT_STRING(envelope, "dmSenderOrgUnit",
8566 outgoing_message->envelope->dmSenderOrgUnit);
8567 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8568 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8570 if (process_recipient) {
8571 if (!outgoing_message->envelope->dbIDRecipient) {
8572 isds_log_message(context,
8573 _("Outgoing message is missing recipient box identifier"));
8574 err = IE_INVAL;
8575 goto leave;
8577 INSERT_STRING(envelope, "dbIDRecipient",
8578 outgoing_message->envelope->dbIDRecipient);
8580 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8581 outgoing_message->envelope->dmRecipientOrgUnit);
8582 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8583 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8584 INSERT_STRING(envelope, "dmToHands",
8585 outgoing_message->envelope->dmToHands);
8588 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8589 "dmAnnotation");
8590 INSERT_STRING(envelope, "dmAnnotation",
8591 outgoing_message->envelope->dmAnnotation);
8593 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8594 0, 50, "dmRecipientRefNumber");
8595 INSERT_STRING(envelope, "dmRecipientRefNumber",
8596 outgoing_message->envelope->dmRecipientRefNumber);
8598 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8599 0, 50, "dmSenderRefNumber");
8600 INSERT_STRING(envelope, "dmSenderRefNumber",
8601 outgoing_message->envelope->dmSenderRefNumber);
8603 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8604 0, 50, "dmRecipientIdent");
8605 INSERT_STRING(envelope, "dmRecipientIdent",
8606 outgoing_message->envelope->dmRecipientIdent);
8608 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8609 0, 50, "dmSenderIdent");
8610 INSERT_STRING(envelope, "dmSenderIdent",
8611 outgoing_message->envelope->dmSenderIdent);
8613 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8614 outgoing_message->envelope->dmLegalTitleLaw, string);
8615 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8616 outgoing_message->envelope->dmLegalTitleYear, string);
8617 INSERT_STRING(envelope, "dmLegalTitleSect",
8618 outgoing_message->envelope->dmLegalTitleSect);
8619 INSERT_STRING(envelope, "dmLegalTitlePar",
8620 outgoing_message->envelope->dmLegalTitlePar);
8621 INSERT_STRING(envelope, "dmLegalTitlePoint",
8622 outgoing_message->envelope->dmLegalTitlePoint);
8624 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8625 outgoing_message->envelope->dmPersonalDelivery);
8626 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8627 outgoing_message->envelope->dmAllowSubstDelivery);
8629 /* ???: Should we require value for dbEffectiveOVM sender?
8630 * ISDS has default as true */
8631 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8632 INSERT_BOOLEAN(envelope, "dmOVM",
8633 outgoing_message->envelope->dmPublishOwnID);
8636 /* Append dmFiles */
8637 if (!outgoing_message->documents) {
8638 isds_log_message(context,
8639 _("Outgoing message is missing list of documents"));
8640 err = IE_INVAL;
8641 goto leave;
8643 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8644 if (!dm_files) {
8645 isds_printf_message(context, _("Could not add dmFiles child to "
8646 "%s element"), create_message->name);
8647 err = IE_ERROR;
8648 goto leave;
8651 /* Check for document hierarchy */
8652 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8653 if (err) goto leave;
8655 /* Process each document */
8656 for (struct isds_list *item =
8657 (struct isds_list *) outgoing_message->documents;
8658 item; item = item->next) {
8659 if (!item->data) {
8660 isds_log_message(context,
8661 _("List of documents contains empty item"));
8662 err = IE_INVAL;
8663 goto leave;
8665 /* FIXME: Check for dmFileMetaType and for document references.
8666 * Only first document can be of MAIN type */
8667 err = insert_document(context, (struct isds_document*) item->data,
8668 dm_files);
8670 if (err) goto leave;
8673 leave:
8674 free(string);
8675 return err;
8677 #endif /* HAVE_LIBCURL */
8680 /* Send a message via ISDS to a recipient
8681 * @context is session context
8682 * @outgoing_message is message to send; Some members are mandatory (like
8683 * dbIDRecipient), some are optional and some are irrelevant (especially data
8684 * about sender). Included pointer to isds_list documents must contain at
8685 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8686 * members will be filled with valid data from ISDS. Exact list of write
8687 * members is subject to change. Currently dmID is changed.
8688 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8689 isds_error isds_send_message(struct isds_ctx *context,
8690 struct isds_message *outgoing_message) {
8692 isds_error err = IE_SUCCESS;
8693 #if HAVE_LIBCURL
8694 xmlNsPtr isds_ns = NULL;
8695 xmlNodePtr request = NULL;
8696 xmlDocPtr response = NULL;
8697 xmlChar *code = NULL, *message = NULL;
8698 xmlXPathContextPtr xpath_ctx = NULL;
8699 xmlXPathObjectPtr result = NULL;
8700 /*_Bool message_is_complete = 0;*/
8701 #endif
8703 if (!context) return IE_INVALID_CONTEXT;
8704 zfree(context->long_message);
8705 if (!outgoing_message) return IE_INVAL;
8707 #if HAVE_LIBCURL
8708 /* Check if connection is established
8709 * TODO: This check should be done downstairs. */
8710 if (!context->curl) return IE_CONNECTION_CLOSED;
8713 /* Build CreateMessage request */
8714 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8715 if (!request) {
8716 isds_log_message(context,
8717 _("Could not build CreateMessage request"));
8718 return IE_ERROR;
8720 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8721 if(!isds_ns) {
8722 isds_log_message(context, _("Could not create ISDS name space"));
8723 xmlFreeNode(request);
8724 return IE_ERROR;
8726 xmlSetNs(request, isds_ns);
8728 /* Append envelope and files */
8729 err = insert_envelope_files(context, outgoing_message, request, 1);
8730 if (err) goto leave;
8733 /* Signal we can serialize message since now */
8734 /*message_is_complete = 1;*/
8737 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8739 /* Sent request */
8740 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8742 /* Don't' destroy request, we want to provide it to application later */
8744 if (err) {
8745 isds_log(ILF_ISDS, ILL_DEBUG,
8746 _("Processing ISDS response on CreateMessage "
8747 "request failed\n"));
8748 goto leave;
8751 /* Check for response status */
8752 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8753 &code, &message, NULL);
8754 if (err) {
8755 isds_log(ILF_ISDS, ILL_DEBUG,
8756 _("ISDS response on CreateMessage request "
8757 "is missing status\n"));
8758 goto leave;
8761 /* Request processed, but refused by server or server failed */
8762 if (xmlStrcmp(code, BAD_CAST "0000")) {
8763 char *box_id_locale =
8764 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8765 char *code_locale = _isds_utf82locale((char*)code);
8766 char *message_locale = _isds_utf82locale((char*)message);
8767 isds_log(ILF_ISDS, ILL_DEBUG,
8768 _("Server did not accept message for %s on CreateMessage "
8769 "request (code=%s, message=%s)\n"),
8770 box_id_locale, code_locale, message_locale);
8771 isds_log_message(context, message_locale);
8772 free(box_id_locale);
8773 free(code_locale);
8774 free(message_locale);
8775 err = IE_ISDS;
8776 goto leave;
8780 /* Extract data */
8781 xpath_ctx = xmlXPathNewContext(response);
8782 if (!xpath_ctx) {
8783 err = IE_ERROR;
8784 goto leave;
8786 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8787 err = IE_ERROR;
8788 goto leave;
8790 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8791 xpath_ctx);
8792 if (!result) {
8793 err = IE_ERROR;
8794 goto leave;
8796 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8797 isds_log_message(context, _("Missing CreateMessageResponse element"));
8798 err = IE_ISDS;
8799 goto leave;
8801 if (result->nodesetval->nodeNr > 1) {
8802 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8803 err = IE_ISDS;
8804 goto leave;
8806 xpath_ctx->node = result->nodesetval->nodeTab[0];
8807 xmlXPathFreeObject(result); result = NULL;
8809 if (outgoing_message->envelope->dmID) {
8810 free(outgoing_message->envelope->dmID);
8811 outgoing_message->envelope->dmID = NULL;
8813 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8814 if (!outgoing_message->envelope->dmID) {
8815 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8816 "but did not return assigned message ID\n"));
8819 leave:
8820 /* TODO: Serialize message into structure member raw */
8821 /* XXX: Each web service transport message in different format.
8822 * Therefore it's not possible to save them directly.
8823 * To save them, one must figure out common format.
8824 * We can leave it on application, or we can implement the ESS format. */
8825 /*if (message_is_complete) {
8826 if (outgoing_message->envelope->dmID) {
8828 /* Add assigned message ID as first child*/
8829 /*xmlNodePtr dmid_text = xmlNewText(
8830 (xmlChar *) outgoing_message->envelope->dmID);
8831 if (!dmid_text) goto serialization_failed;
8833 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8834 BAD_CAST "dmID");
8835 if (!dmid_element) {
8836 xmlFreeNode(dmid_text);
8837 goto serialization_failed;
8840 xmlNodePtr dmid_element_with_text =
8841 xmlAddChild(dmid_element, dmid_text);
8842 if (!dmid_element_with_text) {
8843 xmlFreeNode(dmid_element);
8844 xmlFreeNode(dmid_text);
8845 goto serialization_failed;
8848 node = xmlAddPrevSibling(envelope->childern,
8849 dmid_element_with_text);
8850 if (!node) {
8851 xmlFreeNodeList(dmid_element_with_text);
8852 goto serialization_failed;
8856 /* Serialize message with ID into raw */
8857 /*buffer = serialize_element(envelope)*/
8858 /* }
8860 serialization_failed:
8864 /* Clean up */
8865 xmlXPathFreeObject(result);
8866 xmlXPathFreeContext(xpath_ctx);
8868 free(code);
8869 free(message);
8870 xmlFreeDoc(response);
8871 xmlFreeNode(request);
8873 if (!err)
8874 isds_log(ILF_ISDS, ILL_DEBUG,
8875 _("CreateMessage request processed by server "
8876 "successfully.\n"));
8877 #else /* not HAVE_LIBCURL */
8878 err = IE_NOTSUP;
8879 #endif
8881 return err;
8885 /* Send a message via ISDS to a multiple recipients
8886 * @context is session context
8887 * @outgoing_message is message to send; Some members are mandatory,
8888 * some are optional and some are irrelevant (especially data
8889 * about sender). Data about recipient will be substituted by ISDS from
8890 * @copies. Included pointer to isds_list documents must
8891 * contain at least one document of FILEMETATYPE_MAIN.
8892 * @copies is list of isds_message_copy structures addressing all desired
8893 * recipients. This is read-write structure, some members will be filled with
8894 * valid data from ISDS (message IDs, error codes, error descriptions).
8895 * @return
8896 * ISDS_SUCCESS if all messages have been sent
8897 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8898 * succeeded messages can be identified by copies->data->error),
8899 * or other error code if something other goes wrong. */
8900 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8901 const struct isds_message *outgoing_message,
8902 struct isds_list *copies) {
8904 isds_error err = IE_SUCCESS;
8905 #if HAVE_LIBCURL
8906 isds_error append_err;
8907 xmlNsPtr isds_ns = NULL;
8908 xmlNodePtr request = NULL, recipients, recipient, node;
8909 struct isds_list *item;
8910 struct isds_message_copy *copy;
8911 xmlDocPtr response = NULL;
8912 xmlChar *code = NULL, *message = NULL;
8913 xmlXPathContextPtr xpath_ctx = NULL;
8914 xmlXPathObjectPtr result = NULL;
8915 xmlChar *string = NULL;
8916 int i;
8917 #endif
8919 if (!context) return IE_INVALID_CONTEXT;
8920 zfree(context->long_message);
8921 if (!outgoing_message || !copies) return IE_INVAL;
8923 #if HAVE_LIBCURL
8924 /* Check if connection is established
8925 * TODO: This check should be done downstairs. */
8926 if (!context->curl) return IE_CONNECTION_CLOSED;
8929 /* Build CreateMultipleMessage request */
8930 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
8931 if (!request) {
8932 isds_log_message(context,
8933 _("Could not build CreateMultipleMessage request"));
8934 return IE_ERROR;
8936 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8937 if(!isds_ns) {
8938 isds_log_message(context, _("Could not create ISDS name space"));
8939 xmlFreeNode(request);
8940 return IE_ERROR;
8942 xmlSetNs(request, isds_ns);
8945 /* Build recipients */
8946 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8947 if (!recipients) {
8948 isds_log_message(context, _("Could not add dmRecipients child to "
8949 "CreateMultipleMessage element"));
8950 xmlFreeNode(request);
8951 return IE_ERROR;
8954 /* Insert each recipient */
8955 for (item = copies; item; item = item->next) {
8956 copy = (struct isds_message_copy *) item->data;
8957 if (!copy) {
8958 isds_log_message(context,
8959 _("`copies' list item contains empty data"));
8960 err = IE_INVAL;
8961 goto leave;
8964 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
8965 if (!recipient) {
8966 isds_log_message(context, _("Could not add dmRecipient child to "
8967 "dmRecipients element"));
8968 err = IE_ERROR;
8969 goto leave;
8972 if (!copy->dbIDRecipient) {
8973 isds_log_message(context,
8974 _("Message copy is missing recipient box identifier"));
8975 err = IE_INVAL;
8976 goto leave;
8978 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
8979 INSERT_STRING(recipient, "dmRecipientOrgUnit",
8980 copy->dmRecipientOrgUnit);
8981 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
8982 copy->dmRecipientOrgUnitNum, string);
8983 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
8986 /* Append envelope and files */
8987 err = insert_envelope_files(context, outgoing_message, request, 0);
8988 if (err) goto leave;
8991 isds_log(ILF_ISDS, ILL_DEBUG,
8992 _("Sending CreateMultipleMessage request to ISDS\n"));
8994 /* Sent request */
8995 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8996 if (err) {
8997 isds_log(ILF_ISDS, ILL_DEBUG,
8998 _("Processing ISDS response on CreateMultipleMessage "
8999 "request failed\n"));
9000 goto leave;
9003 /* Check for response status */
9004 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9005 &code, &message, NULL);
9006 if (err) {
9007 isds_log(ILF_ISDS, ILL_DEBUG,
9008 _("ISDS response on CreateMultipleMessage request "
9009 "is missing status\n"));
9010 goto leave;
9013 /* Request processed, but some copies failed */
9014 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9015 char *box_id_locale =
9016 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9017 char *code_locale = _isds_utf82locale((char*)code);
9018 char *message_locale = _isds_utf82locale((char*)message);
9019 isds_log(ILF_ISDS, ILL_DEBUG,
9020 _("Server did accept message for multiple recipients "
9021 "on CreateMultipleMessage request but delivery to "
9022 "some of them failed (code=%s, message=%s)\n"),
9023 box_id_locale, code_locale, message_locale);
9024 isds_log_message(context, message_locale);
9025 free(box_id_locale);
9026 free(code_locale);
9027 free(message_locale);
9028 err = IE_PARTIAL_SUCCESS;
9031 /* Request refused by server as whole */
9032 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9033 char *box_id_locale =
9034 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9035 char *code_locale = _isds_utf82locale((char*)code);
9036 char *message_locale = _isds_utf82locale((char*)message);
9037 isds_log(ILF_ISDS, ILL_DEBUG,
9038 _("Server did not accept message for multiple recipients "
9039 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9040 box_id_locale, code_locale, message_locale);
9041 isds_log_message(context, message_locale);
9042 free(box_id_locale);
9043 free(code_locale);
9044 free(message_locale);
9045 err = IE_ISDS;
9046 goto leave;
9050 /* Extract data */
9051 xpath_ctx = xmlXPathNewContext(response);
9052 if (!xpath_ctx) {
9053 err = IE_ERROR;
9054 goto leave;
9056 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9057 err = IE_ERROR;
9058 goto leave;
9060 result = xmlXPathEvalExpression(
9061 BAD_CAST "/isds:CreateMultipleMessageResponse"
9062 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9063 xpath_ctx);
9064 if (!result) {
9065 err = IE_ERROR;
9066 goto leave;
9068 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9069 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9070 err = IE_ISDS;
9071 goto leave;
9074 /* Extract message ID and delivery status for each copy */
9075 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9076 item = item->next, i++) {
9077 copy = (struct isds_message_copy *) item->data;
9078 xpath_ctx->node = result->nodesetval->nodeTab[i];
9080 append_err = append_TMStatus(context, copy, xpath_ctx);
9081 if (append_err) {
9082 err = append_err;
9083 goto leave;
9086 if (item || i < result->nodesetval->nodeNr) {
9087 isds_printf_message(context, _("ISDS returned unexpected number of "
9088 "message copy delivery states: %d"),
9089 result->nodesetval->nodeNr);
9090 err = IE_ISDS;
9091 goto leave;
9095 leave:
9096 /* Clean up */
9097 free(string);
9098 xmlXPathFreeObject(result);
9099 xmlXPathFreeContext(xpath_ctx);
9101 free(code);
9102 free(message);
9103 xmlFreeDoc(response);
9104 xmlFreeNode(request);
9106 if (!err)
9107 isds_log(ILF_ISDS, ILL_DEBUG,
9108 _("CreateMultipleMessageResponse request processed by server "
9109 "successfully.\n"));
9110 #else /* not HAVE_LIBCURL */
9111 err = IE_NOTSUP;
9112 #endif
9114 return err;
9118 /* Get list of messages. This is common core for getting sent or received
9119 * messages.
9120 * Any criterion argument can be NULL, if you don't care about it.
9121 * @context is session context. Must not be NULL.
9122 * @outgoing_direction is true if you want list of outgoing messages,
9123 * it's false if you want incoming messages.
9124 * @from_time is minimal time and date of message sending inclusive.
9125 * @to_time is maximal time and date of message sending inclusive
9126 * @organization_unit_number is number of sender/recipient respectively.
9127 * @status_filter is bit field of isds_message_status values. Use special
9128 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9129 * all values, you can use bit-wise arithmetic if you want.)
9130 * @offset is index of first message we are interested in. First message is 1.
9131 * Set to 0 (or 1) if you don't care.
9132 * @number is maximal length of list you want to get as input value, outputs
9133 * number of messages matching these criteria. Can be NULL if you don't care
9134 * (applies to output value either).
9135 * @messages is automatically reallocated list of isds_message's. Be ware that
9136 * it returns only brief overview (envelope and some other fields) about each
9137 * message, not the complete message. FIXME: Specify exact fields.
9138 * The list is sorted by delivery time in ascending order.
9139 * Use NULL if you don't care about don't need the data (useful if you want to
9140 * know only the @number). If you provide &NULL, list will be allocated on
9141 * heap, if you provide pointer to non-NULL, list will be freed automatically
9142 * at first. Also in case of error the list will be NULLed.
9143 * @return IE_SUCCESS or appropriate error code. */
9144 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9145 _Bool outgoing_direction,
9146 const struct timeval *from_time, const struct timeval *to_time,
9147 const long int *organization_unit_number,
9148 const unsigned int status_filter,
9149 const unsigned long int offset, unsigned long int *number,
9150 struct isds_list **messages) {
9152 isds_error err = IE_SUCCESS;
9153 #if HAVE_LIBCURL
9154 xmlNsPtr isds_ns = NULL;
9155 xmlNodePtr request = NULL, node;
9156 xmlDocPtr response = NULL;
9157 xmlChar *code = NULL, *message = NULL;
9158 xmlXPathContextPtr xpath_ctx = NULL;
9159 xmlXPathObjectPtr result = NULL;
9160 xmlChar *string = NULL;
9161 long unsigned int count = 0;
9162 #endif
9164 if (!context) return IE_INVALID_CONTEXT;
9165 zfree(context->long_message);
9167 /* Free former message list if any */
9168 if (messages) isds_list_free(messages);
9170 #if HAVE_LIBCURL
9171 /* Check if connection is established
9172 * TODO: This check should be done downstairs. */
9173 if (!context->curl) return IE_CONNECTION_CLOSED;
9175 /* Build GetListOf*Messages request */
9176 request = xmlNewNode(NULL,
9177 (outgoing_direction) ?
9178 BAD_CAST "GetListOfSentMessages" :
9179 BAD_CAST "GetListOfReceivedMessages"
9181 if (!request) {
9182 isds_log_message(context,
9183 (outgoing_direction) ?
9184 _("Could not build GetListOfSentMessages request") :
9185 _("Could not build GetListOfReceivedMessages request")
9187 return IE_ERROR;
9189 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9190 if(!isds_ns) {
9191 isds_log_message(context, _("Could not create ISDS name space"));
9192 xmlFreeNode(request);
9193 return IE_ERROR;
9195 xmlSetNs(request, isds_ns);
9198 if (from_time) {
9199 err = timeval2timestring(from_time, &string);
9200 if (err) goto leave;
9202 INSERT_STRING(request, "dmFromTime", string);
9203 free(string); string = NULL;
9205 if (to_time) {
9206 err = timeval2timestring(to_time, &string);
9207 if (err) goto leave;
9209 INSERT_STRING(request, "dmToTime", string);
9210 free(string); string = NULL;
9212 if (outgoing_direction) {
9213 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9214 organization_unit_number, string);
9215 } else {
9216 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9217 organization_unit_number, string);
9220 if (status_filter > MESSAGESTATE_ANY) {
9221 isds_printf_message(context,
9222 _("Invalid message state filter value: %ld"), status_filter);
9223 err = IE_INVAL;
9224 goto leave;
9226 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9228 if (offset > 0 ) {
9229 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9230 } else {
9231 INSERT_STRING(request, "dmOffset", "1");
9234 /* number 0 means no limit */
9235 if (number && *number == 0) {
9236 INSERT_STRING(request, "dmLimit", NULL);
9237 } else {
9238 INSERT_ULONGINT(request, "dmLimit", number, string);
9242 isds_log(ILF_ISDS, ILL_DEBUG,
9243 (outgoing_direction) ?
9244 _("Sending GetListOfSentMessages request to ISDS\n") :
9245 _("Sending GetListOfReceivedMessages request to ISDS\n")
9248 /* Sent request */
9249 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9250 xmlFreeNode(request); request = NULL;
9252 if (err) {
9253 isds_log(ILF_ISDS, ILL_DEBUG,
9254 (outgoing_direction) ?
9255 _("Processing ISDS response on GetListOfSentMessages "
9256 "request failed\n") :
9257 _("Processing ISDS response on GetListOfReceivedMessages "
9258 "request failed\n")
9260 goto leave;
9263 /* Check for response status */
9264 err = isds_response_status(context, SERVICE_DM_INFO, response,
9265 &code, &message, NULL);
9266 if (err) {
9267 isds_log(ILF_ISDS, ILL_DEBUG,
9268 (outgoing_direction) ?
9269 _("ISDS response on GetListOfSentMessages request "
9270 "is missing status\n") :
9271 _("ISDS response on GetListOfReceivedMessages request "
9272 "is missing status\n")
9274 goto leave;
9277 /* Request processed, but nothing found */
9278 if (xmlStrcmp(code, BAD_CAST "0000")) {
9279 char *code_locale = _isds_utf82locale((char*)code);
9280 char *message_locale = _isds_utf82locale((char*)message);
9281 isds_log(ILF_ISDS, ILL_DEBUG,
9282 (outgoing_direction) ?
9283 _("Server refused GetListOfSentMessages request "
9284 "(code=%s, message=%s)\n") :
9285 _("Server refused GetListOfReceivedMessages request "
9286 "(code=%s, message=%s)\n"),
9287 code_locale, message_locale);
9288 isds_log_message(context, message_locale);
9289 free(code_locale);
9290 free(message_locale);
9291 err = IE_ISDS;
9292 goto leave;
9296 /* Extract data */
9297 xpath_ctx = xmlXPathNewContext(response);
9298 if (!xpath_ctx) {
9299 err = IE_ERROR;
9300 goto leave;
9302 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9303 err = IE_ERROR;
9304 goto leave;
9306 result = xmlXPathEvalExpression(
9307 (outgoing_direction) ?
9308 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9309 "isds:dmRecords/isds:dmRecord" :
9310 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9311 "isds:dmRecords/isds:dmRecord",
9312 xpath_ctx);
9313 if (!result) {
9314 err = IE_ERROR;
9315 goto leave;
9318 /* Fill output arguments in */
9319 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9320 struct isds_envelope *envelope;
9321 struct isds_list *item = NULL, *last_item = NULL;
9323 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9324 /* Create new message */
9325 item = calloc(1, sizeof(*item));
9326 if (!item) {
9327 err = IE_NOMEM;
9328 goto leave;
9330 item->destructor = (void(*)(void**)) &isds_message_free;
9331 item->data = calloc(1, sizeof(struct isds_message));
9332 if (!item->data) {
9333 isds_list_free(&item);
9334 err = IE_NOMEM;
9335 goto leave;
9338 /* Extract envelope data */
9339 xpath_ctx->node = result->nodesetval->nodeTab[count];
9340 envelope = NULL;
9341 err = extract_DmRecord(context, &envelope, xpath_ctx);
9342 if (err) {
9343 isds_list_free(&item);
9344 goto leave;
9347 /* Attach extracted envelope */
9348 ((struct isds_message *) item->data)->envelope = envelope;
9350 /* Append new message into the list */
9351 if (!*messages) {
9352 *messages = last_item = item;
9353 } else {
9354 last_item->next = item;
9355 last_item = item;
9359 if (number) *number = count;
9361 leave:
9362 if (err) {
9363 isds_list_free(messages);
9366 free(string);
9367 xmlXPathFreeObject(result);
9368 xmlXPathFreeContext(xpath_ctx);
9370 free(code);
9371 free(message);
9372 xmlFreeDoc(response);
9373 xmlFreeNode(request);
9375 if (!err)
9376 isds_log(ILF_ISDS, ILL_DEBUG,
9377 (outgoing_direction) ?
9378 _("GetListOfSentMessages request processed by server "
9379 "successfully.\n") :
9380 _("GetListOfReceivedMessages request processed by server "
9381 "successfully.\n")
9383 #else /* not HAVE_LIBCURL */
9384 err = IE_NOTSUP;
9385 #endif
9386 return err;
9390 /* Get list of outgoing (already sent) messages.
9391 * Any criterion argument can be NULL, if you don't care about it.
9392 * @context is session context. Must not be NULL.
9393 * @from_time is minimal time and date of message sending inclusive.
9394 * @to_time is maximal time and date of message sending inclusive
9395 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9396 * @status_filter is bit field of isds_message_status values. Use special
9397 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9398 * all values, you can use bit-wise arithmetic if you want.)
9399 * @offset is index of first message we are interested in. First message is 1.
9400 * Set to 0 (or 1) if you don't care.
9401 * @number is maximal length of list you want to get as input value, outputs
9402 * number of messages matching these criteria. Can be NULL if you don't care
9403 * (applies to output value either).
9404 * @messages is automatically reallocated list of isds_message's. Be ware that
9405 * it returns only brief overview (envelope and some other fields) about each
9406 * message, not the complete message. FIXME: Specify exact fields.
9407 * The list is sorted by delivery time in ascending order.
9408 * Use NULL if you don't care about the meta data (useful if you want to know
9409 * only the @number). If you provide &NULL, list will be allocated on heap,
9410 * if you provide pointer to non-NULL, list will be freed automatically at
9411 * first. Also in case of error the list will be NULLed.
9412 * @return IE_SUCCESS or appropriate error code. */
9413 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9414 const struct timeval *from_time, const struct timeval *to_time,
9415 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9416 const unsigned long int offset, unsigned long int *number,
9417 struct isds_list **messages) {
9419 return isds_get_list_of_messages(
9420 context, 1,
9421 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9422 offset, number,
9423 messages);
9427 /* Get list of incoming (addressed to you) messages.
9428 * Any criterion argument can be NULL, if you don't care about it.
9429 * @context is session context. Must not be NULL.
9430 * @from_time is minimal time and date of message sending inclusive.
9431 * @to_time is maximal time and date of message sending inclusive
9432 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9433 * @status_filter is bit field of isds_message_status values. Use special
9434 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9435 * all values, you can use bit-wise arithmetic if you want.)
9436 * @offset is index of first message we are interested in. First message is 1.
9437 * Set to 0 (or 1) if you don't care.
9438 * @number is maximal length of list you want to get as input value, outputs
9439 * number of messages matching these criteria. Can be NULL if you don't care
9440 * (applies to output value either).
9441 * @messages is automatically reallocated list of isds_message's. Be ware that
9442 * it returns only brief overview (envelope and some other fields) about each
9443 * message, not the complete message. FIXME: Specify exact fields.
9444 * Use NULL if you don't care about the meta data (useful if you want to know
9445 * only the @number). If you provide &NULL, list will be allocated on heap,
9446 * if you provide pointer to non-NULL, list will be freed automatically at
9447 * first. Also in case of error the list will be NULLed.
9448 * @return IE_SUCCESS or appropriate error code. */
9449 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9450 const struct timeval *from_time, const struct timeval *to_time,
9451 const long int *dmRecipientOrgUnitNum,
9452 const unsigned int status_filter,
9453 const unsigned long int offset, unsigned long int *number,
9454 struct isds_list **messages) {
9456 return isds_get_list_of_messages(
9457 context, 0,
9458 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9459 offset, number,
9460 messages);
9464 /* Get list of sent message state changes.
9465 * Any criterion argument can be NULL, if you don't care about it.
9466 * @context is session context. Must not be NULL.
9467 * @from_time is minimal time and date of status changes inclusive
9468 * @to_time is maximal time and date of status changes inclusive
9469 * @changed_states is automatically reallocated list of
9470 * isds_message_status_change's. If you provide &NULL, list will be allocated
9471 * on heap, if you provide pointer to non-NULL, list will be freed
9472 * automatically at first. Also in case of error the list will be NULLed.
9473 * XXX: The list item ordering is not specified.
9474 * XXX: Server provides only `recent' changes.
9475 * @return IE_SUCCESS or appropriate error code. */
9476 isds_error isds_get_list_of_sent_message_state_changes(
9477 struct isds_ctx *context,
9478 const struct timeval *from_time, const struct timeval *to_time,
9479 struct isds_list **changed_states) {
9481 isds_error err = IE_SUCCESS;
9482 #if HAVE_LIBCURL
9483 xmlNsPtr isds_ns = NULL;
9484 xmlNodePtr request = NULL, node;
9485 xmlDocPtr response = NULL;
9486 xmlXPathContextPtr xpath_ctx = NULL;
9487 xmlXPathObjectPtr result = NULL;
9488 xmlChar *string = NULL;
9489 long unsigned int count = 0;
9490 #endif
9492 if (!context) return IE_INVALID_CONTEXT;
9493 zfree(context->long_message);
9495 /* Free former message list if any */
9496 isds_list_free(changed_states);
9498 #if HAVE_LIBCURL
9499 /* Check if connection is established
9500 * TODO: This check should be done downstairs. */
9501 if (!context->curl) return IE_CONNECTION_CLOSED;
9503 /* Build GetMessageStateChanges request */
9504 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
9505 if (!request) {
9506 isds_log_message(context,
9507 _("Could not build GetMessageStateChanges request"));
9508 return IE_ERROR;
9510 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9511 if(!isds_ns) {
9512 isds_log_message(context, _("Could not create ISDS name space"));
9513 xmlFreeNode(request);
9514 return IE_ERROR;
9516 xmlSetNs(request, isds_ns);
9519 if (from_time) {
9520 err = timeval2timestring(from_time, &string);
9521 if (err) goto leave;
9523 INSERT_STRING(request, "dmFromTime", string);
9524 zfree(string);
9526 if (to_time) {
9527 err = timeval2timestring(to_time, &string);
9528 if (err) goto leave;
9530 INSERT_STRING(request, "dmToTime", string);
9531 zfree(string);
9534 /* Sent request */
9535 err = send_destroy_request_check_response(context,
9536 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9537 &response, NULL, NULL);
9538 if (err) goto leave;
9541 /* Extract data */
9542 xpath_ctx = xmlXPathNewContext(response);
9543 if (!xpath_ctx) {
9544 err = IE_ERROR;
9545 goto leave;
9547 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9548 err = IE_ERROR;
9549 goto leave;
9551 result = xmlXPathEvalExpression(
9552 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9553 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9554 if (!result) {
9555 err = IE_ERROR;
9556 goto leave;
9559 /* Fill output arguments in */
9560 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9561 struct isds_list *item = NULL, *last_item = NULL;
9563 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9564 /* Create new status change */
9565 item = calloc(1, sizeof(*item));
9566 if (!item) {
9567 err = IE_NOMEM;
9568 goto leave;
9570 item->destructor =
9571 (void(*)(void**)) &isds_message_status_change_free;
9573 /* Extract message status change */
9574 xpath_ctx->node = result->nodesetval->nodeTab[count];
9575 err = extract_StateChangesRecord(context,
9576 (struct isds_message_status_change **) &item->data,
9577 xpath_ctx);
9578 if (err) {
9579 isds_list_free(&item);
9580 goto leave;
9583 /* Append new message status change into the list */
9584 if (!*changed_states) {
9585 *changed_states = last_item = item;
9586 } else {
9587 last_item->next = item;
9588 last_item = item;
9593 leave:
9594 if (err) {
9595 isds_list_free(changed_states);
9598 free(string);
9599 xmlXPathFreeObject(result);
9600 xmlXPathFreeContext(xpath_ctx);
9601 xmlFreeDoc(response);
9602 xmlFreeNode(request);
9604 if (!err)
9605 isds_log(ILF_ISDS, ILL_DEBUG,
9606 _("GetMessageStateChanges request processed by server "
9607 "successfully.\n"));
9608 #else /* not HAVE_LIBCURL */
9609 err = IE_NOTSUP;
9610 #endif
9611 return err;
9615 #if HAVE_LIBCURL
9616 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9617 * code
9618 * @context is session context
9619 * @service is ISDS WS service handler
9620 * @service_name is name of SERVICE_DM_OPERATIONS
9621 * @message_id is message ID to send as service argument to ISDS
9622 * @response is reallocated server SOAP body response as XML document
9623 * @raw_response is reallocated bit stream with response body. Use
9624 * NULL if you don't care
9625 * @raw_response_length is size of @raw_response in bytes
9626 * @code is reallocated ISDS status code
9627 * @status_message is reallocated ISDS status message
9628 * @return error coded from lower layer, context message will be set up
9629 * appropriately. */
9630 static isds_error build_send_check_message_request(struct isds_ctx *context,
9631 const isds_service service, const xmlChar *service_name,
9632 const char *message_id,
9633 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9634 xmlChar **code, xmlChar **status_message) {
9636 isds_error err = IE_SUCCESS;
9637 char *service_name_locale = NULL, *message_id_locale = NULL;
9638 xmlNodePtr request = NULL, node;
9639 xmlNsPtr isds_ns = NULL;
9641 if (!context) return IE_INVALID_CONTEXT;
9642 if (!service_name || !message_id) return IE_INVAL;
9643 if (!response || !code || !status_message) return IE_INVAL;
9644 if (!raw_response_length && raw_response) return IE_INVAL;
9646 /* Free output argument */
9647 xmlFreeDoc(*response); *response = NULL;
9648 if (raw_response) zfree(*raw_response);
9649 zfree(*code);
9650 zfree(*status_message);
9653 /* Check if connection is established
9654 * TODO: This check should be done downstairs. */
9655 if (!context->curl) return IE_CONNECTION_CLOSED;
9657 service_name_locale = _isds_utf82locale((char*)service_name);
9658 message_id_locale = _isds_utf82locale(message_id);
9659 if (!service_name_locale || !message_id_locale) {
9660 err = IE_NOMEM;
9661 goto leave;
9664 /* Build request */
9665 request = xmlNewNode(NULL, service_name);
9666 if (!request) {
9667 isds_printf_message(context,
9668 _("Could not build %s request for %s message ID"),
9669 service_name_locale, message_id_locale);
9670 err = IE_ERROR;
9671 goto leave;
9673 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9674 if(!isds_ns) {
9675 isds_log_message(context, _("Could not create ISDS name space"));
9676 err = IE_ERROR;
9677 goto leave;
9679 xmlSetNs(request, isds_ns);
9682 /* Add requested ID */
9683 err = validate_message_id_length(context, (xmlChar *) message_id);
9684 if (err) goto leave;
9685 INSERT_STRING(request, "dmID", message_id);
9688 isds_log(ILF_ISDS, ILL_DEBUG,
9689 _("Sending %s request for %s message ID to ISDS\n"),
9690 service_name_locale, message_id_locale);
9692 /* Send request */
9693 err = _isds(context, service, request, response,
9694 raw_response, raw_response_length);
9695 xmlFreeNode(request); request = NULL;
9697 if (err) {
9698 isds_log(ILF_ISDS, ILL_DEBUG,
9699 _("Processing ISDS response on %s request failed\n"),
9700 service_name_locale);
9701 goto leave;
9704 /* Check for response status */
9705 err = isds_response_status(context, service, *response,
9706 code, status_message, NULL);
9707 if (err) {
9708 isds_log(ILF_ISDS, ILL_DEBUG,
9709 _("ISDS response on %s request is missing status\n"),
9710 service_name_locale);
9711 goto leave;
9714 /* Request processed, but nothing found */
9715 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9716 char *code_locale = _isds_utf82locale((char*) *code);
9717 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9718 isds_log(ILF_ISDS, ILL_DEBUG,
9719 _("Server refused %s request for %s message ID "
9720 "(code=%s, message=%s)\n"),
9721 service_name_locale, message_id_locale,
9722 code_locale, status_message_locale);
9723 isds_log_message(context, status_message_locale);
9724 free(code_locale);
9725 free(status_message_locale);
9726 err = IE_ISDS;
9727 goto leave;
9730 leave:
9731 free(message_id_locale);
9732 free(service_name_locale);
9733 xmlFreeNode(request);
9734 return err;
9738 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9739 * signed data and free ISDS response.
9740 * @context is session context
9741 * @message_id is UTF-8 encoded message ID for logging purpose
9742 * @response is parsed XML document. It will be freed and NULLed in the middle
9743 * of function run to save memory. This is not guaranteed in case of error.
9744 * @request_name is name of ISDS request used to construct response root
9745 * element name and for logging purpose.
9746 * @raw is reallocated output buffer with DER encoded CMS data
9747 * @raw_length is size of @raw buffer in bytes
9748 * @returns standard error codes, in case of error, @raw will be freed and
9749 * NULLed, @response sometimes. */
9750 static isds_error find_extract_signed_data_free_response(
9751 struct isds_ctx *context, const xmlChar *message_id,
9752 xmlDocPtr *response, const xmlChar *request_name,
9753 void **raw, size_t *raw_length) {
9755 isds_error err = IE_SUCCESS;
9756 char *xpath_expression = NULL;
9757 xmlXPathContextPtr xpath_ctx = NULL;
9758 xmlXPathObjectPtr result = NULL;
9759 char *encoded_structure = NULL;
9761 if (!context) return IE_INVALID_CONTEXT;
9762 if (!raw) return IE_INVAL;
9763 zfree(*raw);
9764 if (!message_id || !response || !*response || !request_name || !raw_length)
9765 return IE_INVAL;
9767 /* Build XPath expression */
9768 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9769 "Response/isds:dmSignature");
9770 if (!xpath_expression) return IE_NOMEM;
9772 /* Extract data */
9773 xpath_ctx = xmlXPathNewContext(*response);
9774 if (!xpath_ctx) {
9775 err = IE_ERROR;
9776 goto leave;
9778 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9779 err = IE_ERROR;
9780 goto leave;
9782 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9783 if (!result) {
9784 err = IE_ERROR;
9785 goto leave;
9787 /* Empty response */
9788 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9789 char *message_id_locale = _isds_utf82locale((char*) message_id);
9790 isds_printf_message(context,
9791 _("Server did not return any signed data for message ID `%s' "
9792 "on %s request"),
9793 message_id_locale, request_name);
9794 free(message_id_locale);
9795 err = IE_ISDS;
9796 goto leave;
9798 /* More responses */
9799 if (result->nodesetval->nodeNr > 1) {
9800 char *message_id_locale = _isds_utf82locale((char*) message_id);
9801 isds_printf_message(context,
9802 _("Server did return more signed data for message ID `%s' "
9803 "on %s request"),
9804 message_id_locale, request_name);
9805 free(message_id_locale);
9806 err = IE_ISDS;
9807 goto leave;
9809 /* One response */
9810 xpath_ctx->node = result->nodesetval->nodeTab[0];
9812 /* Extract PKCS#7 structure */
9813 EXTRACT_STRING(".", encoded_structure);
9814 if (!encoded_structure) {
9815 isds_log_message(context, _("dmSignature element is empty"));
9818 /* Here we have delivery info as standalone CMS in encoded_structure.
9819 * We don't need any other data, free them: */
9820 xmlXPathFreeObject(result); result = NULL;
9821 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9822 xmlFreeDoc(*response); *response = NULL;
9825 /* Decode PKCS#7 to DER format */
9826 *raw_length = _isds_b64decode(encoded_structure, raw);
9827 if (*raw_length == (size_t) -1) {
9828 isds_log_message(context,
9829 _("Error while Base64-decoding PKCS#7 structure"));
9830 err = IE_ERROR;
9831 goto leave;
9834 leave:
9835 if (err) {
9836 zfree(*raw);
9837 raw_length = 0;
9840 free(encoded_structure);
9841 xmlXPathFreeObject(result);
9842 xmlXPathFreeContext(xpath_ctx);
9843 free(xpath_expression);
9845 return err;
9847 #endif /* HAVE_LIBCURL */
9850 /* Download incoming message envelope identified by ID.
9851 * @context is session context
9852 * @message_id is message identifier (you can get them from
9853 * isds_get_list_of_received_messages())
9854 * @message is automatically reallocated message retrieved from ISDS.
9855 * It will miss documents per se. Use isds_get_received_message(), if you are
9856 * interested in documents (content) too.
9857 * Returned hash and timestamp require documents to be verifiable. */
9858 isds_error isds_get_received_envelope(struct isds_ctx *context,
9859 const char *message_id, struct isds_message **message) {
9861 isds_error err = IE_SUCCESS;
9862 #if HAVE_LIBCURL
9863 xmlDocPtr response = NULL;
9864 xmlChar *code = NULL, *status_message = NULL;
9865 xmlXPathContextPtr xpath_ctx = NULL;
9866 xmlXPathObjectPtr result = NULL;
9867 #endif
9869 if (!context) return IE_INVALID_CONTEXT;
9870 zfree(context->long_message);
9872 /* Free former message if any */
9873 if (!message) return IE_INVAL;
9874 isds_message_free(message);
9876 #if HAVE_LIBCURL
9877 /* Do request and check for success */
9878 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9879 BAD_CAST "MessageEnvelopeDownload", message_id,
9880 &response, NULL, NULL, &code, &status_message);
9881 if (err) goto leave;
9883 /* Extract data */
9884 xpath_ctx = xmlXPathNewContext(response);
9885 if (!xpath_ctx) {
9886 err = IE_ERROR;
9887 goto leave;
9889 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9890 err = IE_ERROR;
9891 goto leave;
9893 result = xmlXPathEvalExpression(
9894 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9895 "isds:dmReturnedMessageEnvelope",
9896 xpath_ctx);
9897 if (!result) {
9898 err = IE_ERROR;
9899 goto leave;
9901 /* Empty response */
9902 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9903 char *message_id_locale = _isds_utf82locale((char*) message_id);
9904 isds_printf_message(context,
9905 _("Server did not return any envelope for ID `%s' "
9906 "on MessageEnvelopeDownload request"), message_id_locale);
9907 free(message_id_locale);
9908 err = IE_ISDS;
9909 goto leave;
9911 /* More envelops */
9912 if (result->nodesetval->nodeNr > 1) {
9913 char *message_id_locale = _isds_utf82locale((char*) message_id);
9914 isds_printf_message(context,
9915 _("Server did return more envelopes for ID `%s' "
9916 "on MessageEnvelopeDownload request"), message_id_locale);
9917 free(message_id_locale);
9918 err = IE_ISDS;
9919 goto leave;
9921 /* One message */
9922 xpath_ctx->node = result->nodesetval->nodeTab[0];
9924 /* Extract the envelope (= message without documents, hence 0) */
9925 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9926 if (err) goto leave;
9928 /* Save XML blob */
9929 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9930 &(*message)->raw_length);
9932 leave:
9933 if (err) {
9934 isds_message_free(message);
9937 xmlXPathFreeObject(result);
9938 xmlXPathFreeContext(xpath_ctx);
9940 free(code);
9941 free(status_message);
9942 if (!*message || !(*message)->xml) {
9943 xmlFreeDoc(response);
9946 if (!err)
9947 isds_log(ILF_ISDS, ILL_DEBUG,
9948 _("MessageEnvelopeDownload request processed by server "
9949 "successfully.\n")
9951 #else /* not HAVE_LIBCURL */
9952 err = IE_NOTSUP;
9953 #endif
9954 return err;
9958 /* Load delivery info of any format from buffer.
9959 * @context is session context
9960 * @raw_type advertises format of @buffer content. Only delivery info types
9961 * are accepted.
9962 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9963 * retrieve such data from message->raw after calling
9964 * isds_get_signed_delivery_info().
9965 * @length is length of buffer in bytes.
9966 * @message is automatically reallocated message parsed from @buffer.
9967 * @strategy selects how buffer will be attached into raw isds_message member.
9968 * */
9969 isds_error isds_load_delivery_info(struct isds_ctx *context,
9970 const isds_raw_type raw_type,
9971 const void *buffer, const size_t length,
9972 struct isds_message **message, const isds_buffer_strategy strategy) {
9974 isds_error err = IE_SUCCESS;
9975 message_ns_type message_ns;
9976 xmlDocPtr message_doc = NULL;
9977 xmlXPathContextPtr xpath_ctx = NULL;
9978 xmlXPathObjectPtr result = NULL;
9979 void *xml_stream = NULL;
9980 size_t xml_stream_length = 0;
9982 if (!context) return IE_INVALID_CONTEXT;
9983 zfree(context->long_message);
9984 if (!message) return IE_INVAL;
9985 isds_message_free(message);
9986 if (!buffer) return IE_INVAL;
9989 /* Select buffer format and extract XML from CMS*/
9990 switch (raw_type) {
9991 case RAWTYPE_DELIVERYINFO:
9992 message_ns = MESSAGE_NS_UNSIGNED;
9993 xml_stream = (void *) buffer;
9994 xml_stream_length = length;
9995 break;
9997 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
9998 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9999 xml_stream = (void *) buffer;
10000 xml_stream_length = length;
10001 break;
10003 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10004 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10005 err = _isds_extract_cms_data(context, buffer, length,
10006 &xml_stream, &xml_stream_length);
10007 if (err) goto leave;
10008 break;
10010 default:
10011 isds_log_message(context, _("Bad raw delivery representation type"));
10012 return IE_INVAL;
10013 break;
10016 isds_log(ILF_ISDS, ILL_DEBUG,
10017 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10018 xml_stream_length, xml_stream);
10020 /* Convert delivery info XML stream into XPath context */
10021 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10022 if (!message_doc) {
10023 err = IE_XML;
10024 goto leave;
10026 xpath_ctx = xmlXPathNewContext(message_doc);
10027 if (!xpath_ctx) {
10028 err = IE_ERROR;
10029 goto leave;
10031 /* XXX: Name spaces mangled for signed delivery info:
10032 * http://isds.czechpoint.cz/v20/delivery:
10034 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10035 * <q:dmDelivery>
10036 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10037 * <p:dmID>170272</p:dmID>
10038 * ...
10039 * </p:dmDm>
10040 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10041 * ...
10042 * </q:dmEvents>...</q:dmEvents>
10043 * </q:dmDelivery>
10044 * </q:GetDeliveryInfoResponse>
10045 * */
10046 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10047 err = IE_ERROR;
10048 goto leave;
10050 result = xmlXPathEvalExpression(
10051 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10052 xpath_ctx);
10053 if (!result) {
10054 err = IE_ERROR;
10055 goto leave;
10057 /* Empty delivery info */
10058 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10059 isds_printf_message(context,
10060 _("XML document is not sisds:dmDelivery document"));
10061 err = IE_ISDS;
10062 goto leave;
10064 /* More delivery info's */
10065 if (result->nodesetval->nodeNr > 1) {
10066 isds_printf_message(context,
10067 _("XML document has more sisds:dmDelivery elements"));
10068 err = IE_ISDS;
10069 goto leave;
10071 /* One delivery info */
10072 xpath_ctx->node = result->nodesetval->nodeTab[0];
10074 /* Extract the envelope (= message without documents, hence 0).
10075 * XXX: extract_TReturnedMessage() can obtain attachments size,
10076 * but delivery info carries none. It's coded as option elements,
10077 * so it should work. */
10078 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10079 if (err) goto leave;
10081 /* Extract events */
10082 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10083 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10084 if (err) { err = IE_ERROR; goto leave; }
10085 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10086 if (err) goto leave;
10088 /* Append raw CMS structure into message */
10089 (*message)->raw_type = raw_type;
10090 switch (strategy) {
10091 case BUFFER_DONT_STORE:
10092 break;
10093 case BUFFER_COPY:
10094 (*message)->raw = malloc(length);
10095 if (!(*message)->raw) {
10096 err = IE_NOMEM;
10097 goto leave;
10099 memcpy((*message)->raw, buffer, length);
10100 (*message)->raw_length = length;
10101 break;
10102 case BUFFER_MOVE:
10103 (*message)->raw = (void *) buffer;
10104 (*message)->raw_length = length;
10105 break;
10106 default:
10107 err = IE_ENUM;
10108 goto leave;
10111 leave:
10112 if (err) {
10113 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10114 isds_message_free(message);
10117 xmlXPathFreeObject(result);
10118 xmlXPathFreeContext(xpath_ctx);
10119 if (!*message || !(*message)->xml) {
10120 xmlFreeDoc(message_doc);
10122 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10124 if (!err)
10125 isds_log(ILF_ISDS, ILL_DEBUG,
10126 _("Delivery info loaded successfully.\n"));
10127 return err;
10131 /* Download signed delivery info-sheet of given message identified by ID.
10132 * @context is session context
10133 * @message_id is message identifier (you can get them from
10134 * isds_get_list_of_{sent,received}_messages())
10135 * @message is automatically reallocated message retrieved from ISDS.
10136 * It will miss documents per se. Use isds_get_signed_received_message(),
10137 * if you are interested in documents (content). OTOH, only this function
10138 * can get list events message has gone through. */
10139 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10140 const char *message_id, struct isds_message **message) {
10142 isds_error err = IE_SUCCESS;
10143 #if HAVE_LIBCURL
10144 xmlDocPtr response = NULL;
10145 xmlChar *code = NULL, *status_message = NULL;
10146 void *raw = NULL;
10147 size_t raw_length = 0;
10148 #endif
10150 if (!context) return IE_INVALID_CONTEXT;
10151 zfree(context->long_message);
10153 /* Free former message if any */
10154 if (!message) return IE_INVAL;
10155 isds_message_free(message);
10157 #if HAVE_LIBCURL
10158 /* Do request and check for success */
10159 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10160 BAD_CAST "GetSignedDeliveryInfo", message_id,
10161 &response, NULL, NULL, &code, &status_message);
10162 if (err) goto leave;
10164 /* Find signed delivery info, extract it into raw and maybe free
10165 * response */
10166 err = find_extract_signed_data_free_response(context,
10167 (xmlChar *)message_id, &response,
10168 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10169 if (err) goto leave;
10171 /* Parse delivery info */
10172 err = isds_load_delivery_info(context,
10173 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10174 message, BUFFER_MOVE);
10175 if (err) goto leave;
10177 raw = NULL;
10179 leave:
10180 if (err) {
10181 isds_message_free(message);
10184 free(raw);
10185 free(code);
10186 free(status_message);
10187 xmlFreeDoc(response);
10189 if (!err)
10190 isds_log(ILF_ISDS, ILL_DEBUG,
10191 _("GetSignedDeliveryInfo request processed by server "
10192 "successfully.\n")
10194 #else /* not HAVE_LIBCURL */
10195 err = IE_NOTSUP;
10196 #endif
10197 return err;
10201 /* Download delivery info-sheet of given message identified by ID.
10202 * @context is session context
10203 * @message_id is message identifier (you can get them from
10204 * isds_get_list_of_{sent,received}_messages())
10205 * @message is automatically reallocated message retrieved from ISDS.
10206 * It will miss documents per se. Use isds_get_received_message(), if you are
10207 * interested in documents (content). OTOH, only this function can get list
10208 * of events message has gone through. */
10209 isds_error isds_get_delivery_info(struct isds_ctx *context,
10210 const char *message_id, struct isds_message **message) {
10212 isds_error err = IE_SUCCESS;
10213 #if HAVE_LIBCURL
10214 xmlDocPtr response = NULL;
10215 xmlChar *code = NULL, *status_message = NULL;
10216 xmlNodePtr delivery_node = NULL;
10217 void *raw = NULL;
10218 size_t raw_length = 0;
10219 #endif
10221 if (!context) return IE_INVALID_CONTEXT;
10222 zfree(context->long_message);
10224 /* Free former message if any */
10225 if (!message) return IE_INVAL;
10226 isds_message_free(message);
10228 #if HAVE_LIBCURL
10229 /* Do request and check for success */
10230 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10231 BAD_CAST "GetDeliveryInfo", message_id,
10232 &response, NULL, NULL, &code, &status_message);
10233 if (err) goto leave;
10236 /* Serialize delivery info */
10237 delivery_node = xmlDocGetRootElement(response);
10238 if (!delivery_node) {
10239 char *message_id_locale = _isds_utf82locale((char*) message_id);
10240 isds_printf_message(context,
10241 _("Server did not return any delivery info for ID `%s' "
10242 "on GetDeliveryInfo request"), message_id_locale);
10243 free(message_id_locale);
10244 err = IE_ISDS;
10245 goto leave;
10247 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10248 if (err) goto leave;
10250 /* Parse delivery info */
10251 /* TODO: Here we parse the response second time. We could single delivery
10252 * parser from isds_load_delivery_info() to make things faster. */
10253 err = isds_load_delivery_info(context,
10254 RAWTYPE_DELIVERYINFO, raw, raw_length,
10255 message, BUFFER_MOVE);
10256 if (err) goto leave;
10258 raw = NULL;
10261 leave:
10262 if (err) {
10263 isds_message_free(message);
10266 free(raw);
10267 free(code);
10268 free(status_message);
10269 xmlFreeDoc(response);
10271 if (!err)
10272 isds_log(ILF_ISDS, ILL_DEBUG,
10273 _("GetDeliveryInfo request processed by server "
10274 "successfully.\n")
10276 #else /* not HAVE_LIBCURL */
10277 err = IE_NOTSUP;
10278 #endif
10279 return err;
10283 /* Download incoming message identified by ID.
10284 * @context is session context
10285 * @message_id is message identifier (you can get them from
10286 * isds_get_list_of_received_messages())
10287 * @message is automatically reallocated message retrieved from ISDS */
10288 isds_error isds_get_received_message(struct isds_ctx *context,
10289 const char *message_id, struct isds_message **message) {
10291 isds_error err = IE_SUCCESS;
10292 #if HAVE_LIBCURL
10293 xmlDocPtr response = NULL;
10294 void *xml_stream = NULL;
10295 size_t xml_stream_length;
10296 xmlChar *code = NULL, *status_message = NULL;
10297 xmlXPathContextPtr xpath_ctx = NULL;
10298 xmlXPathObjectPtr result = NULL;
10299 char *phys_path = NULL;
10300 size_t phys_start, phys_end;
10301 #endif
10303 if (!context) return IE_INVALID_CONTEXT;
10304 zfree(context->long_message);
10306 /* Free former message if any */
10307 if (NULL == message) return IE_INVAL;
10308 if (message) isds_message_free(message);
10310 #if HAVE_LIBCURL
10311 /* Do request and check for success */
10312 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10313 BAD_CAST "MessageDownload", message_id,
10314 &response, &xml_stream, &xml_stream_length,
10315 &code, &status_message);
10316 if (err) goto leave;
10318 /* Extract data */
10319 xpath_ctx = xmlXPathNewContext(response);
10320 if (!xpath_ctx) {
10321 err = IE_ERROR;
10322 goto leave;
10324 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10325 err = IE_ERROR;
10326 goto leave;
10328 result = xmlXPathEvalExpression(
10329 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10330 xpath_ctx);
10331 if (!result) {
10332 err = IE_ERROR;
10333 goto leave;
10335 /* Empty response */
10336 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10337 char *message_id_locale = _isds_utf82locale((char*) message_id);
10338 isds_printf_message(context,
10339 _("Server did not return any message for ID `%s' "
10340 "on MessageDownload request"), message_id_locale);
10341 free(message_id_locale);
10342 err = IE_ISDS;
10343 goto leave;
10345 /* More messages */
10346 if (result->nodesetval->nodeNr > 1) {
10347 char *message_id_locale = _isds_utf82locale((char*) message_id);
10348 isds_printf_message(context,
10349 _("Server did return more messages for ID `%s' "
10350 "on MessageDownload request"), message_id_locale);
10351 free(message_id_locale);
10352 err = IE_ISDS;
10353 goto leave;
10355 /* One message */
10356 xpath_ctx->node = result->nodesetval->nodeTab[0];
10358 /* Extract the message */
10359 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10360 if (err) goto leave;
10362 /* Locate raw XML blob */
10363 phys_path = strdup(
10364 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10365 PHYSXML_ELEMENT_SEPARATOR
10366 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10367 PHYSXML_ELEMENT_SEPARATOR
10368 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10370 if (!phys_path) {
10371 err = IE_NOMEM;
10372 goto leave;
10374 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10375 phys_path, &phys_start, &phys_end);
10376 zfree(phys_path);
10377 if (err) {
10378 isds_log_message(context,
10379 _("Substring with isds:MessageDownloadResponse element "
10380 "could not be located in raw SOAP message"));
10381 goto leave;
10383 /* Save XML blob */
10384 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10385 &(*message)->raw_length);*/
10386 /* TODO: Store name space declarations from ancestors */
10387 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10388 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10389 (*message)->raw_length = phys_end - phys_start + 1;
10390 (*message)->raw = malloc((*message)->raw_length);
10391 if (!(*message)->raw) {
10392 err = IE_NOMEM;
10393 goto leave;
10395 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10398 leave:
10399 if (err) {
10400 isds_message_free(message);
10403 free(phys_path);
10405 xmlXPathFreeObject(result);
10406 xmlXPathFreeContext(xpath_ctx);
10408 free(code);
10409 free(status_message);
10410 free(xml_stream);
10411 if (!*message || !(*message)->xml) {
10412 xmlFreeDoc(response);
10415 if (!err)
10416 isds_log(ILF_ISDS, ILL_DEBUG,
10417 _("MessageDownload request processed by server "
10418 "successfully.\n")
10420 #else /* not HAVE_LIBCURL */
10421 err = IE_NOTSUP;
10422 #endif
10423 return err;
10427 /* Load message of any type from buffer.
10428 * @context is session context
10429 * @raw_type defines content type of @buffer. Only message types are allowed.
10430 * @buffer is message raw representation. Format (CMS, plain signed,
10431 * message direction) is defined in @raw_type. You can retrieve such data
10432 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10433 * @length is length of buffer in bytes.
10434 * @message is automatically reallocated message parsed from @buffer.
10435 * @strategy selects how buffer will be attached into raw isds_message member.
10436 * */
10437 isds_error isds_load_message(struct isds_ctx *context,
10438 const isds_raw_type raw_type, const void *buffer, const size_t length,
10439 struct isds_message **message, const isds_buffer_strategy strategy) {
10441 isds_error err = IE_SUCCESS;
10442 void *xml_stream = NULL;
10443 size_t xml_stream_length = 0;
10444 message_ns_type message_ns;
10445 xmlDocPtr message_doc = NULL;
10446 xmlXPathContextPtr xpath_ctx = NULL;
10447 xmlXPathObjectPtr result = NULL;
10449 if (!context) return IE_INVALID_CONTEXT;
10450 zfree(context->long_message);
10451 if (!message) return IE_INVAL;
10452 isds_message_free(message);
10453 if (!buffer) return IE_INVAL;
10456 /* Select buffer format and extract XML from CMS*/
10457 switch (raw_type) {
10458 case RAWTYPE_INCOMING_MESSAGE:
10459 message_ns = MESSAGE_NS_UNSIGNED;
10460 xml_stream = (void *) buffer;
10461 xml_stream_length = length;
10462 break;
10464 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10465 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10466 xml_stream = (void *) buffer;
10467 xml_stream_length = length;
10468 break;
10470 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10471 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10472 err = _isds_extract_cms_data(context, buffer, length,
10473 &xml_stream, &xml_stream_length);
10474 if (err) goto leave;
10475 break;
10477 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10478 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10479 xml_stream = (void *) buffer;
10480 xml_stream_length = length;
10481 break;
10483 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10484 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10485 err = _isds_extract_cms_data(context, buffer, length,
10486 &xml_stream, &xml_stream_length);
10487 if (err) goto leave;
10488 break;
10490 default:
10491 isds_log_message(context, _("Bad raw message representation type"));
10492 return IE_INVAL;
10493 break;
10496 isds_log(ILF_ISDS, ILL_DEBUG,
10497 _("Loading message:\n%.*s\nEnd of message\n"),
10498 xml_stream_length, xml_stream);
10500 /* Convert messages XML stream into XPath context */
10501 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10502 if (!message_doc) {
10503 err = IE_XML;
10504 goto leave;
10506 xpath_ctx = xmlXPathNewContext(message_doc);
10507 if (!xpath_ctx) {
10508 err = IE_ERROR;
10509 goto leave;
10511 /* XXX: Standard name space for unsigned incoming direction:
10512 * http://isds.czechpoint.cz/v20/
10514 * XXX: Name spaces mangled for signed outgoing direction:
10515 * http://isds.czechpoint.cz/v20/SentMessage:
10517 * <q:MessageDownloadResponse
10518 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10519 * <q:dmReturnedMessage>
10520 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10521 * <p:dmID>151916</p:dmID>
10522 * ...
10523 * </p:dmDm>
10524 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10525 * ...
10526 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10527 * </q:dmReturnedMessage>
10528 * </q:MessageDownloadResponse>
10530 * XXX: Name spaces mangled for signed incoming direction:
10531 * http://isds.czechpoint.cz/v20/message:
10533 * <q:MessageDownloadResponse
10534 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10535 * <q:dmReturnedMessage>
10536 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10537 * <p:dmID>151916</p:dmID>
10538 * ...
10539 * </p:dmDm>
10540 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10541 * ...
10542 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10543 * </q:dmReturnedMessage>
10544 * </q:MessageDownloadResponse>
10546 * Stupidity of ISDS developers is unlimited */
10547 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10548 err = IE_ERROR;
10549 goto leave;
10551 result = xmlXPathEvalExpression(
10552 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10553 xpath_ctx);
10554 if (!result) {
10555 err = IE_ERROR;
10556 goto leave;
10558 /* Empty message */
10559 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10560 isds_printf_message(context,
10561 _("XML document does not contain "
10562 "sisds:dmReturnedMessage element"));
10563 err = IE_ISDS;
10564 goto leave;
10566 /* More messages */
10567 if (result->nodesetval->nodeNr > 1) {
10568 isds_printf_message(context,
10569 _("XML document has more sisds:dmReturnedMessage elements"));
10570 err = IE_ISDS;
10571 goto leave;
10573 /* One message */
10574 xpath_ctx->node = result->nodesetval->nodeTab[0];
10576 /* Extract the message */
10577 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10578 if (err) goto leave;
10580 /* Append raw buffer into message */
10581 (*message)->raw_type = raw_type;
10582 switch (strategy) {
10583 case BUFFER_DONT_STORE:
10584 break;
10585 case BUFFER_COPY:
10586 (*message)->raw = malloc(length);
10587 if (!(*message)->raw) {
10588 err = IE_NOMEM;
10589 goto leave;
10591 memcpy((*message)->raw, buffer, length);
10592 (*message)->raw_length = length;
10593 break;
10594 case BUFFER_MOVE:
10595 (*message)->raw = (void *) buffer;
10596 (*message)->raw_length = length;
10597 break;
10598 default:
10599 err = IE_ENUM;
10600 goto leave;
10604 leave:
10605 if (err) {
10606 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10607 isds_message_free(message);
10610 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10611 xmlXPathFreeObject(result);
10612 xmlXPathFreeContext(xpath_ctx);
10613 if (!*message || !(*message)->xml) {
10614 xmlFreeDoc(message_doc);
10617 if (!err)
10618 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10619 return err;
10623 /* Determine type of raw message or delivery info according some heuristics.
10624 * It does not validate the raw blob.
10625 * @context is session context
10626 * @raw_type returns content type of @buffer. Valid only if exit code of this
10627 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10628 * reallocated memory.
10629 * @buffer is message raw representation.
10630 * @length is length of buffer in bytes. */
10631 isds_error isds_guess_raw_type(struct isds_ctx *context,
10632 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10633 isds_error err;
10634 void *xml_stream = NULL;
10635 size_t xml_stream_length = 0;
10636 xmlDocPtr document = NULL;
10637 xmlNodePtr root = NULL;
10639 if (!context) return IE_INVALID_CONTEXT;
10640 zfree(context->long_message);
10641 if (length == 0 || !buffer) return IE_INVAL;
10642 if (!raw_type) return IE_INVAL;
10644 /* Try CMS */
10645 err = _isds_extract_cms_data(context, buffer, length,
10646 &xml_stream, &xml_stream_length);
10647 if (err) {
10648 xml_stream = (void *) buffer;
10649 xml_stream_length = (size_t) length;
10650 err = IE_SUCCESS;
10653 /* Try XML */
10654 document = xmlParseMemory(xml_stream, xml_stream_length);
10655 if (!document) {
10656 isds_printf_message(context,
10657 _("Could not parse data as XML document"));
10658 err = IE_NOTSUP;
10659 goto leave;
10662 /* Get root element */
10663 root = xmlDocGetRootElement(document);
10664 if (!root) {
10665 isds_printf_message(context,
10666 _("XML document is missing root element"));
10667 err = IE_XML;
10668 goto leave;
10671 if (!root->ns || !root->ns->href) {
10672 isds_printf_message(context,
10673 _("Root element does not belong to any name space"));
10674 err = IE_NOTSUP;
10675 goto leave;
10678 /* Test name space */
10679 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10680 if (xml_stream == buffer)
10681 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10682 else
10683 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10684 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10685 if (xml_stream == buffer)
10686 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10687 else
10688 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10689 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10690 if (xml_stream == buffer)
10691 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10692 else
10693 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10694 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10695 if (xml_stream != buffer) {
10696 isds_printf_message(context,
10697 _("Document in ISDS name space is encapsulated into CMS" ));
10698 err = IE_NOTSUP;
10699 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10700 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10701 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10702 *raw_type = RAWTYPE_DELIVERYINFO;
10703 else {
10704 isds_printf_message(context,
10705 _("Unknown root element in ISDS name space"));
10706 err = IE_NOTSUP;
10708 } else {
10709 isds_printf_message(context,
10710 _("Unknown name space"));
10711 err = IE_NOTSUP;
10714 leave:
10715 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10716 xmlFreeDoc(document);
10717 return err;
10721 /* Download signed incoming/outgoing message identified by ID.
10722 * @context is session context
10723 * @output is true for outgoing message, false for incoming message
10724 * @message_id is message identifier (you can get them from
10725 * isds_get_list_of_{sent,received}_messages())
10726 * @message is automatically reallocated message retrieved from ISDS. The raw
10727 * member will be filled with PKCS#7 structure in DER format. */
10728 static isds_error isds_get_signed_message(struct isds_ctx *context,
10729 const _Bool outgoing, const char *message_id,
10730 struct isds_message **message) {
10732 isds_error err = IE_SUCCESS;
10733 #if HAVE_LIBCURL
10734 xmlDocPtr response = NULL;
10735 xmlChar *code = NULL, *status_message = NULL;
10736 xmlXPathContextPtr xpath_ctx = NULL;
10737 xmlXPathObjectPtr result = NULL;
10738 char *encoded_structure = NULL;
10739 void *raw = NULL;
10740 size_t raw_length = 0;
10741 #endif
10743 if (!context) return IE_INVALID_CONTEXT;
10744 zfree(context->long_message);
10745 if (!message) return IE_INVAL;
10746 isds_message_free(message);
10748 #if HAVE_LIBCURL
10749 /* Do request and check for success */
10750 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10751 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10752 BAD_CAST "SignedMessageDownload",
10753 message_id, &response, NULL, NULL, &code, &status_message);
10754 if (err) goto leave;
10756 /* Find signed message, extract it into raw and maybe free
10757 * response */
10758 err = find_extract_signed_data_free_response(context,
10759 (xmlChar *)message_id, &response,
10760 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10761 BAD_CAST "SignedMessageDownload",
10762 &raw, &raw_length);
10763 if (err) goto leave;
10765 /* Parse message */
10766 err = isds_load_message(context,
10767 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10768 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10769 raw, raw_length, message, BUFFER_MOVE);
10770 if (err) goto leave;
10772 raw = NULL;
10774 leave:
10775 if (err) {
10776 isds_message_free(message);
10779 free(encoded_structure);
10780 xmlXPathFreeObject(result);
10781 xmlXPathFreeContext(xpath_ctx);
10782 free(raw);
10784 free(code);
10785 free(status_message);
10786 xmlFreeDoc(response);
10788 if (!err)
10789 isds_log(ILF_ISDS, ILL_DEBUG,
10790 (outgoing) ?
10791 _("SignedSentMessageDownload request processed by server "
10792 "successfully.\n") :
10793 _("SignedMessageDownload request processed by server "
10794 "successfully.\n")
10796 #else /* not HAVE_LIBCURL */
10797 err = IE_NOTSUP;
10798 #endif
10799 return err;
10803 /* Download signed incoming message identified by ID.
10804 * @context is session context
10805 * @message_id is message identifier (you can get them from
10806 * isds_get_list_of_received_messages())
10807 * @message is automatically reallocated message retrieved from ISDS. The raw
10808 * member will be filled with PKCS#7 structure in DER format. */
10809 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10810 const char *message_id, struct isds_message **message) {
10811 return isds_get_signed_message(context, 0, message_id, message);
10815 /* Download signed outgoing message identified by ID.
10816 * @context is session context
10817 * @message_id is message identifier (you can get them from
10818 * isds_get_list_of_sent_messages())
10819 * @message is automatically reallocated message retrieved from ISDS. The raw
10820 * member will be filled with PKCS#7 structure in DER format. */
10821 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10822 const char *message_id, struct isds_message **message) {
10823 return isds_get_signed_message(context, 1, message_id, message);
10827 /* Get type and name of user who sent a message identified by ID.
10828 * @context is session context
10829 * @message_id is message identifier
10830 * @sender_type is pointer to automatically allocated type of sender detected
10831 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10832 * library or to the server, NULL will be returned. Pass NULL if you don't
10833 * care about it.
10834 * @raw_sender_type is automatically reallocated UTF-8 string describing
10835 * sender type or NULL if not known to server. Pass NULL if you don't care.
10836 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10837 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10838 isds_error isds_get_message_sender(struct isds_ctx *context,
10839 const char *message_id, isds_sender_type **sender_type,
10840 char **raw_sender_type, char **sender_name) {
10841 isds_error err = IE_SUCCESS;
10842 #if HAVE_LIBCURL
10843 xmlDocPtr response = NULL;
10844 xmlChar *code = NULL, *status_message = NULL;
10845 xmlXPathContextPtr xpath_ctx = NULL;
10846 xmlXPathObjectPtr result = NULL;
10847 char *type_string = NULL;
10848 #endif
10850 if (!context) return IE_INVALID_CONTEXT;
10851 zfree(context->long_message);
10852 if (sender_type) zfree(*sender_type);
10853 if (raw_sender_type) zfree(*raw_sender_type);
10854 if (sender_name) zfree(*sender_name);
10855 if (!message_id) return IE_INVAL;
10857 #if HAVE_LIBCURL
10858 /* Do request and check for success */
10859 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10860 BAD_CAST "GetMessageAuthor",
10861 message_id, &response, NULL, NULL, &code, &status_message);
10862 if (err) goto leave;
10864 /* Extract data */
10865 xpath_ctx = xmlXPathNewContext(response);
10866 if (!xpath_ctx) {
10867 err = IE_ERROR;
10868 goto leave;
10870 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10871 err = IE_ERROR;
10872 goto leave;
10874 result = xmlXPathEvalExpression(
10875 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10876 if (!result) {
10877 err = IE_ERROR;
10878 goto leave;
10880 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10881 isds_log_message(context,
10882 _("Missing GetMessageAuthorResponse element"));
10883 err = IE_ISDS;
10884 goto leave;
10886 if (result->nodesetval->nodeNr > 1) {
10887 isds_log_message(context,
10888 _("Multiple GetMessageAuthorResponse element"));
10889 err = IE_ISDS;
10890 goto leave;
10892 xpath_ctx->node = result->nodesetval->nodeTab[0];
10893 xmlXPathFreeObject(result); result = NULL;
10895 /* Fill output arguments in */
10896 EXTRACT_STRING("isds:userType", type_string);
10897 if (NULL != type_string) {
10898 if (NULL != sender_type) {
10899 *sender_type = calloc(1, sizeof(**sender_type));
10900 if (NULL == *sender_type) {
10901 err = IE_NOMEM;
10902 goto leave;
10905 err = string2isds_sender_type((xmlChar *)type_string,
10906 *sender_type);
10907 if (err) {
10908 zfree(*sender_type);
10909 if (err == IE_ENUM) {
10910 err = IE_SUCCESS;
10911 char *type_string_locale = _isds_utf82locale(type_string);
10912 isds_log(ILF_ISDS, ILL_WARNING,
10913 _("Unknown isds:userType value: %s"),
10914 type_string_locale);
10915 free(type_string_locale);
10920 if (NULL != sender_name)
10921 EXTRACT_STRING("isds:authorName", *sender_name);
10923 leave:
10924 if (err) {
10925 if (NULL != sender_type) zfree(*sender_type);
10926 zfree(type_string);
10927 if (NULL != sender_name) zfree(*sender_name);
10929 if (NULL != raw_sender_type) *raw_sender_type = type_string;
10931 xmlXPathFreeObject(result);
10932 xmlXPathFreeContext(xpath_ctx);
10934 free(code);
10935 free(status_message);
10936 xmlFreeDoc(response);
10938 if (!err)
10939 isds_log(ILF_ISDS, ILL_DEBUG,
10940 _("GetMessageAuthor request processed by server "
10941 "successfully.\n"));
10942 #else /* not HAVE_LIBCURL */
10943 err = IE_NOTSUP;
10944 #endif
10945 return err;
10949 /* Retrieve hash of message identified by ID stored in ISDS.
10950 * @context is session context
10951 * @message_id is message identifier
10952 * @hash is automatically reallocated message hash downloaded from ISDS.
10953 * Message must exist in system and must not be deleted. */
10954 isds_error isds_download_message_hash(struct isds_ctx *context,
10955 const char *message_id, struct isds_hash **hash) {
10957 isds_error err = IE_SUCCESS;
10958 #if HAVE_LIBCURL
10959 xmlDocPtr response = NULL;
10960 xmlChar *code = NULL, *status_message = NULL;
10961 xmlXPathContextPtr xpath_ctx = NULL;
10962 xmlXPathObjectPtr result = NULL;
10963 #endif
10965 if (!context) return IE_INVALID_CONTEXT;
10966 zfree(context->long_message);
10968 isds_hash_free(hash);
10970 #if HAVE_LIBCURL
10971 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10972 BAD_CAST "VerifyMessage", message_id,
10973 &response, NULL, NULL, &code, &status_message);
10974 if (err) goto leave;
10977 /* Extract data */
10978 xpath_ctx = xmlXPathNewContext(response);
10979 if (!xpath_ctx) {
10980 err = IE_ERROR;
10981 goto leave;
10983 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10984 err = IE_ERROR;
10985 goto leave;
10987 result = xmlXPathEvalExpression(
10988 BAD_CAST "/isds:VerifyMessageResponse",
10989 xpath_ctx);
10990 if (!result) {
10991 err = IE_ERROR;
10992 goto leave;
10994 /* Empty response */
10995 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10996 char *message_id_locale = _isds_utf82locale((char*) message_id);
10997 isds_printf_message(context,
10998 _("Server did not return any response for ID `%s' "
10999 "on VerifyMessage request"), message_id_locale);
11000 free(message_id_locale);
11001 err = IE_ISDS;
11002 goto leave;
11004 /* More responses */
11005 if (result->nodesetval->nodeNr > 1) {
11006 char *message_id_locale = _isds_utf82locale((char*) message_id);
11007 isds_printf_message(context,
11008 _("Server did return more responses for ID `%s' "
11009 "on VerifyMessage request"), message_id_locale);
11010 free(message_id_locale);
11011 err = IE_ISDS;
11012 goto leave;
11014 /* One response */
11015 xpath_ctx->node = result->nodesetval->nodeTab[0];
11017 /* Extract the hash */
11018 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11020 leave:
11021 if (err) {
11022 isds_hash_free(hash);
11025 xmlXPathFreeObject(result);
11026 xmlXPathFreeContext(xpath_ctx);
11028 free(code);
11029 free(status_message);
11030 xmlFreeDoc(response);
11032 if (!err)
11033 isds_log(ILF_ISDS, ILL_DEBUG,
11034 _("VerifyMessage request processed by server "
11035 "successfully.\n")
11037 #else /* not HAVE_LIBCURL */
11038 err = IE_NOTSUP;
11039 #endif
11040 return err;
11044 /* Erase message specified by @message_id from long term storage. Other
11045 * message cannot be erased on user request.
11046 * @context is session context
11047 * @message_id is message identifier.
11048 * @incoming is true for incoming message, false for outgoing message.
11049 * @return
11050 * IE_SUCCESS if message has ben removed
11051 * IE_INVAL if message does not exist in long term storage or message
11052 * belongs to different box
11053 * TODO: IE_NOEPRM if user has no permission to erase a message */
11054 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11055 const char *message_id, _Bool incoming) {
11056 isds_error err = IE_SUCCESS;
11057 #if HAVE_LIBCURL
11058 xmlNodePtr request = NULL, node;
11059 xmlNsPtr isds_ns = NULL;
11060 xmlDocPtr response = NULL;
11061 xmlChar *code = NULL, *status_message = NULL;
11062 #endif
11064 if (!context) return IE_INVALID_CONTEXT;
11065 zfree(context->long_message);
11066 if (NULL == message_id) return IE_INVAL;
11068 /* Check if connection is established
11069 * TODO: This check should be done downstairs. */
11070 if (!context->curl) return IE_CONNECTION_CLOSED;
11072 #if HAVE_LIBCURL
11073 /* Build request */
11074 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11075 if (!request) {
11076 isds_log_message(context,
11077 _("Could build EraseMessage request"));
11078 return IE_ERROR;
11080 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11081 if(!isds_ns) {
11082 isds_log_message(context, _("Could not create ISDS name space"));
11083 xmlFreeNode(request);
11084 return IE_ERROR;
11086 xmlSetNs(request, isds_ns);
11088 err = validate_message_id_length(context, (xmlChar *) message_id);
11089 if (err) goto leave;
11090 INSERT_STRING(request, "dmID", message_id);
11092 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11095 /* Send request */
11096 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11097 "message ID %s to ISDS\n"), message_id);
11098 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11099 xmlFreeNode(request); request = NULL;
11101 if (err) {
11102 isds_log(ILF_ISDS, ILL_DEBUG,
11103 _("Processing ISDS response on EraseMessage request "
11104 "failed\n"));
11105 goto leave;
11108 /* Check for response status */
11109 err = isds_response_status(context, SERVICE_DM_INFO, response,
11110 &code, &status_message, NULL);
11111 if (err) {
11112 isds_log(ILF_ISDS, ILL_DEBUG,
11113 _("ISDS response on EraseMessage request is missing "
11114 "status\n"));
11115 goto leave;
11118 /* Check server status code */
11119 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11120 isds_log_message(context, _("Message to erase belongs to other box"));
11121 err = IE_INVAL;
11122 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11123 isds_log_message(context, _("Message to erase is not saved in "
11124 "long term storage or the direction does not match"));
11125 err = IE_INVAL;
11126 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11127 char *code_locale = _isds_utf82locale((char*) code);
11128 char *message_locale = _isds_utf82locale((char*) status_message);
11129 isds_log(ILF_ISDS, ILL_DEBUG,
11130 _("Server refused EraseMessage request "
11131 "(code=%s, message=%s)\n"),
11132 code_locale, message_locale);
11133 isds_log_message(context, message_locale);
11134 free(code_locale);
11135 free(message_locale);
11136 err = IE_ISDS;
11137 goto leave;
11140 leave:
11141 free(code);
11142 free(status_message);
11143 xmlFreeDoc(response);
11144 xmlFreeNode(request);
11146 if (!err)
11147 isds_log(ILF_ISDS, ILL_DEBUG,
11148 _("EraseMessage request processed by server "
11149 "successfully.\n")
11151 #else /* not HAVE_LIBCURL */
11152 err = IE_NOTSUP;
11153 #endif
11154 return err;
11158 /* Mark message as read. This is a transactional commit function to acknowledge
11159 * to ISDS the message has been downloaded and processed by client properly.
11160 * @context is session context
11161 * @message_id is message identifier. */
11162 isds_error isds_mark_message_read(struct isds_ctx *context,
11163 const char *message_id) {
11165 isds_error err = IE_SUCCESS;
11166 #if HAVE_LIBCURL
11167 xmlDocPtr response = NULL;
11168 xmlChar *code = NULL, *status_message = NULL;
11169 #endif
11171 if (!context) return IE_INVALID_CONTEXT;
11172 zfree(context->long_message);
11174 #if HAVE_LIBCURL
11175 /* Do request and check for success */
11176 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11177 BAD_CAST "MarkMessageAsDownloaded", message_id,
11178 &response, NULL, NULL, &code, &status_message);
11180 free(code);
11181 free(status_message);
11182 xmlFreeDoc(response);
11184 if (!err)
11185 isds_log(ILF_ISDS, ILL_DEBUG,
11186 _("MarkMessageAsDownloaded request processed by server "
11187 "successfully.\n")
11189 #else /* not HAVE_LIBCURL */
11190 err = IE_NOTSUP;
11191 #endif
11192 return err;
11196 /* Mark message as received by recipient. This is applicable only to
11197 * commercial message. Use envelope->dmType message member to distinguish
11198 * commercial message from government message. Government message is
11199 * received automatically (by law), commercial message on recipient request.
11200 * @context is session context
11201 * @message_id is message identifier. */
11202 isds_error isds_mark_message_received(struct isds_ctx *context,
11203 const char *message_id) {
11205 isds_error err = IE_SUCCESS;
11206 #if HAVE_LIBCURL
11207 xmlDocPtr response = NULL;
11208 xmlChar *code = NULL, *status_message = NULL;
11209 #endif
11211 if (!context) return IE_INVALID_CONTEXT;
11212 zfree(context->long_message);
11214 #if HAVE_LIBCURL
11215 /* Do request and check for success */
11216 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11217 BAD_CAST "ConfirmDelivery", message_id,
11218 &response, NULL, NULL, &code, &status_message);
11220 free(code);
11221 free(status_message);
11222 xmlFreeDoc(response);
11224 if (!err)
11225 isds_log(ILF_ISDS, ILL_DEBUG,
11226 _("ConfirmDelivery request processed by server "
11227 "successfully.\n")
11229 #else /* not HAVE_LIBCURL */
11230 err = IE_NOTSUP;
11231 #endif
11232 return err;
11236 /* Send document for authorized conversion into Czech POINT system.
11237 * This is public anonymous service, no log-in necessary. Special context is
11238 * used to reuse keep-a-live HTTPS connection.
11239 * @context is Czech POINT session context. DO NOT use context connected to
11240 * ISDS server. Use new context or context used by this function previously.
11241 * @document is document to convert. Only data, data_length, dmFileDescr and
11242 * is_xml members are significant. Be ware that not all document formats can be
11243 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11244 * @id is reallocated identifier assigned by Czech POINT system to
11245 * your document on submit. Use is to tell it to Czech POINT officer.
11246 * @date is reallocated document submit date (submitted documents
11247 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11248 * value. */
11249 isds_error czp_convert_document(struct isds_ctx *context,
11250 const struct isds_document *document,
11251 char **id, struct tm **date) {
11252 isds_error err = IE_SUCCESS;
11253 #if HAVE_LIBCURL
11254 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11255 xmlNodePtr request = NULL, node;
11256 xmlDocPtr response = NULL;
11258 xmlXPathContextPtr xpath_ctx = NULL;
11259 xmlXPathObjectPtr result = NULL;
11260 long int status = -1;
11261 long int *status_ptr = &status;
11262 char *string = NULL;
11263 #endif
11266 if (!context) return IE_INVALID_CONTEXT;
11267 zfree(context->long_message);
11268 if (!document || !id || !date) return IE_INVAL;
11270 if (document->is_xml) {
11271 isds_log_message(context,
11272 _("XML documents cannot be submitted to conversion"));
11273 return IE_NOTSUP;
11276 /* Free output arguments */
11277 zfree(*id);
11278 zfree(*date);
11280 #if HAVE_LIBCURL
11281 /* Store configuration */
11282 context->type = CTX_TYPE_CZP;
11283 free(context->url);
11284 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11285 if (!(context->url))
11286 return IE_NOMEM;
11288 /* Prepare CURL handle if not yet connected */
11289 if (!context->curl) {
11290 context->curl = curl_easy_init();
11291 if (!(context->curl))
11292 return IE_ERROR;
11295 /* Build conversion request */
11296 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11297 if (!request) {
11298 isds_log_message(context,
11299 _("Could not build Czech POINT conversion request"));
11300 return IE_ERROR;
11302 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11303 if(!deposit_ns) {
11304 isds_log_message(context,
11305 _("Could not create Czech POINT deposit name space"));
11306 xmlFreeNode(request);
11307 return IE_ERROR;
11309 xmlSetNs(request, deposit_ns);
11311 /* Insert children. They are in empty namespace! */
11312 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11313 if(!empty_ns) {
11314 isds_log_message(context, _("Could not create empty name space"));
11315 err = IE_ERROR;
11316 goto leave;
11318 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11319 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11320 document->dmFileDescr);
11322 /* Document encoded in Base64 */
11323 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11324 document->data, document->data_length);
11325 if (err) goto leave;
11327 isds_log(ILF_ISDS, ILL_DEBUG,
11328 _("Submitting document for conversion into Czech POINT deposit"));
11330 /* Send conversion request */
11331 err = _czp_czpdeposit(context, request, &response);
11332 xmlFreeNode(request); request = NULL;
11334 if (err) {
11335 czp_do_close_connection(context);
11336 goto leave;
11340 /* Extract response */
11341 xpath_ctx = xmlXPathNewContext(response);
11342 if (!xpath_ctx) {
11343 err = IE_ERROR;
11344 goto leave;
11346 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11347 err = IE_ERROR;
11348 goto leave;
11350 result = xmlXPathEvalExpression(
11351 BAD_CAST "/deposit:saveDocumentResponse/return",
11352 xpath_ctx);
11353 if (!result) {
11354 err = IE_ERROR;
11355 goto leave;
11357 /* Empty response */
11358 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11359 isds_printf_message(context,
11360 _("Missing `return' element in Czech POINT deposit response"));
11361 err = IE_ISDS;
11362 goto leave;
11364 /* More responses */
11365 if (result->nodesetval->nodeNr > 1) {
11366 isds_printf_message(context,
11367 _("Multiple `return' element in Czech POINT deposit response"));
11368 err = IE_ISDS;
11369 goto leave;
11371 /* One response */
11372 xpath_ctx->node = result->nodesetval->nodeTab[0];
11374 /* Get status */
11375 EXTRACT_LONGINT("status", status_ptr, 1);
11376 if (status) {
11377 EXTRACT_STRING("statusMsg", string);
11378 char *string_locale = _isds_utf82locale(string);
11379 isds_printf_message(context,
11380 _("Czech POINT deposit refused document for conversion "
11381 "(code=%ld, message=%s)"),
11382 status, string_locale);
11383 free(string_locale);
11384 err = IE_ISDS;
11385 goto leave;
11388 /* Get document ID */
11389 EXTRACT_STRING("documentID", *id);
11391 /* Get submit date */
11392 EXTRACT_STRING("dateInserted", string);
11393 if (string) {
11394 *date = calloc(1, sizeof(**date));
11395 if (!*date) {
11396 err = IE_NOMEM;
11397 goto leave;
11399 err = _isds_datestring2tm((xmlChar *)string, *date);
11400 if (err) {
11401 if (err == IE_NOTSUP) {
11402 err = IE_ISDS;
11403 char *string_locale = _isds_utf82locale(string);
11404 isds_printf_message(context,
11405 _("Invalid dateInserted value: %s"), string_locale);
11406 free(string_locale);
11408 goto leave;
11412 leave:
11413 free(string);
11414 xmlXPathFreeObject(result);
11415 xmlXPathFreeContext(xpath_ctx);
11417 xmlFreeDoc(response);
11418 xmlFreeNode(request);
11420 if (!err) {
11421 char *id_locale = _isds_utf82locale((char *) *id);
11422 isds_log(ILF_ISDS, ILL_DEBUG,
11423 _("Document %s has been submitted for conversion "
11424 "to server successfully\n"), id_locale);
11425 free(id_locale);
11427 #else /* not HAVE_LIBCURL */
11428 err = IE_NOTSUP;
11429 #endif
11430 return err;
11434 /* Close possibly opened connection to Czech POINT document deposit.
11435 * @context is Czech POINT session context. */
11436 isds_error czp_close_connection(struct isds_ctx *context) {
11437 if (!context) return IE_INVALID_CONTEXT;
11438 zfree(context->long_message);
11439 #if HAVE_LIBCURL
11440 return czp_do_close_connection(context);
11441 #else
11442 return IE_NOTSUP;
11443 #endif
11447 /* Send request for new box creation in testing ISDS instance.
11448 * It's not possible to request for a production box currently, as it
11449 * communicates via e-mail.
11450 * XXX: This function does not work either. Server complains about invalid
11451 * e-mail address.
11452 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11453 * this function
11454 * @context is special session context for box creation request. DO NOT use
11455 * standard context as it could reveal your password. Use fresh new context or
11456 * context previously used by this function.
11457 * @box is box description to create including single primary user (in case of
11458 * FO box type). It outputs box ID assigned by ISDS in dbID element.
11459 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11460 * box, or contact address of PFO box owner). The email member is mandatory as
11461 * it will be used to deliver credentials.
11462 * @former_names is former name of box owner. Pass NULL if you don't care.
11463 * @approval is optional external approval of box manipulation
11464 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11465 * NULL, if you don't care.*/
11466 isds_error isds_request_new_testing_box(struct isds_ctx *context,
11467 struct isds_DbOwnerInfo *box, const struct isds_list *users,
11468 const char *former_names, const struct isds_approval *approval,
11469 char **refnumber) {
11470 isds_error err = IE_SUCCESS;
11471 #if HAVE_LIBCURL
11472 xmlNodePtr request = NULL;
11473 xmlDocPtr response = NULL;
11474 xmlXPathContextPtr xpath_ctx = NULL;
11475 xmlXPathObjectPtr result = NULL;
11476 #endif
11479 if (!context) return IE_INVALID_CONTEXT;
11480 zfree(context->long_message);
11481 if (!box) return IE_INVAL;
11483 #if HAVE_LIBCURL
11484 if (!box->email || box->email[0] == '\0') {
11485 isds_log_message(context, _("E-mail field is mandatory"));
11486 return IE_INVAL;
11489 /* Scratch box ID */
11490 zfree(box->dbID);
11492 /* Store configuration */
11493 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
11494 free(context->url);
11495 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
11496 if (!(context->url))
11497 return IE_NOMEM;
11499 /* Prepare CURL handle if not yet connected */
11500 if (!context->curl) {
11501 context->curl = curl_easy_init();
11502 if (!(context->curl))
11503 return IE_ERROR;
11506 /* Build CreateDataBox request */
11507 err = build_CreateDBInput_request(context,
11508 &request, BAD_CAST "CreateDataBox",
11509 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
11510 if (err) goto leave;
11512 /* Send it to server and process response */
11513 err = send_destroy_request_check_response(context,
11514 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
11515 &response, (xmlChar **) refnumber, NULL);
11516 if (err) goto leave;
11518 /* Extract box ID */
11519 xpath_ctx = xmlXPathNewContext(response);
11520 if (!xpath_ctx) {
11521 err = IE_ERROR;
11522 goto leave;
11524 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11525 err = IE_ERROR;
11526 goto leave;
11528 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
11530 leave:
11531 xmlXPathFreeObject(result);
11532 xmlXPathFreeContext(xpath_ctx);
11533 xmlFreeDoc(response);
11534 xmlFreeNode(request);
11536 if (!err) {
11537 isds_log(ILF_ISDS, ILL_DEBUG,
11538 _("CreateDataBox request processed by server successfully.\n"));
11540 #else /* not HAVE_LIBCURL */
11541 err = IE_NOTSUP;
11542 #endif
11544 return err;
11548 /* Submit CMS signed message to ISDS to verify its originality. This is
11549 * stronger form of isds_verify_message_hash() because ISDS does more checks
11550 * than simple one (potentialy old weak) hash comparison.
11551 * @context is session context
11552 * @message is memory with raw CMS signed message bit stream
11553 * @length is @message size in bytes
11554 * @return
11555 * IE_SUCCESS if message originates in ISDS
11556 * IE_NOTEQUAL if message is unknown to ISDS
11557 * other code for other errors */
11558 isds_error isds_authenticate_message(struct isds_ctx *context,
11559 const void *message, size_t length) {
11560 isds_error err = IE_SUCCESS;
11561 #if HAVE_LIBCURL
11562 xmlNsPtr isds_ns = NULL;
11563 xmlNodePtr request = NULL;
11564 xmlDocPtr response = NULL;
11565 xmlXPathContextPtr xpath_ctx = NULL;
11566 xmlXPathObjectPtr result = NULL;
11567 _Bool *authentic = NULL;
11568 #endif
11570 if (!context) return IE_INVALID_CONTEXT;
11571 zfree(context->long_message);
11572 if (!message || length == 0) return IE_INVAL;
11574 #if HAVE_LIBCURL
11575 /* Check if connection is established
11576 * TODO: This check should be done downstairs. */
11577 if (!context->curl) return IE_CONNECTION_CLOSED;
11580 /* Build AuthenticateMessage request */
11581 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11582 if (!request) {
11583 isds_log_message(context,
11584 _("Could not build AuthenticateMessage request"));
11585 return IE_ERROR;
11587 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11588 if(!isds_ns) {
11589 isds_log_message(context, _("Could not create ISDS name space"));
11590 xmlFreeNode(request);
11591 return IE_ERROR;
11593 xmlSetNs(request, isds_ns);
11595 /* Insert Base64 encoded message */
11596 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11597 message, length);
11598 if (err) goto leave;
11600 /* Send request to server and process response */
11601 err = send_destroy_request_check_response(context,
11602 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11603 &response, NULL, NULL);
11604 if (err) goto leave;
11607 /* ISDS has decided */
11608 xpath_ctx = xmlXPathNewContext(response);
11609 if (!xpath_ctx) {
11610 err = IE_ERROR;
11611 goto leave;
11613 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11614 err = IE_ERROR;
11615 goto leave;
11618 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11620 if (!authentic) {
11621 isds_log_message(context,
11622 _("Server did not return any response on "
11623 "AuthenticateMessage request"));
11624 err = IE_ISDS;
11625 goto leave;
11627 if (*authentic) {
11628 isds_log(ILF_ISDS, ILL_DEBUG,
11629 _("ISDS authenticated the message successfully\n"));
11630 } else {
11631 isds_log_message(context, _("ISDS does not know the message"));
11632 err = IE_NOTEQUAL;
11636 leave:
11637 free(authentic);
11638 xmlXPathFreeObject(result);
11639 xmlXPathFreeContext(xpath_ctx);
11641 xmlFreeDoc(response);
11642 xmlFreeNode(request);
11643 #else /* not HAVE_LIBCURL */
11644 err = IE_NOTSUP;
11645 #endif
11647 return err;
11651 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11652 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11653 * be re-signed.
11654 * @context is session context
11655 * @input_data is memory with raw CMS signed message or delivery info bit
11656 * stream to re-sign
11657 * @input_length is @input_data size in bytes
11658 * @output_data is pointer to auto-allocated memory where to store re-signed
11659 * input data blob. Caller must free it.
11660 * @output_data is pointer where to store @output_data size in bytes
11661 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11662 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11663 * @return
11664 * IE_SUCCESS if CMS blob has been re-signed successfully
11665 * other code for other errors */
11666 isds_error isds_resign_message(struct isds_ctx *context,
11667 const void *input_data, size_t input_length,
11668 void **output_data, size_t *output_length, struct tm **valid_to) {
11669 isds_error err = IE_SUCCESS;
11670 #if HAVE_LIBCURL
11671 xmlNsPtr isds_ns = NULL;
11672 xmlNodePtr request = NULL;
11673 xmlDocPtr response = NULL;
11674 xmlXPathContextPtr xpath_ctx = NULL;
11675 xmlXPathObjectPtr result = NULL;
11676 char *string = NULL;
11677 const xmlChar *codes[] = {
11678 BAD_CAST "2200",
11679 BAD_CAST "2201",
11680 BAD_CAST "2204",
11681 BAD_CAST "2207",
11682 NULL
11684 const char *meanings[] = {
11685 "Message is bad",
11686 "Message is not original",
11687 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11688 "Time stamp could not been generated in time"
11690 const isds_error errors[] = {
11691 IE_INVAL,
11692 IE_NOTUNIQ,
11693 IE_INVAL,
11694 IE_ISDS,
11696 struct code_map_isds_error map = {
11697 .codes = codes,
11698 .meanings = meanings,
11699 .errors = errors
11701 #endif
11703 if (NULL != output_data) *output_data = NULL;
11704 if (NULL != output_length) *output_length = 0;
11705 if (NULL != valid_to) *valid_to = NULL;
11707 if (NULL == context) return IE_INVALID_CONTEXT;
11708 zfree(context->long_message);
11709 if (NULL == input_data || 0 == input_length) {
11710 isds_log_message(context, _("Empty CMS blob on input"));
11711 return IE_INVAL;
11713 if (NULL == output_data || NULL == output_length) {
11714 isds_log_message(context,
11715 _("NULL pointer provided for output CMS blob"));
11716 return IE_INVAL;
11719 #if HAVE_LIBCURL
11720 /* Check if connection is established
11721 * TODO: This check should be done downstairs. */
11722 if (!context->curl) return IE_CONNECTION_CLOSED;
11725 /* Build Re-signISDSDocument request */
11726 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11727 if (!request) {
11728 isds_log_message(context,
11729 _("Could not build Re-signISDSDocument request"));
11730 return IE_ERROR;
11732 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11733 if(!isds_ns) {
11734 isds_log_message(context, _("Could not create ISDS name space"));
11735 xmlFreeNode(request);
11736 return IE_ERROR;
11738 xmlSetNs(request, isds_ns);
11740 /* Insert Base64 encoded CMS blob */
11741 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11742 input_data, input_length);
11743 if (err) goto leave;
11745 /* Send request to server and process response */
11746 err = send_destroy_request_check_response(context,
11747 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11748 &response, NULL, &map);
11749 if (err) goto leave;
11752 /* Extract re-signed data */
11753 xpath_ctx = xmlXPathNewContext(response);
11754 if (!xpath_ctx) {
11755 err = IE_ERROR;
11756 goto leave;
11758 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11759 err = IE_ERROR;
11760 goto leave;
11762 result = xmlXPathEvalExpression(
11763 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11764 if (!result) {
11765 err = IE_ERROR;
11766 goto leave;
11768 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11769 isds_log_message(context,
11770 _("Missing Re-signISDSDocumentResponse element"));
11771 err = IE_ISDS;
11772 goto leave;
11774 if (result->nodesetval->nodeNr > 1) {
11775 isds_log_message(context,
11776 _("Multiple Re-signISDSDocumentResponse element"));
11777 err = IE_ISDS;
11778 goto leave;
11780 xpath_ctx->node = result->nodesetval->nodeTab[0];
11781 xmlXPathFreeObject(result); result = NULL;
11783 EXTRACT_STRING("isds:dmResultDoc", string);
11784 /* Decode non-empty data */
11785 if (NULL != string && string[0] != '\0') {
11786 *output_length = _isds_b64decode(string, output_data);
11787 if (*output_length == (size_t) -1) {
11788 isds_log_message(context,
11789 _("Error while Base64-decoding re-signed data"));
11790 err = IE_ERROR;
11791 goto leave;
11793 } else {
11794 isds_log_message(context, _("Server did not send re-signed data"));
11795 err = IE_ISDS;
11796 goto leave;
11798 zfree(string);
11800 if (NULL != valid_to) {
11801 /* Get time stamp expiration date */
11802 EXTRACT_STRING("isds:dmValidTo", string);
11803 if (NULL != string) {
11804 *valid_to = calloc(1, sizeof(**valid_to));
11805 if (!*valid_to) {
11806 err = IE_NOMEM;
11807 goto leave;
11809 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11810 if (err) {
11811 if (err == IE_NOTSUP) {
11812 err = IE_ISDS;
11813 char *string_locale = _isds_utf82locale(string);
11814 isds_printf_message(context,
11815 _("Invalid dmValidTo value: %s"), string_locale);
11816 free(string_locale);
11818 goto leave;
11823 leave:
11824 free(string);
11826 xmlXPathFreeObject(result);
11827 xmlXPathFreeContext(xpath_ctx);
11829 xmlFreeDoc(response);
11830 xmlFreeNode(request);
11831 #else /* not HAVE_LIBCURL */
11832 err = IE_NOTSUP;
11833 #endif
11835 return err;
11838 #undef INSERT_ELEMENT
11839 #undef CHECK_FOR_STRING_LENGTH
11840 #undef INSERT_STRING_ATTRIBUTE
11841 #undef INSERT_ULONGINTNOPTR
11842 #undef INSERT_ULONGINT
11843 #undef INSERT_LONGINT
11844 #undef INSERT_BOOLEAN
11845 #undef INSERT_SCALAR_BOOLEAN
11846 #undef INSERT_STRING
11847 #undef INSERT_STRING_WITH_NS
11848 #undef EXTRACT_STRING_ATTRIBUTE
11849 #undef EXTRACT_ULONGINT
11850 #undef EXTRACT_LONGINT
11851 #undef EXTRACT_BOOLEAN
11852 #undef EXTRACT_STRING
11855 /* Compute hash of message from raw representation and store it into envelope.
11856 * Original hash structure will be destroyed in envelope.
11857 * @context is session context
11858 * @message is message carrying raw XML message blob
11859 * @algorithm is desired hash algorithm to use */
11860 isds_error isds_compute_message_hash(struct isds_ctx *context,
11861 struct isds_message *message, const isds_hash_algorithm algorithm) {
11862 isds_error err = IE_SUCCESS;
11863 const char *nsuri;
11864 void *xml_stream = NULL;
11865 size_t xml_stream_length;
11866 size_t phys_start, phys_end;
11867 char *phys_path = NULL;
11868 struct isds_hash *new_hash = NULL;
11871 if (!context) return IE_INVALID_CONTEXT;
11872 zfree(context->long_message);
11873 if (!message) return IE_INVAL;
11875 if (!message->raw) {
11876 isds_log_message(context,
11877 _("Message does not carry raw representation"));
11878 return IE_INVAL;
11881 switch (message->raw_type) {
11882 case RAWTYPE_INCOMING_MESSAGE:
11883 nsuri = ISDS_NS;
11884 xml_stream = message->raw;
11885 xml_stream_length = message->raw_length;
11886 break;
11888 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11889 nsuri = SISDS_INCOMING_NS;
11890 xml_stream = message->raw;
11891 xml_stream_length = message->raw_length;
11892 break;
11894 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11895 nsuri = SISDS_INCOMING_NS;
11896 err = _isds_extract_cms_data(context,
11897 message->raw, message->raw_length,
11898 &xml_stream, &xml_stream_length);
11899 if (err) goto leave;
11900 break;
11902 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11903 nsuri = SISDS_OUTGOING_NS;
11904 xml_stream = message->raw;
11905 xml_stream_length = message->raw_length;
11906 break;
11908 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11909 nsuri = SISDS_OUTGOING_NS;
11910 err = _isds_extract_cms_data(context,
11911 message->raw, message->raw_length,
11912 &xml_stream, &xml_stream_length);
11913 if (err) goto leave;
11914 break;
11916 default:
11917 isds_log_message(context, _("Bad raw representation type"));
11918 return IE_INVAL;
11919 break;
11923 /* XXX: Hash is computed from original string representing isds:dmDm
11924 * subtree. That means no encoding, white space, xmlns attributes changes.
11925 * In other words, input for hash can be invalid XML stream. */
11926 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
11927 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11928 PHYSXML_ELEMENT_SEPARATOR,
11929 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
11930 PHYSXML_ELEMENT_SEPARATOR
11931 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
11932 err = IE_NOMEM;
11933 goto leave;
11935 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11936 phys_path, &phys_start, &phys_end);
11937 zfree(phys_path);
11938 if (err) {
11939 isds_log_message(context,
11940 _("Substring with isds:dmDM element could not be located "
11941 "in raw message"));
11942 goto leave;
11946 /* Compute hash */
11947 new_hash = calloc(1, sizeof(*new_hash));
11948 if (!new_hash) {
11949 err = IE_NOMEM;
11950 goto leave;
11952 new_hash->algorithm = algorithm;
11953 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
11954 new_hash);
11955 if (err) {
11956 isds_log_message(context, _("Could not compute message hash"));
11957 goto leave;
11960 /* Save computed hash */
11961 if (!message->envelope) {
11962 message->envelope = calloc(1, sizeof(*message->envelope));
11963 if (!message->envelope) {
11964 err = IE_NOMEM;
11965 goto leave;
11968 isds_hash_free(&message->envelope->hash);
11969 message->envelope->hash = new_hash;
11971 leave:
11972 if (err) {
11973 isds_hash_free(&new_hash);
11976 free(phys_path);
11977 if (xml_stream != message->raw) free(xml_stream);
11978 return err;
11982 /* Compare two hashes.
11983 * @h1 is first hash
11984 * @h2 is another hash
11985 * @return
11986 * IE_SUCCESS if hashes equal
11987 * IE_NOTUNIQ if hashes are comparable, but they don't equal
11988 * IE_ENUM if not comparable, but both structures defined
11989 * IE_INVAL if some of the structures are undefined (NULL)
11990 * IE_ERROR if internal error occurs */
11991 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
11992 if (h1 == NULL || h2 == NULL) return IE_INVAL;
11993 if (h1->algorithm != h2->algorithm) return IE_ENUM;
11994 if (h1->length != h2->length) return IE_ERROR;
11995 if (h1->length > 0 && !h1->value) return IE_ERROR;
11996 if (h2->length > 0 && !h2->value) return IE_ERROR;
11998 for (int i = 0; i < h1->length; i++) {
11999 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12000 return IE_NOTEQUAL;
12002 return IE_SUCCESS;
12006 /* Check message has gone through ISDS by comparing message hash stored in
12007 * ISDS and locally computed hash. You must provide message with valid raw
12008 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12009 * This is convenient wrapper for isds_download_message_hash(),
12010 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12011 * @context is session context
12012 * @message is message with valid raw and envelope member; envelope->hash
12013 * member will be changed during function run. Use envelope on heap only.
12014 * @return
12015 * IE_SUCCESS if message originates in ISDS
12016 * IE_NOTEQUAL if message is unknown to ISDS
12017 * other code for other errors */
12018 isds_error isds_verify_message_hash(struct isds_ctx *context,
12019 struct isds_message *message) {
12020 isds_error err = IE_SUCCESS;
12021 struct isds_hash *downloaded_hash = NULL;
12023 if (!context) return IE_INVALID_CONTEXT;
12024 zfree(context->long_message);
12025 if (!message) return IE_INVAL;
12027 if (!message->envelope) {
12028 isds_log_message(context,
12029 _("Given message structure is missing envelope"));
12030 return IE_INVAL;
12032 if (!message->raw) {
12033 isds_log_message(context,
12034 _("Given message structure is missing raw representation"));
12035 return IE_INVAL;
12038 err = isds_download_message_hash(context, message->envelope->dmID,
12039 &downloaded_hash);
12040 if (err) goto leave;
12042 err = isds_compute_message_hash(context, message,
12043 downloaded_hash->algorithm);
12044 if (err) goto leave;
12046 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12048 leave:
12049 isds_hash_free(&downloaded_hash);
12050 return err;
12054 /* Search for document by document ID in list of documents. IDs are compared
12055 * as UTF-8 string.
12056 * @documents is list of isds_documents
12057 * @id is document identifier
12058 * @return first matching document or NULL. */
12059 const struct isds_document *isds_find_document_by_id(
12060 const struct isds_list *documents, const char *id) {
12061 const struct isds_list *item;
12062 const struct isds_document *document;
12064 for (item = documents; item; item = item->next) {
12065 document = (struct isds_document *) item->data;
12066 if (!document) continue;
12068 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12069 return document;
12072 return NULL;
12076 /* Normalize @mime_type to be proper MIME type.
12077 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12078 * guess regular MIME type (e.g. "application/pdf").
12079 * @mime_type is UTF-8 encoded MIME type to fix
12080 * @return original @mime_type if no better interpretation exists, or
12081 * constant static UTF-8 encoded string with proper MIME type. */
12082 const char *isds_normalize_mime_type(const char *mime_type) {
12083 if (!mime_type) return NULL;
12085 for (int offset = 0;
12086 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12087 offset += 2) {
12088 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12089 extension_map_mime[offset]))
12090 return (const char *) extension_map_mime[offset + 1];
12093 return mime_type;
12097 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12098 struct isds_message **message);
12099 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12100 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12101 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12102 struct isds_address **address);
12104 int isds_message_free(struct isds_message **message);
12105 int isds_address_free(struct isds_address **address);
12109 /* Makes known all relevant namespaces to given XPath context
12110 * @xpath_ctx is XPath context
12111 * @message_ns selects proper message name space. Unsigned and signed
12112 * messages and delivery info's differ in prefix and URI. */
12113 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12114 const message_ns_type message_ns) {
12115 const xmlChar *message_namespace = NULL;
12117 if (!xpath_ctx) return IE_ERROR;
12119 switch(message_ns) {
12120 case MESSAGE_NS_1:
12121 message_namespace = BAD_CAST ISDS1_NS; break;
12122 case MESSAGE_NS_UNSIGNED:
12123 message_namespace = BAD_CAST ISDS_NS; break;
12124 case MESSAGE_NS_SIGNED_INCOMING:
12125 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12126 case MESSAGE_NS_SIGNED_OUTGOING:
12127 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12128 case MESSAGE_NS_SIGNED_DELIVERY:
12129 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12130 default:
12131 return IE_ENUM;
12134 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12135 return IE_ERROR;
12136 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12137 return IE_ERROR;
12138 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12139 return IE_ERROR;
12140 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12141 return IE_ERROR;
12142 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12143 return IE_ERROR;
12144 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12145 return IE_ERROR;
12146 return IE_SUCCESS;