Fix XPath context in isds_find_box_by_fulltext()
[libisds.git] / src / isds.c
blob58d45171b8dc9e705c67b8965a723b13808d4ecb
1 #include "isds_priv.h"
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include <stdint.h>
8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
9 #include "utils.h"
10 #if HAVE_LIBCURL
11 #include "soap.h"
12 #endif
13 #include "validator.h"
14 #include "crypto.h"
15 #include "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 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
2231 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2232 if (-1 == isds_asprintf((char **) string,
2233 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
2234 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2235 broken.tm_hour, broken.tm_min, broken.tm_sec,
2236 time->tv_usec))
2237 return IE_ERROR;
2239 return IE_SUCCESS;
2241 #endif /* HAVE_LIBCURL */
2244 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2245 * It respects microseconds too.
2246 * In case of error, @time will be freed. */
2247 static isds_error timestring2timeval(const xmlChar *string,
2248 struct timeval **time) {
2249 struct tm broken;
2250 char *offset, *delim, *endptr;
2251 char subseconds[7];
2252 int offset_hours, offset_minutes;
2253 int i;
2254 #ifdef _WIN32
2255 int tmp;
2256 #endif
2258 if (!time) return IE_INVAL;
2259 if (!string) {
2260 zfree(*time);
2261 return IE_INVAL;
2264 memset(&broken, 0, sizeof(broken));
2266 if (!*time) {
2267 *time = calloc(1, sizeof(**time));
2268 if (!*time) return IE_NOMEM;
2269 } else {
2270 memset(*time, 0, sizeof(**time));
2274 /* xsd:date is ISO 8601 string, thus ASCII */
2275 /*TODO: negative year */
2277 #ifdef _WIN32
2278 i = 0;
2279 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2280 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2281 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2282 &i)) < 6) {
2283 zfree(*time);
2284 return IE_DATE;
2287 broken.tm_year -= 1900;
2288 broken.tm_mon--;
2289 offset = (char*)string + i;
2290 #else
2291 /* Parse date and time without subseconds and offset */
2292 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2293 if (!offset) {
2294 zfree(*time);
2295 return IE_DATE;
2297 #endif
2299 /* Get subseconds */
2300 if (*offset == '.' ) {
2301 offset++;
2303 /* Copy first 6 digits, pad it with zeros.
2304 * XXX: It truncates longer number, no round.
2305 * Current server implementation uses only millisecond resolution. */
2306 /* TODO: isdigit() is locale sensitive */
2307 for (i = 0;
2308 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
2309 i++, offset++) {
2310 subseconds[i] = *offset;
2312 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
2313 subseconds[i] = '0';
2315 subseconds[6] = '\0';
2317 /* Convert it into integer */
2318 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
2319 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
2320 (*time)->tv_usec == LONG_MAX) {
2321 zfree(*time);
2322 return IE_DATE;
2325 /* move to the zone offset delimiter or signal NULL*/
2326 delim = strchr(offset, '-');
2327 if (!delim)
2328 delim = strchr(offset, '+');
2329 if (!delim)
2330 delim = strchr(offset, 'Z');
2331 offset = delim;
2334 /* Get zone offset */
2335 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2336 * "" equals to "Z" and it means UTC zone. */
2337 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2338 * colon separator */
2339 if (offset && (*offset == '-' || *offset == '+')) {
2340 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2341 zfree(*time);
2342 return IE_DATE;
2344 if (*offset == '+') {
2345 broken.tm_hour -= offset_hours;
2346 broken.tm_min -= offset_minutes;
2347 } else {
2348 broken.tm_hour += offset_hours;
2349 broken.tm_min += offset_minutes;
2353 /* Convert to time_t */
2354 (*time)->tv_sec = _isds_timegm(&broken);
2355 if ((*time)->tv_sec == (time_t) -1) {
2356 zfree(*time);
2357 return IE_DATE;
2360 return IE_SUCCESS;
2364 /* Convert unsigned int into isds_message_status.
2365 * @context is session context
2366 * @number is pointer to number value. NULL will be treated as invalid value.
2367 * @status is automatically reallocated status
2368 * @return IE_SUCCESS, or error code and free status */
2369 static isds_error uint2isds_message_status(struct isds_ctx *context,
2370 const unsigned long int *number, isds_message_status **status) {
2371 if (!context) return IE_INVALID_CONTEXT;
2372 if (!status) return IE_INVAL;
2374 free(*status); *status = NULL;
2375 if (!number) return IE_INVAL;
2377 if (*number < 1 || *number > 10) {
2378 isds_printf_message(context, _("Invalid message status value: %lu"),
2379 *number);
2380 return IE_ENUM;
2383 *status = malloc(sizeof(**status));
2384 if (!*status) return IE_NOMEM;
2386 **status = 1 << *number;
2387 return IE_SUCCESS;
2391 /* Convert event description string into isds_event members type and
2392 * description
2393 * @string is raw event description starting with event prefix
2394 * @event is structure where to store type and stripped description to
2395 * @return standard error code, unknown prefix is not classified as an error.
2396 * */
2397 static isds_error eventstring2event(const xmlChar *string,
2398 struct isds_event* event) {
2399 const xmlChar *known_prefixes[] = {
2400 BAD_CAST "EV0:",
2401 BAD_CAST "EV1:",
2402 BAD_CAST "EV2:",
2403 BAD_CAST "EV3:",
2404 BAD_CAST "EV4:",
2405 BAD_CAST "EV5:",
2406 BAD_CAST "EV11:",
2407 BAD_CAST "EV12:",
2408 BAD_CAST "EV13:"
2410 const isds_event_type types[] = {
2411 EVENT_ENTERED_SYSTEM,
2412 EVENT_ACCEPTED_BY_RECIPIENT,
2413 EVENT_ACCEPTED_BY_FICTION,
2414 EVENT_UNDELIVERABLE,
2415 EVENT_COMMERCIAL_ACCEPTED,
2416 EVENT_DELIVERED,
2417 EVENT_PRIMARY_LOGIN,
2418 EVENT_ENTRUSTED_LOGIN,
2419 EVENT_SYSCERT_LOGIN
2421 unsigned int index;
2422 size_t length;
2424 if (!string || !event) return IE_INVAL;
2426 if (!event->type) {
2427 event->type = malloc(sizeof(*event->type));
2428 if (!(event->type)) return IE_NOMEM;
2430 zfree(event->description);
2432 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2433 index++) {
2434 length = xmlUTF8Strlen(known_prefixes[index]);
2436 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2437 /* Prefix is known */
2438 *event->type = types[index];
2440 /* Strip prefix from description and spaces */
2441 /* TODO: Recognize all white spaces from UCS blank class and
2442 * operate on UTF-8 chars. */
2443 for (; string[length] != '\0' && string[length] == ' '; length++);
2444 event->description = strdup((char *) (string + length));
2445 if (!(event->description)) return IE_NOMEM;
2447 return IE_SUCCESS;
2451 /* Unknown event prefix.
2452 * XSD allows any string */
2453 char *string_locale = _isds_utf82locale((char *) string);
2454 isds_log(ILF_ISDS, ILL_WARNING,
2455 _("Unknown delivery info event prefix: %s\n"), string_locale);
2456 free(string_locale);
2458 *event->type = EVENT_UKNOWN;
2459 event->description = strdup((char *) string);
2460 if (!(event->description)) return IE_NOMEM;
2462 return IE_SUCCESS;
2466 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2467 * and leave label */
2468 #define EXTRACT_STRING(element, string) { \
2469 xmlXPathFreeObject(result); \
2470 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2471 if (NULL == (result)) { \
2472 err = IE_ERROR; \
2473 goto leave; \
2475 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2476 if (result->nodesetval->nodeNr > 1) { \
2477 isds_printf_message(context, _("Multiple %s element"), element); \
2478 err = IE_ERROR; \
2479 goto leave; \
2481 (string) = (char *) \
2482 xmlXPathCastNodeSetToString(result->nodesetval); \
2483 if (NULL == (string)) { \
2484 err = IE_ERROR; \
2485 goto leave; \
2490 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2492 char *string = NULL; \
2493 EXTRACT_STRING(element, string); \
2495 if (string) { \
2496 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2497 if (!(booleanPtr)) { \
2498 free(string); \
2499 err = IE_NOMEM; \
2500 goto leave; \
2503 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2504 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2505 *(booleanPtr) = 1; \
2506 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2507 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2508 *(booleanPtr) = 0; \
2509 else { \
2510 char *string_locale = _isds_utf82locale((char*)string); \
2511 isds_printf_message(context, \
2512 _("%s value is not valid boolean: %s"), \
2513 element, string_locale); \
2514 free(string_locale); \
2515 free(string); \
2516 err = IE_ERROR; \
2517 goto leave; \
2520 free(string); \
2524 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2526 char *string = NULL; \
2527 EXTRACT_STRING(element, string); \
2529 if (NULL == string) { \
2530 isds_printf_message(context, _("%s element is empty"), element); \
2531 err = IE_ERROR; \
2532 goto leave; \
2534 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2535 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2536 (boolean) = 1; \
2537 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2538 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2539 (boolean) = 0; \
2540 else { \
2541 char *string_locale = _isds_utf82locale((char*)string); \
2542 isds_printf_message(context, \
2543 _("%s value is not valid boolean: %s"), \
2544 element, string_locale); \
2545 free(string_locale); \
2546 free(string); \
2547 err = IE_ERROR; \
2548 goto leave; \
2551 free(string); \
2554 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2556 char *string = NULL; \
2557 EXTRACT_STRING(element, string); \
2558 if (string) { \
2559 long int number; \
2560 char *endptr; \
2562 number = strtol((char*)string, &endptr, 10); \
2564 if (*endptr != '\0') { \
2565 char *string_locale = _isds_utf82locale((char *)string); \
2566 isds_printf_message(context, \
2567 _("%s is not valid integer: %s"), \
2568 element, string_locale); \
2569 free(string_locale); \
2570 free(string); \
2571 err = IE_ISDS; \
2572 goto leave; \
2575 if (number == LONG_MIN || number == LONG_MAX) { \
2576 char *string_locale = _isds_utf82locale((char *)string); \
2577 isds_printf_message(context, \
2578 _("%s value out of range of long int: %s"), \
2579 element, string_locale); \
2580 free(string_locale); \
2581 free(string); \
2582 err = IE_ERROR; \
2583 goto leave; \
2586 free(string); string = NULL; \
2588 if (!(preallocated)) { \
2589 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2590 if (!(longintPtr)) { \
2591 err = IE_NOMEM; \
2592 goto leave; \
2595 *(longintPtr) = number; \
2599 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2601 char *string = NULL; \
2602 EXTRACT_STRING(element, string); \
2603 if (string) { \
2604 long int number; \
2605 char *endptr; \
2607 number = strtol((char*)string, &endptr, 10); \
2609 if (*endptr != '\0') { \
2610 char *string_locale = _isds_utf82locale((char *)string); \
2611 isds_printf_message(context, \
2612 _("%s is not valid integer: %s"), \
2613 element, string_locale); \
2614 free(string_locale); \
2615 free(string); \
2616 err = IE_ISDS; \
2617 goto leave; \
2620 if (number == LONG_MIN || number == LONG_MAX) { \
2621 char *string_locale = _isds_utf82locale((char *)string); \
2622 isds_printf_message(context, \
2623 _("%s value out of range of long int: %s"), \
2624 element, string_locale); \
2625 free(string_locale); \
2626 free(string); \
2627 err = IE_ERROR; \
2628 goto leave; \
2631 free(string); string = NULL; \
2632 if (number < 0) { \
2633 isds_printf_message(context, \
2634 _("%s value is negative: %ld"), element, number); \
2635 err = IE_ERROR; \
2636 goto leave; \
2639 if (!(preallocated)) { \
2640 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2641 if (!(ulongintPtr)) { \
2642 err = IE_NOMEM; \
2643 goto leave; \
2646 *(ulongintPtr) = number; \
2650 #define EXTRACT_DATE(element, tmPtr) { \
2651 char *string = NULL; \
2652 EXTRACT_STRING(element, string); \
2653 if (NULL != string) { \
2654 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2655 if (NULL == (tmPtr)) { \
2656 free(string); \
2657 err = IE_NOMEM; \
2658 goto leave; \
2660 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2661 if (err) { \
2662 if (err == IE_NOTSUP) { \
2663 err = IE_ISDS; \
2664 char *string_locale = _isds_utf82locale(string); \
2665 char *element_locale = _isds_utf82locale(element); \
2666 isds_printf_message(context, _("Invalid %s value: %s"), \
2667 element_locale, string_locale); \
2668 free(string_locale); \
2669 free(element_locale); \
2671 free(string); \
2672 goto leave; \
2674 free(string); \
2678 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2679 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2680 NULL); \
2681 if ((required) && (!string)) { \
2682 char *attribute_locale = _isds_utf82locale(attribute); \
2683 char *element_locale = \
2684 _isds_utf82locale((char *)xpath_ctx->node->name); \
2685 isds_printf_message(context, \
2686 _("Could not extract required %s attribute value from " \
2687 "%s element"), attribute_locale, element_locale); \
2688 free(element_locale); \
2689 free(attribute_locale); \
2690 err = IE_ERROR; \
2691 goto leave; \
2696 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2698 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2699 (xmlChar *) (string)); \
2700 if (!node) { \
2701 isds_printf_message(context, \
2702 _("Could not add %s child to %s element"), \
2703 element, (parent)->name); \
2704 err = IE_ERROR; \
2705 goto leave; \
2709 #define INSERT_STRING(parent, element, string) \
2710 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2712 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2714 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2715 else { INSERT_STRING(parent, element, "false"); } \
2718 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2720 if (booleanPtr) { \
2721 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2722 } else { \
2723 INSERT_STRING(parent, element, NULL); \
2727 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2728 if ((longintPtr)) { \
2729 /* FIXME: locale sensitive */ \
2730 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2731 err = IE_NOMEM; \
2732 goto leave; \
2734 INSERT_STRING(parent, element, buffer) \
2735 free(buffer); (buffer) = NULL; \
2736 } else { INSERT_STRING(parent, element, NULL) } \
2739 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2740 if ((ulongintPtr)) { \
2741 /* FIXME: locale sensitive */ \
2742 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2743 err = IE_NOMEM; \
2744 goto leave; \
2746 INSERT_STRING(parent, element, buffer) \
2747 free(buffer); (buffer) = NULL; \
2748 } else { INSERT_STRING(parent, element, NULL) } \
2751 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2753 /* FIXME: locale sensitive */ \
2754 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2755 err = IE_NOMEM; \
2756 goto leave; \
2758 INSERT_STRING(parent, element, buffer) \
2759 free(buffer); (buffer) = NULL; \
2762 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2763 * new attribute. */
2764 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2766 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2767 (xmlChar *) (string)); \
2768 if (!attribute_node) { \
2769 isds_printf_message(context, _("Could not add %s " \
2770 "attribute to %s element"), \
2771 (attribute), (parent)->name); \
2772 err = IE_ERROR; \
2773 goto leave; \
2777 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2778 if (string) { \
2779 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2780 if (length > (maximum)) { \
2781 isds_printf_message(context, \
2782 ngettext("%s has more than %d characters", \
2783 "%s has more than %d characters", (maximum)), \
2784 (name), (maximum)); \
2785 err = IE_2BIG; \
2786 goto leave; \
2788 if (length < (minimum)) { \
2789 isds_printf_message(context, \
2790 ngettext("%s has less than %d characters", \
2791 "%s has less than %d characters", (minimum)), \
2792 (name), (minimum)); \
2793 err = IE_2SMALL; \
2794 goto leave; \
2799 #define INSERT_ELEMENT(child, parent, element) \
2801 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2802 if (!(child)) { \
2803 isds_printf_message(context, \
2804 _("Could not add %s child to %s element"), \
2805 (element), (parent)->name); \
2806 err = IE_ERROR; \
2807 goto leave; \
2812 /* Find child element by name in given XPath context and switch context onto
2813 * it. The child must be uniq and must exist. Otherwise fails.
2814 * @context is ISDS context
2815 * @child is child element name
2816 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2817 * into it child. In error case, the @xpath_ctx keeps original value. */
2818 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2819 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2820 isds_error err = IE_SUCCESS;
2821 xmlXPathObjectPtr result = NULL;
2823 if (!context) return IE_INVALID_CONTEXT;
2824 if (!child || !xpath_ctx) return IE_INVAL;
2826 /* Find child */
2827 result = xmlXPathEvalExpression(child, xpath_ctx);
2828 if (!result) {
2829 err = IE_XML;
2830 goto leave;
2833 /* No match */
2834 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2835 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2836 char *child_locale = _isds_utf82locale((char*) child);
2837 isds_printf_message(context,
2838 _("%s element does not contain %s child"),
2839 parent_locale, child_locale);
2840 free(child_locale);
2841 free(parent_locale);
2842 err = IE_NOEXIST;
2843 goto leave;
2846 /* More matches */
2847 if (result->nodesetval->nodeNr > 1) {
2848 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2849 char *child_locale = _isds_utf82locale((char*) child);
2850 isds_printf_message(context,
2851 _("%s element contains multiple %s children"),
2852 parent_locale, child_locale);
2853 free(child_locale);
2854 free(parent_locale);
2855 err = IE_NOTUNIQ;
2856 goto leave;
2859 /* Switch context */
2860 xpath_ctx->node = result->nodesetval->nodeTab[0];
2862 leave:
2863 xmlXPathFreeObject(result);
2864 return err;
2869 #if HAVE_LIBCURL
2870 /* Find and convert XSD:gPersonName group in current node into structure
2871 * @context is ISDS context
2872 * @personName is automatically reallocated person name structure. If no member
2873 * value is found, will be freed.
2874 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2875 * elements
2876 * In case of error @personName will be freed. */
2877 static isds_error extract_gPersonName(struct isds_ctx *context,
2878 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2879 isds_error err = IE_SUCCESS;
2880 xmlXPathObjectPtr result = NULL;
2882 if (!context) return IE_INVALID_CONTEXT;
2883 if (!personName) return IE_INVAL;
2884 isds_PersonName_free(personName);
2885 if (!xpath_ctx) return IE_INVAL;
2888 *personName = calloc(1, sizeof(**personName));
2889 if (!*personName) {
2890 err = IE_NOMEM;
2891 goto leave;
2894 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2895 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2896 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2897 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2899 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2900 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2901 isds_PersonName_free(personName);
2903 leave:
2904 if (err) isds_PersonName_free(personName);
2905 xmlXPathFreeObject(result);
2906 return err;
2910 /* Find and convert XSD:gAddress group in current node into structure
2911 * @context is ISDS context
2912 * @address is automatically reallocated address structure. If no member
2913 * value is found, will be freed.
2914 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2915 * elements
2916 * In case of error @address will be freed. */
2917 static isds_error extract_gAddress(struct isds_ctx *context,
2918 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2919 isds_error err = IE_SUCCESS;
2920 xmlXPathObjectPtr result = NULL;
2922 if (!context) return IE_INVALID_CONTEXT;
2923 if (!address) return IE_INVAL;
2924 isds_Address_free(address);
2925 if (!xpath_ctx) return IE_INVAL;
2928 *address = calloc(1, sizeof(**address));
2929 if (!*address) {
2930 err = IE_NOMEM;
2931 goto leave;
2934 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2935 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2936 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2937 EXTRACT_STRING("isds:adNumberInMunicipality",
2938 (*address)->adNumberInMunicipality);
2939 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2940 EXTRACT_STRING("isds:adState", (*address)->adState);
2942 if (!(*address)->adCity && !(*address)->adStreet &&
2943 !(*address)->adNumberInStreet &&
2944 !(*address)->adNumberInMunicipality &&
2945 !(*address)->adZipCode && !(*address)->adState)
2946 isds_Address_free(address);
2948 leave:
2949 if (err) isds_Address_free(address);
2950 xmlXPathFreeObject(result);
2951 return err;
2955 /* Find and convert isds:biDate element in current node into structure
2956 * @context is ISDS context
2957 * @biDate is automatically reallocated birth date structure. If no member
2958 * value is found, will be freed.
2959 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2960 * element
2961 * In case of error @biDate will be freed. */
2962 static isds_error extract_BiDate(struct isds_ctx *context,
2963 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2964 isds_error err = IE_SUCCESS;
2965 xmlXPathObjectPtr result = NULL;
2966 char *string = NULL;
2968 if (!context) return IE_INVALID_CONTEXT;
2969 if (!biDate) return IE_INVAL;
2970 zfree(*biDate);
2971 if (!xpath_ctx) return IE_INVAL;
2973 EXTRACT_STRING("isds:biDate", string);
2974 if (string) {
2975 *biDate = calloc(1, sizeof(**biDate));
2976 if (!*biDate) {
2977 err = IE_NOMEM;
2978 goto leave;
2980 err = _isds_datestring2tm((xmlChar *)string, *biDate);
2981 if (err) {
2982 if (err == IE_NOTSUP) {
2983 err = IE_ISDS;
2984 char *string_locale = _isds_utf82locale(string);
2985 isds_printf_message(context,
2986 _("Invalid isds:biDate value: %s"), string_locale);
2987 free(string_locale);
2989 goto leave;
2993 leave:
2994 if (err) zfree(*biDate);
2995 free(string);
2996 xmlXPathFreeObject(result);
2997 return err;
3001 /* Convert isds:dBOwnerInfo XML tree into structure
3002 * @context is ISDS context
3003 * @db_owner_info is automatically reallocated box owner info structure
3004 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
3005 * In case of error @db_owner_info will be freed. */
3006 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
3007 struct isds_DbOwnerInfo **db_owner_info,
3008 xmlXPathContextPtr xpath_ctx) {
3009 isds_error err = IE_SUCCESS;
3010 xmlXPathObjectPtr result = NULL;
3011 char *string = NULL;
3013 if (!context) return IE_INVALID_CONTEXT;
3014 if (!db_owner_info) return IE_INVAL;
3015 isds_DbOwnerInfo_free(db_owner_info);
3016 if (!xpath_ctx) return IE_INVAL;
3019 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3020 if (!*db_owner_info) {
3021 err = IE_NOMEM;
3022 goto leave;
3025 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
3027 EXTRACT_STRING("isds:dbType", string);
3028 if (string) {
3029 (*db_owner_info)->dbType =
3030 calloc(1, sizeof(*((*db_owner_info)->dbType)));
3031 if (!(*db_owner_info)->dbType) {
3032 err = IE_NOMEM;
3033 goto leave;
3035 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
3036 if (err) {
3037 zfree((*db_owner_info)->dbType);
3038 if (err == IE_ENUM) {
3039 err = IE_ISDS;
3040 char *string_locale = _isds_utf82locale(string);
3041 isds_printf_message(context, _("Unknown isds:dbType: %s"),
3042 string_locale);
3043 free(string_locale);
3045 goto leave;
3047 zfree(string);
3050 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
3052 err = extract_gPersonName(context, &(*db_owner_info)->personName,
3053 xpath_ctx);
3054 if (err) goto leave;
3056 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
3058 (*db_owner_info)->birthInfo =
3059 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
3060 if (!(*db_owner_info)->birthInfo) {
3061 err = IE_NOMEM;
3062 goto leave;
3064 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
3065 xpath_ctx);
3066 if (err) goto leave;
3067 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3068 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3069 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3070 if (!(*db_owner_info)->birthInfo->biDate &&
3071 !(*db_owner_info)->birthInfo->biCity &&
3072 !(*db_owner_info)->birthInfo->biCounty &&
3073 !(*db_owner_info)->birthInfo->biState)
3074 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3076 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3077 if (err) goto leave;
3079 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3080 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3081 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3082 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3083 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3085 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3087 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3088 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3089 (*db_owner_info)->dbOpenAddressing);
3091 leave:
3092 if (err) isds_DbOwnerInfo_free(db_owner_info);
3093 free(string);
3094 xmlXPathFreeObject(result);
3095 return err;
3099 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3100 * @context is session context
3101 * @owner is libisds structure with box description
3102 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3103 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3104 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
3106 isds_error err = IE_SUCCESS;
3107 xmlNodePtr node;
3108 xmlChar *string = NULL;
3110 if (!context) return IE_INVALID_CONTEXT;
3111 if (!owner || !db_owner_info) return IE_INVAL;
3114 /* Build XSD:tDbOwnerInfo */
3115 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3116 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3118 /* dbType */
3119 if (owner->dbType) {
3120 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
3121 if (!type_string) {
3122 isds_printf_message(context, _("Invalid dbType value: %d"),
3123 *(owner->dbType));
3124 err = IE_ENUM;
3125 goto leave;
3127 INSERT_STRING(db_owner_info, "dbType", type_string);
3129 INSERT_STRING(db_owner_info, "ic", owner->ic);
3130 if (owner->personName) {
3131 INSERT_STRING(db_owner_info, "pnFirstName",
3132 owner->personName->pnFirstName);
3133 INSERT_STRING(db_owner_info, "pnMiddleName",
3134 owner->personName->pnMiddleName);
3135 INSERT_STRING(db_owner_info, "pnLastName",
3136 owner->personName->pnLastName);
3137 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3138 owner->personName->pnLastNameAtBirth);
3140 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3141 if (owner->birthInfo) {
3142 if (owner->birthInfo->biDate) {
3143 if (!tm2datestring(owner->birthInfo->biDate, &string))
3144 INSERT_STRING(db_owner_info, "biDate", string);
3145 free(string); string = NULL;
3147 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
3148 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
3149 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
3151 if (owner->address) {
3152 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
3153 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
3154 INSERT_STRING(db_owner_info, "adNumberInStreet",
3155 owner->address->adNumberInStreet);
3156 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3157 owner->address->adNumberInMunicipality);
3158 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
3159 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
3161 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3162 INSERT_STRING(db_owner_info, "email", owner->email);
3163 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3165 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3166 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3168 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3169 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3171 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3173 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3174 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3175 owner->dbOpenAddressing);
3177 leave:
3178 free(string);
3179 return err;
3183 /* Convert XSD:tDbUserInfo XML tree into structure
3184 * @context is ISDS context
3185 * @db_user_info is automatically reallocated user info structure
3186 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3187 * In case of error @db_user_info will be freed. */
3188 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3189 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3190 isds_error err = IE_SUCCESS;
3191 xmlXPathObjectPtr result = NULL;
3192 char *string = NULL;
3194 if (!context) return IE_INVALID_CONTEXT;
3195 if (!db_user_info) return IE_INVAL;
3196 isds_DbUserInfo_free(db_user_info);
3197 if (!xpath_ctx) return IE_INVAL;
3200 *db_user_info = calloc(1, sizeof(**db_user_info));
3201 if (!*db_user_info) {
3202 err = IE_NOMEM;
3203 goto leave;
3206 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3208 EXTRACT_STRING("isds:userType", string);
3209 if (string) {
3210 (*db_user_info)->userType =
3211 calloc(1, sizeof(*((*db_user_info)->userType)));
3212 if (!(*db_user_info)->userType) {
3213 err = IE_NOMEM;
3214 goto leave;
3216 err = string2isds_UserType((xmlChar *)string,
3217 (*db_user_info)->userType);
3218 if (err) {
3219 zfree((*db_user_info)->userType);
3220 if (err == IE_ENUM) {
3221 err = IE_ISDS;
3222 char *string_locale = _isds_utf82locale(string);
3223 isds_printf_message(context,
3224 _("Unknown isds:userType value: %s"), string_locale);
3225 free(string_locale);
3227 goto leave;
3229 zfree(string);
3232 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3234 (*db_user_info)->personName =
3235 calloc(1, sizeof(*((*db_user_info)->personName)));
3236 if (!(*db_user_info)->personName) {
3237 err = IE_NOMEM;
3238 goto leave;
3241 err = extract_gPersonName(context, &(*db_user_info)->personName,
3242 xpath_ctx);
3243 if (err) goto leave;
3245 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3246 if (err) goto leave;
3248 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3249 if (err) goto leave;
3251 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3252 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3254 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3255 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3256 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3258 /* ???: Default value is "CZ" according specification. Should we provide
3259 * it? */
3260 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3262 leave:
3263 if (err) isds_DbUserInfo_free(db_user_info);
3264 free(string);
3265 xmlXPathFreeObject(result);
3266 return err;
3270 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3271 * @context is session context
3272 * @user is libisds structure with user description
3273 * @db_user_info is XML element of XSD:tDbUserInfo */
3274 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3275 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3277 isds_error err = IE_SUCCESS;
3278 xmlNodePtr node;
3279 xmlChar *string = NULL;
3281 if (!context) return IE_INVALID_CONTEXT;
3282 if (!user || !db_user_info) return IE_INVAL;
3284 /* Build XSD:tDbUserInfo */
3285 if (user->personName) {
3286 INSERT_STRING(db_user_info, "pnFirstName",
3287 user->personName->pnFirstName);
3288 INSERT_STRING(db_user_info, "pnMiddleName",
3289 user->personName->pnMiddleName);
3290 INSERT_STRING(db_user_info, "pnLastName",
3291 user->personName->pnLastName);
3292 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3293 user->personName->pnLastNameAtBirth);
3295 if (user->address) {
3296 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3297 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3298 INSERT_STRING(db_user_info, "adNumberInStreet",
3299 user->address->adNumberInStreet);
3300 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3301 user->address->adNumberInMunicipality);
3302 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3303 INSERT_STRING(db_user_info, "adState", user->address->adState);
3305 if (user->biDate) {
3306 if (!tm2datestring(user->biDate, &string))
3307 INSERT_STRING(db_user_info, "biDate", string);
3308 zfree(string);
3310 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3311 INSERT_STRING(db_user_info, "userID", user->userID);
3313 /* userType */
3314 if (user->userType) {
3315 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3316 if (!type_string) {
3317 isds_printf_message(context, _("Invalid userType value: %d"),
3318 *(user->userType));
3319 err = IE_ENUM;
3320 goto leave;
3322 INSERT_STRING(db_user_info, "userType", type_string);
3325 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3326 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3327 INSERT_STRING(db_user_info, "ic", user->ic);
3328 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3329 INSERT_STRING(db_user_info, "firmName", user->firmName);
3330 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3331 INSERT_STRING(db_user_info, "caCity", user->caCity);
3332 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3333 INSERT_STRING(db_user_info, "caState", user->caState);
3335 leave:
3336 free(string);
3337 return err;
3341 /* Convert XSD:tPDZRec XML tree into structure
3342 * @context is ISDS context
3343 * @permission is automatically reallocated commercial permission structure
3344 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3345 * In case of error @permission will be freed. */
3346 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3347 struct isds_commercial_permission **permission,
3348 xmlXPathContextPtr xpath_ctx) {
3349 isds_error err = IE_SUCCESS;
3350 xmlXPathObjectPtr result = NULL;
3351 char *string = NULL;
3353 if (!context) return IE_INVALID_CONTEXT;
3354 if (!permission) return IE_INVAL;
3355 isds_commercial_permission_free(permission);
3356 if (!xpath_ctx) return IE_INVAL;
3359 *permission = calloc(1, sizeof(**permission));
3360 if (!*permission) {
3361 err = IE_NOMEM;
3362 goto leave;
3365 EXTRACT_STRING("isds:PDZType", string);
3366 if (string) {
3367 err = string2isds_payment_type((xmlChar *)string,
3368 &(*permission)->type);
3369 if (err) {
3370 if (err == IE_ENUM) {
3371 err = IE_ISDS;
3372 char *string_locale = _isds_utf82locale(string);
3373 isds_printf_message(context,
3374 _("Unknown isds:PDZType value: %s"), string_locale);
3375 free(string_locale);
3377 goto leave;
3379 zfree(string);
3382 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3383 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3385 EXTRACT_STRING("isds:PDZExpire", string);
3386 if (string) {
3387 err = timestring2timeval((xmlChar *) string,
3388 &((*permission)->expiration));
3389 if (err) {
3390 char *string_locale = _isds_utf82locale(string);
3391 if (err == IE_DATE) err = IE_ISDS;
3392 isds_printf_message(context,
3393 _("Could not convert PDZExpire as ISO time: %s"),
3394 string_locale);
3395 free(string_locale);
3396 goto leave;
3398 zfree(string);
3401 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3402 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3404 leave:
3405 if (err) isds_commercial_permission_free(permission);
3406 free(string);
3407 xmlXPathFreeObject(result);
3408 return err;
3412 /* Convert XSD:tCiRecord XML tree into structure
3413 * @context is ISDS context
3414 * @event is automatically reallocated commercial credit event structure
3415 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3416 * In case of error @event will be freed. */
3417 static isds_error extract_CiRecord(struct isds_ctx *context,
3418 struct isds_credit_event **event,
3419 xmlXPathContextPtr xpath_ctx) {
3420 isds_error err = IE_SUCCESS;
3421 xmlXPathObjectPtr result = NULL;
3422 char *string = NULL;
3423 long int *number_ptr;
3425 if (!context) return IE_INVALID_CONTEXT;
3426 if (!event) return IE_INVAL;
3427 isds_credit_event_free(event);
3428 if (!xpath_ctx) return IE_INVAL;
3431 *event = calloc(1, sizeof(**event));
3432 if (!*event) {
3433 err = IE_NOMEM;
3434 goto leave;
3437 EXTRACT_STRING("isds:ciEventTime", string);
3438 if (string) {
3439 err = timestring2timeval((xmlChar *) string,
3440 &(*event)->time);
3441 if (err) {
3442 char *string_locale = _isds_utf82locale(string);
3443 if (err == IE_DATE) err = IE_ISDS;
3444 isds_printf_message(context,
3445 _("Could not convert ciEventTime as ISO time: %s"),
3446 string_locale);
3447 free(string_locale);
3448 goto leave;
3450 zfree(string);
3453 EXTRACT_STRING("isds:ciEventType", string);
3454 if (string) {
3455 err = string2isds_credit_event_type((xmlChar *)string,
3456 &(*event)->type);
3457 if (err) {
3458 if (err == IE_ENUM) {
3459 err = IE_ISDS;
3460 char *string_locale = _isds_utf82locale(string);
3461 isds_printf_message(context,
3462 _("Unknown isds:ciEventType value: %s"), string_locale);
3463 free(string_locale);
3465 goto leave;
3467 zfree(string);
3470 number_ptr = &((*event)->credit_change);
3471 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3472 number_ptr = &(*event)->new_credit;
3473 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3475 switch((*event)->type) {
3476 case ISDS_CREDIT_CHARGED:
3477 EXTRACT_STRING("isds:ciTransID",
3478 (*event)->details.charged.transaction);
3479 break;
3480 case ISDS_CREDIT_DISCHARGED:
3481 EXTRACT_STRING("isds:ciTransID",
3482 (*event)->details.discharged.transaction);
3483 break;
3484 case ISDS_CREDIT_MESSAGE_SENT:
3485 EXTRACT_STRING("isds:ciRecipientID",
3486 (*event)->details.message_sent.recipient);
3487 EXTRACT_STRING("isds:ciPDZID",
3488 (*event)->details.message_sent.message_id);
3489 break;
3490 case ISDS_CREDIT_STORAGE_SET:
3491 number_ptr = &((*event)->details.storage_set.new_capacity);
3492 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3493 EXTRACT_DATE("isds:ciNewFrom",
3494 (*event)->details.storage_set.new_valid_from);
3495 EXTRACT_DATE("isds:ciNewTo",
3496 (*event)->details.storage_set.new_valid_to);
3497 EXTRACT_LONGINT("isds:ciOldCapacity",
3498 (*event)->details.storage_set.old_capacity, 0);
3499 EXTRACT_DATE("isds:ciOldFrom",
3500 (*event)->details.storage_set.old_valid_from);
3501 EXTRACT_DATE("isds:ciOldTo",
3502 (*event)->details.storage_set.old_valid_to);
3503 EXTRACT_STRING("isds:ciDoneBy",
3504 (*event)->details.storage_set.initiator);
3505 break;
3506 case ISDS_CREDIT_EXPIRED:
3507 break;
3510 leave:
3511 if (err) isds_credit_event_free(event);
3512 free(string);
3513 xmlXPathFreeObject(result);
3514 return err;
3518 #endif /* HAVE_LIBCURL */
3521 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3522 * isds_envelope structure. The envelope is automatically allocated but not
3523 * reallocated. The date are just appended into envelope structure.
3524 * @context is ISDS context
3525 * @envelope is automatically allocated message envelope structure
3526 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3527 * In case of error @envelope will be freed. */
3528 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3529 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3530 isds_error err = IE_SUCCESS;
3531 xmlXPathObjectPtr result = NULL;
3533 if (!context) return IE_INVALID_CONTEXT;
3534 if (!envelope) return IE_INVAL;
3535 if (!xpath_ctx) return IE_INVAL;
3538 if (!*envelope) {
3539 /* Allocate envelope */
3540 *envelope = calloc(1, sizeof(**envelope));
3541 if (!*envelope) {
3542 err = IE_NOMEM;
3543 goto leave;
3545 } else {
3546 /* Else free former data */
3547 zfree((*envelope)->dmSenderOrgUnit);
3548 zfree((*envelope)->dmSenderOrgUnitNum);
3549 zfree((*envelope)->dbIDRecipient);
3550 zfree((*envelope)->dmRecipientOrgUnit);
3551 zfree((*envelope)->dmRecipientOrgUnitNum);
3552 zfree((*envelope)->dmToHands);
3553 zfree((*envelope)->dmAnnotation);
3554 zfree((*envelope)->dmRecipientRefNumber);
3555 zfree((*envelope)->dmSenderRefNumber);
3556 zfree((*envelope)->dmRecipientIdent);
3557 zfree((*envelope)->dmSenderIdent);
3558 zfree((*envelope)->dmLegalTitleLaw);
3559 zfree((*envelope)->dmLegalTitleYear);
3560 zfree((*envelope)->dmLegalTitleSect);
3561 zfree((*envelope)->dmLegalTitlePar);
3562 zfree((*envelope)->dmLegalTitlePoint);
3563 zfree((*envelope)->dmPersonalDelivery);
3564 zfree((*envelope)->dmAllowSubstDelivery);
3567 /* Extract envelope elements added by sender or ISDS
3568 * (XSD: gMessageEnvelopeSub type) */
3569 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3570 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3571 (*envelope)->dmSenderOrgUnitNum, 0);
3572 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3573 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3574 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3575 (*envelope)->dmRecipientOrgUnitNum, 0);
3576 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3577 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3578 EXTRACT_STRING("isds:dmRecipientRefNumber",
3579 (*envelope)->dmRecipientRefNumber);
3580 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3581 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3582 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3584 /* Extract envelope elements regarding law reference */
3585 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3586 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3587 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3588 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3589 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3591 /* Extract envelope other elements */
3592 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3593 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3594 (*envelope)->dmAllowSubstDelivery);
3596 leave:
3597 if (err) isds_envelope_free(envelope);
3598 xmlXPathFreeObject(result);
3599 return err;
3604 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3605 * isds_envelope structure. The envelope is automatically allocated but not
3606 * reallocated. The date are just appended into envelope structure.
3607 * @context is ISDS context
3608 * @envelope is automatically allocated message envelope structure
3609 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3610 * In case of error @envelope will be freed. */
3611 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3612 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3613 isds_error err = IE_SUCCESS;
3614 xmlXPathObjectPtr result = NULL;
3616 if (!context) return IE_INVALID_CONTEXT;
3617 if (!envelope) return IE_INVAL;
3618 if (!xpath_ctx) return IE_INVAL;
3621 if (!*envelope) {
3622 /* Allocate envelope */
3623 *envelope = calloc(1, sizeof(**envelope));
3624 if (!*envelope) {
3625 err = IE_NOMEM;
3626 goto leave;
3628 } else {
3629 /* Else free former data */
3630 zfree((*envelope)->dmID);
3631 zfree((*envelope)->dbIDSender);
3632 zfree((*envelope)->dmSender);
3633 zfree((*envelope)->dmSenderAddress);
3634 zfree((*envelope)->dmSenderType);
3635 zfree((*envelope)->dmRecipient);
3636 zfree((*envelope)->dmRecipientAddress);
3637 zfree((*envelope)->dmAmbiguousRecipient);
3640 /* Extract envelope elements added by ISDS
3641 * (XSD: gMessageEnvelope type) */
3642 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3643 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3644 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3645 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3646 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3647 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3648 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3649 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3650 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3651 (*envelope)->dmAmbiguousRecipient);
3653 /* Extract envelope elements added by sender and ISDS
3654 * (XSD: gMessageEnvelope type) */
3655 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3656 if (err) goto leave;
3658 leave:
3659 if (err) isds_envelope_free(envelope);
3660 xmlXPathFreeObject(result);
3661 return err;
3665 /* Convert other envelope elements from XML tree into isds_envelope structure:
3666 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3667 * The envelope is automatically allocated but not reallocated.
3668 * The data are just appended into envelope structure.
3669 * @context is ISDS context
3670 * @envelope is automatically allocated message envelope structure
3671 * @xpath_ctx is XPath context with current node as parent desired elements
3672 * In case of error @envelope will be freed. */
3673 static isds_error append_status_size_times(struct isds_ctx *context,
3674 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3675 isds_error err = IE_SUCCESS;
3676 xmlXPathObjectPtr result = NULL;
3677 char *string = NULL;
3678 unsigned long int *unumber = NULL;
3680 if (!context) return IE_INVALID_CONTEXT;
3681 if (!envelope) return IE_INVAL;
3682 if (!xpath_ctx) return IE_INVAL;
3685 if (!*envelope) {
3686 /* Allocate new */
3687 *envelope = calloc(1, sizeof(**envelope));
3688 if (!*envelope) {
3689 err = IE_NOMEM;
3690 goto leave;
3692 } else {
3693 /* Free old data */
3694 zfree((*envelope)->dmMessageStatus);
3695 zfree((*envelope)->dmAttachmentSize);
3696 zfree((*envelope)->dmDeliveryTime);
3697 zfree((*envelope)->dmAcceptanceTime);
3701 /* dmMessageStatus element is mandatory */
3702 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3703 if (!unumber) {
3704 isds_log_message(context,
3705 _("Missing mandatory sisds:dmMessageStatus integer"));
3706 err = IE_ISDS;
3707 goto leave;
3709 err = uint2isds_message_status(context, unumber,
3710 &((*envelope)->dmMessageStatus));
3711 if (err) {
3712 if (err == IE_ENUM) err = IE_ISDS;
3713 goto leave;
3715 free(unumber); unumber = NULL;
3717 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3720 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3721 if (string) {
3722 err = timestring2timeval((xmlChar *) string,
3723 &((*envelope)->dmDeliveryTime));
3724 if (err) {
3725 char *string_locale = _isds_utf82locale(string);
3726 if (err == IE_DATE) err = IE_ISDS;
3727 isds_printf_message(context,
3728 _("Could not convert dmDeliveryTime as ISO time: %s"),
3729 string_locale);
3730 free(string_locale);
3731 goto leave;
3733 zfree(string);
3736 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3737 if (string) {
3738 err = timestring2timeval((xmlChar *) string,
3739 &((*envelope)->dmAcceptanceTime));
3740 if (err) {
3741 char *string_locale = _isds_utf82locale(string);
3742 if (err == IE_DATE) err = IE_ISDS;
3743 isds_printf_message(context,
3744 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3745 string_locale);
3746 free(string_locale);
3747 goto leave;
3749 zfree(string);
3752 leave:
3753 if (err) isds_envelope_free(envelope);
3754 free(unumber);
3755 free(string);
3756 xmlXPathFreeObject(result);
3757 return err;
3761 /* Convert message type attribute of current element into isds_envelope
3762 * structure.
3763 * TODO: This function can be incorporated into append_status_size_times() as
3764 * they are called always together.
3765 * The envelope is automatically allocated but not reallocated.
3766 * The data are just appended into envelope structure.
3767 * @context is ISDS context
3768 * @envelope is automatically allocated message envelope structure
3769 * @xpath_ctx is XPath context with current node as parent of attribute
3770 * carrying message type
3771 * In case of error @envelope will be freed. */
3772 static isds_error append_message_type(struct isds_ctx *context,
3773 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3774 isds_error err = IE_SUCCESS;
3776 if (!context) return IE_INVALID_CONTEXT;
3777 if (!envelope) return IE_INVAL;
3778 if (!xpath_ctx) return IE_INVAL;
3781 if (!*envelope) {
3782 /* Allocate new */
3783 *envelope = calloc(1, sizeof(**envelope));
3784 if (!*envelope) {
3785 err = IE_NOMEM;
3786 goto leave;
3788 } else {
3789 /* Free old data */
3790 zfree((*envelope)->dmType);
3794 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3796 if (!(*envelope)->dmType) {
3797 /* Use default value */
3798 (*envelope)->dmType = strdup("V");
3799 if (!(*envelope)->dmType) {
3800 err = IE_NOMEM;
3801 goto leave;
3803 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3804 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3805 isds_printf_message(context,
3806 _("Message type in dmType attribute is not 1 character long: "
3807 "%s"),
3808 type_locale);
3809 free(type_locale);
3810 err = IE_ISDS;
3811 goto leave;
3814 leave:
3815 if (err) isds_envelope_free(envelope);
3816 return err;
3820 #if HAVE_LIBCURL
3821 /* Convert dmType isds_envelope member into XML attribute and append it to
3822 * current node.
3823 * @context is ISDS context
3824 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3825 * @dm_envelope is XML element the resulting attribute will be appended to.
3826 * @return error code, in case of error context' message is filled. */
3827 static isds_error insert_message_type(struct isds_ctx *context,
3828 const char *type, xmlNodePtr dm_envelope) {
3829 isds_error err = IE_SUCCESS;
3830 xmlAttrPtr attribute_node;
3832 if (!context) return IE_INVALID_CONTEXT;
3833 if (!dm_envelope) return IE_INVAL;
3835 /* Insert optional message type */
3836 if (type) {
3837 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3838 char *type_locale = _isds_utf82locale(type);
3839 isds_printf_message(context,
3840 _("Message type in envelope is not 1 character long: %s"),
3841 type_locale);
3842 free(type_locale);
3843 err = IE_INVAL;
3844 goto leave;
3846 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3849 leave:
3850 return err;
3852 #endif /* HAVE_LIBCURL */
3855 /* Extract message document into reallocated document structure
3856 * @context is ISDS context
3857 * @document is automatically reallocated message documents structure
3858 * @xpath_ctx is XPath context with current node as isds:dmFile
3859 * In case of error @document will be freed. */
3860 static isds_error extract_document(struct isds_ctx *context,
3861 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3862 isds_error err = IE_SUCCESS;
3863 xmlXPathObjectPtr result = NULL;
3864 xmlNodePtr file_node;
3865 char *string = NULL;
3867 if (!context) return IE_INVALID_CONTEXT;
3868 if (!document) return IE_INVAL;
3869 isds_document_free(document);
3870 if (!xpath_ctx) return IE_INVAL;
3871 file_node = xpath_ctx->node;
3873 *document = calloc(1, sizeof(**document));
3874 if (!*document) {
3875 err = IE_NOMEM;
3876 goto leave;
3879 /* Extract document meta data */
3880 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3881 if (context->normalize_mime_type) {
3882 const char *normalized_type =
3883 isds_normalize_mime_type((*document)->dmMimeType);
3884 if (NULL != normalized_type &&
3885 normalized_type != (*document)->dmMimeType) {
3886 char *new_type = strdup(normalized_type);
3887 if (NULL == new_type) {
3888 isds_printf_message(context,
3889 _("Not enough memory to normalize document MIME type"));
3890 err = IE_NOMEM;
3891 goto leave;
3893 free((*document)->dmMimeType);
3894 (*document)->dmMimeType = new_type;
3898 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3899 err = string2isds_FileMetaType((xmlChar*)string,
3900 &((*document)->dmFileMetaType));
3901 if (err) {
3902 char *meta_type_locale = _isds_utf82locale(string);
3903 isds_printf_message(context,
3904 _("Document has invalid dmFileMetaType attribute value: %s"),
3905 meta_type_locale);
3906 free(meta_type_locale);
3907 err = IE_ISDS;
3908 goto leave;
3910 zfree(string);
3912 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3913 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3914 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3915 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3918 /* Extract document data.
3919 * Base64 encoded blob or XML subtree must be presented. */
3921 /* Check for dmEncodedContent */
3922 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3923 xpath_ctx);
3924 if (!result) {
3925 err = IE_XML;
3926 goto leave;
3929 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3930 /* Here we have Base64 blob */
3931 (*document)->is_xml = 0;
3933 if (result->nodesetval->nodeNr > 1) {
3934 isds_printf_message(context,
3935 _("Document has more dmEncodedContent elements"));
3936 err = IE_ISDS;
3937 goto leave;
3940 xmlXPathFreeObject(result); result = NULL;
3941 EXTRACT_STRING("isds:dmEncodedContent", string);
3943 /* Decode non-empty document */
3944 if (string && string[0] != '\0') {
3945 (*document)->data_length =
3946 _isds_b64decode(string, &((*document)->data));
3947 if ((*document)->data_length == (size_t) -1) {
3948 isds_printf_message(context,
3949 _("Error while Base64-decoding document content"));
3950 err = IE_ERROR;
3951 goto leave;
3954 } else {
3955 /* No Base64 blob, try XML document */
3956 xmlXPathFreeObject(result); result = NULL;
3957 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3958 xpath_ctx);
3959 if (!result) {
3960 err = IE_XML;
3961 goto leave;
3964 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3965 /* Here we have XML document */
3966 (*document)->is_xml = 1;
3968 if (result->nodesetval->nodeNr > 1) {
3969 isds_printf_message(context,
3970 _("Document has more dmXMLContent elements"));
3971 err = IE_ISDS;
3972 goto leave;
3975 /* XXX: We cannot serialize the content simply because:
3976 * - XML document may point out of its scope (e.g. to message
3977 * envelope)
3978 * - isds:dmXMLContent can contain more elements, no element,
3979 * a text node only
3980 * - it's not the XML way
3981 * Thus we provide the only right solution: XML DOM. Let's
3982 * application to cope with this hot potato :) */
3983 (*document)->xml_node_list =
3984 result->nodesetval->nodeTab[0]->children;
3985 } else {
3986 /* No base64 blob, nor XML document */
3987 isds_printf_message(context,
3988 _("Document has no dmEncodedContent, nor dmXMLContent "
3989 "element"));
3990 err = IE_ISDS;
3991 goto leave;
3996 leave:
3997 if (err) isds_document_free(document);
3998 free(string);
3999 xmlXPathFreeObject(result);
4000 xpath_ctx->node = file_node;
4001 return err;
4006 /* Extract message documents into reallocated list of documents
4007 * @context is ISDS context
4008 * @documents is automatically reallocated message documents list structure
4009 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4010 * In case of error @documents will be freed. */
4011 static isds_error extract_documents(struct isds_ctx *context,
4012 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
4013 isds_error err = IE_SUCCESS;
4014 xmlXPathObjectPtr result = NULL;
4015 xmlNodePtr files_node;
4016 struct isds_list *document, *prev_document = NULL;
4018 if (!context) return IE_INVALID_CONTEXT;
4019 if (!documents) return IE_INVAL;
4020 isds_list_free(documents);
4021 if (!xpath_ctx) return IE_INVAL;
4022 files_node = xpath_ctx->node;
4024 /* Find documents */
4025 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
4026 if (!result) {
4027 err = IE_XML;
4028 goto leave;
4031 /* No match */
4032 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4033 isds_printf_message(context,
4034 _("Message does not contain any document"));
4035 err = IE_ISDS;
4036 goto leave;
4040 /* Iterate over documents */
4041 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4043 /* Allocate and append list item */
4044 document = calloc(1, sizeof(*document));
4045 if (!document) {
4046 err = IE_NOMEM;
4047 goto leave;
4049 document->destructor = (void (*)(void **))isds_document_free;
4050 if (i == 0) *documents = document;
4051 else prev_document->next = document;
4052 prev_document = document;
4054 /* Extract document */
4055 xpath_ctx->node = result->nodesetval->nodeTab[i];
4056 err = extract_document(context,
4057 (struct isds_document **) &(document->data), xpath_ctx);
4058 if (err) goto leave;
4062 leave:
4063 if (err) isds_list_free(documents);
4064 xmlXPathFreeObject(result);
4065 xpath_ctx->node = files_node;
4066 return err;
4070 #if HAVE_LIBCURL
4071 /* Convert isds:dmRecord XML tree into structure
4072 * @context is ISDS context
4073 * @envelope is automatically reallocated message envelope structure
4074 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4075 * In case of error @envelope will be freed. */
4076 static isds_error extract_DmRecord(struct isds_ctx *context,
4077 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4078 isds_error err = IE_SUCCESS;
4079 xmlXPathObjectPtr result = NULL;
4081 if (!context) return IE_INVALID_CONTEXT;
4082 if (!envelope) return IE_INVAL;
4083 isds_envelope_free(envelope);
4084 if (!xpath_ctx) return IE_INVAL;
4087 *envelope = calloc(1, sizeof(**envelope));
4088 if (!*envelope) {
4089 err = IE_NOMEM;
4090 goto leave;
4094 /* Extract tRecord data */
4095 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4097 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4098 * dmAcceptanceTime. */
4099 err = append_status_size_times(context, envelope, xpath_ctx);
4100 if (err) goto leave;
4102 /* Extract envelope elements added by sender and ISDS
4103 * (XSD: gMessageEnvelope type) */
4104 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4105 if (err) goto leave;
4107 /* Get message type */
4108 err = append_message_type(context, envelope, xpath_ctx);
4109 if (err) goto leave;
4112 leave:
4113 if (err) isds_envelope_free(envelope);
4114 xmlXPathFreeObject(result);
4115 return err;
4119 /* Convert XSD:tStateChangesRecord type XML tree into structure
4120 * @context is ISDS context
4121 * @changed_status is automatically reallocated message state change structure
4122 * @xpath_ctx is XPath context with current node as element of
4123 * XSD:tStateChangesRecord type
4124 * In case of error @changed_status will be freed. */
4125 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4126 struct isds_message_status_change **changed_status,
4127 xmlXPathContextPtr xpath_ctx) {
4128 isds_error err = IE_SUCCESS;
4129 xmlXPathObjectPtr result = NULL;
4130 unsigned long int *unumber = NULL;
4131 char *string = NULL;
4133 if (!context) return IE_INVALID_CONTEXT;
4134 if (!changed_status) return IE_INVAL;
4135 isds_message_status_change_free(changed_status);
4136 if (!xpath_ctx) return IE_INVAL;
4139 *changed_status = calloc(1, sizeof(**changed_status));
4140 if (!*changed_status) {
4141 err = IE_NOMEM;
4142 goto leave;
4146 /* Extract tGetStateChangesInput data */
4147 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4149 /* dmEventTime is mandatory */
4150 EXTRACT_STRING("isds:dmEventTime", string);
4151 if (string) {
4152 err = timestring2timeval((xmlChar *) string,
4153 &((*changed_status)->time));
4154 if (err) {
4155 char *string_locale = _isds_utf82locale(string);
4156 if (err == IE_DATE) err = IE_ISDS;
4157 isds_printf_message(context,
4158 _("Could not convert dmEventTime as ISO time: %s"),
4159 string_locale);
4160 free(string_locale);
4161 goto leave;
4163 zfree(string);
4166 /* dmMessageStatus element is mandatory */
4167 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4168 if (!unumber) {
4169 isds_log_message(context,
4170 _("Missing mandatory isds:dmMessageStatus integer"));
4171 err = IE_ISDS;
4172 goto leave;
4174 err = uint2isds_message_status(context, unumber,
4175 &((*changed_status)->dmMessageStatus));
4176 if (err) {
4177 if (err == IE_ENUM) err = IE_ISDS;
4178 goto leave;
4180 zfree(unumber);
4183 leave:
4184 free(unumber);
4185 free(string);
4186 if (err) isds_message_status_change_free(changed_status);
4187 xmlXPathFreeObject(result);
4188 return err;
4190 #endif /* HAVE_LIBCURL */
4193 /* Find and convert isds:dmHash XML tree into structure
4194 * @context is ISDS context
4195 * @envelope is automatically reallocated message hash structure
4196 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4197 * In case of error @hash will be freed. */
4198 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4199 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4200 isds_error err = IE_SUCCESS;
4201 xmlNodePtr old_ctx_node;
4202 xmlXPathObjectPtr result = NULL;
4203 char *string = NULL;
4205 if (!context) return IE_INVALID_CONTEXT;
4206 if (!hash) return IE_INVAL;
4207 isds_hash_free(hash);
4208 if (!xpath_ctx) return IE_INVAL;
4210 old_ctx_node = xpath_ctx->node;
4212 *hash = calloc(1, sizeof(**hash));
4213 if (!*hash) {
4214 err = IE_NOMEM;
4215 goto leave;
4218 /* Locate dmHash */
4219 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4220 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4221 err = IE_ISDS;
4222 goto leave;
4224 if (err) {
4225 err = IE_ERROR;
4226 goto leave;
4229 /* Get hash algorithm */
4230 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4231 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4232 if (err) {
4233 if (err == IE_ENUM) {
4234 char *string_locale = _isds_utf82locale(string);
4235 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4236 string_locale);
4237 free(string_locale);
4239 goto leave;
4241 zfree(string);
4243 /* Get hash value */
4244 EXTRACT_STRING(".", string);
4245 if (!string) {
4246 isds_printf_message(context,
4247 _("sisds:dmHash element is missing hash value"));
4248 err = IE_ISDS;
4249 goto leave;
4251 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4252 if ((*hash)->length == (size_t) -1) {
4253 isds_printf_message(context,
4254 _("Error while Base64-decoding hash value"));
4255 err = IE_ERROR;
4256 goto leave;
4259 leave:
4260 if (err) isds_hash_free(hash);
4261 free(string);
4262 xmlXPathFreeObject(result);
4263 xpath_ctx->node = old_ctx_node;
4264 return err;
4268 /* Find and append isds:dmQTimestamp XML tree into envelope.
4269 * Because one service is allowed to miss time-stamp content, and we think
4270 * other could too (flaw in specification), this function is deliberated and
4271 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4272 * @context is ISDS context
4273 * @envelope is automatically allocated envelope structure
4274 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4275 * child
4276 * In case of error @envelope will be freed. */
4277 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4278 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4279 isds_error err = IE_SUCCESS;
4280 xmlXPathObjectPtr result = NULL;
4281 char *string = NULL;
4283 if (!context) return IE_INVALID_CONTEXT;
4284 if (!envelope) return IE_INVAL;
4285 if (!xpath_ctx) {
4286 isds_envelope_free(envelope);
4287 return IE_INVAL;
4290 if (!*envelope) {
4291 *envelope = calloc(1, sizeof(**envelope));
4292 if (!*envelope) {
4293 err = IE_NOMEM;
4294 goto leave;
4296 } else {
4297 zfree((*envelope)->timestamp);
4298 (*envelope)->timestamp_length = 0;
4301 /* Get dmQTimestamp */
4302 EXTRACT_STRING("sisds:dmQTimestamp", string);
4303 if (!string) {
4304 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4305 goto leave;
4307 (*envelope)->timestamp_length =
4308 _isds_b64decode(string, &((*envelope)->timestamp));
4309 if ((*envelope)->timestamp_length == (size_t) -1) {
4310 isds_printf_message(context,
4311 _("Error while Base64-decoding time stamp value"));
4312 err = IE_ERROR;
4313 goto leave;
4316 leave:
4317 if (err) isds_envelope_free(envelope);
4318 free(string);
4319 xmlXPathFreeObject(result);
4320 return err;
4324 /* Convert XSD tReturnedMessage XML tree into message structure.
4325 * It does not store serialized XML tree into message->raw.
4326 * It does store (pointer to) parsed XML tree into message->xml if needed.
4327 * @context is ISDS context
4328 * @include_documents Use true if documents must be extracted
4329 * (tReturnedMessage XSD type), use false if documents shall be omitted
4330 * (tReturnedMessageEnvelope).
4331 * @message is automatically reallocated message structure
4332 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4333 * type
4334 * In case of error @message will be freed. */
4335 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4336 const _Bool include_documents, struct isds_message **message,
4337 xmlXPathContextPtr xpath_ctx) {
4338 isds_error err = IE_SUCCESS;
4339 xmlNodePtr message_node;
4341 if (!context) return IE_INVALID_CONTEXT;
4342 if (!message) return IE_INVAL;
4343 isds_message_free(message);
4344 if (!xpath_ctx) return IE_INVAL;
4347 *message = calloc(1, sizeof(**message));
4348 if (!*message) {
4349 err = IE_NOMEM;
4350 goto leave;
4353 /* Save message XPATH context node */
4354 message_node = xpath_ctx->node;
4357 /* Extract dmDM */
4358 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4359 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4360 if (err) { err = IE_ERROR; goto leave; }
4361 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4362 if (err) goto leave;
4364 if (include_documents) {
4365 struct isds_list *item;
4367 /* Extract dmFiles */
4368 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4369 xpath_ctx);
4370 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4371 err = IE_ISDS; goto leave;
4373 if (err) { err = IE_ERROR; goto leave; }
4374 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4375 if (err) goto leave;
4377 /* Store xmlDoc of this message if needed */
4378 /* Only if we got a XML document in all the documents. */
4379 for (item = (*message)->documents; item; item = item->next) {
4380 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4381 (*message)->xml = xpath_ctx->doc;
4382 break;
4388 /* Restore context to message */
4389 xpath_ctx->node = message_node;
4391 /* Extract dmHash */
4392 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4393 xpath_ctx);
4394 if (err) goto leave;
4396 /* Extract dmQTimestamp, */
4397 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4398 xpath_ctx);
4399 if (err) goto leave;
4401 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4402 * dmAcceptanceTime. */
4403 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4404 if (err) goto leave;
4406 /* Get message type */
4407 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4408 if (err) goto leave;
4410 leave:
4411 if (err) isds_message_free(message);
4412 return err;
4416 /* Extract message event into reallocated isds_event structure
4417 * @context is ISDS context
4418 * @event is automatically reallocated message event structure
4419 * @xpath_ctx is XPath context with current node as isds:dmEvent
4420 * In case of error @event will be freed. */
4421 static isds_error extract_event(struct isds_ctx *context,
4422 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4423 isds_error err = IE_SUCCESS;
4424 xmlXPathObjectPtr result = NULL;
4425 xmlNodePtr event_node;
4426 char *string = NULL;
4428 if (!context) return IE_INVALID_CONTEXT;
4429 if (!event) return IE_INVAL;
4430 isds_event_free(event);
4431 if (!xpath_ctx) return IE_INVAL;
4432 event_node = xpath_ctx->node;
4434 *event = calloc(1, sizeof(**event));
4435 if (!*event) {
4436 err = IE_NOMEM;
4437 goto leave;
4440 /* Extract event data.
4441 * All elements are optional according XSD. That's funny. */
4442 EXTRACT_STRING("sisds:dmEventTime", string);
4443 if (string) {
4444 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4445 if (err) {
4446 char *string_locale = _isds_utf82locale(string);
4447 if (err == IE_DATE) err = IE_ISDS;
4448 isds_printf_message(context,
4449 _("Could not convert dmEventTime as ISO time: %s"),
4450 string_locale);
4451 free(string_locale);
4452 goto leave;
4454 zfree(string);
4457 /* dmEventDescr element has prefix and the rest */
4458 EXTRACT_STRING("sisds:dmEventDescr", string);
4459 if (string) {
4460 err = eventstring2event((xmlChar *) string, *event);
4461 if (err) goto leave;
4462 zfree(string);
4465 leave:
4466 if (err) isds_event_free(event);
4467 free(string);
4468 xmlXPathFreeObject(result);
4469 xpath_ctx->node = event_node;
4470 return err;
4474 /* Convert element of XSD tEventsArray type from XML tree into
4475 * isds_list of isds_event's structure. The list is automatically reallocated.
4476 * @context is ISDS context
4477 * @events is automatically reallocated list of event structures
4478 * @xpath_ctx is XPath context with current node as tEventsArray
4479 * In case of error @events will be freed. */
4480 static isds_error extract_events(struct isds_ctx *context,
4481 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4482 isds_error err = IE_SUCCESS;
4483 xmlXPathObjectPtr result = NULL;
4484 xmlNodePtr events_node;
4485 struct isds_list *event, *prev_event = NULL;
4487 if (!context) return IE_INVALID_CONTEXT;
4488 if (!events) return IE_INVAL;
4489 if (!xpath_ctx) return IE_INVAL;
4490 events_node = xpath_ctx->node;
4492 /* Free old list */
4493 isds_list_free(events);
4495 /* Find events */
4496 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4497 if (!result) {
4498 err = IE_XML;
4499 goto leave;
4502 /* No match */
4503 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4504 isds_printf_message(context,
4505 _("Delivery info does not contain any event"));
4506 err = IE_ISDS;
4507 goto leave;
4511 /* Iterate over events */
4512 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4514 /* Allocate and append list item */
4515 event = calloc(1, sizeof(*event));
4516 if (!event) {
4517 err = IE_NOMEM;
4518 goto leave;
4520 event->destructor = (void (*)(void **))isds_event_free;
4521 if (i == 0) *events = event;
4522 else prev_event->next = event;
4523 prev_event = event;
4525 /* Extract event */
4526 xpath_ctx->node = result->nodesetval->nodeTab[i];
4527 err = extract_event(context,
4528 (struct isds_event **) &(event->data), xpath_ctx);
4529 if (err) goto leave;
4533 leave:
4534 if (err) isds_list_free(events);
4535 xmlXPathFreeObject(result);
4536 xpath_ctx->node = events_node;
4537 return err;
4541 #if HAVE_LIBCURL
4542 /* Insert Base64 encoded data as element with text child.
4543 * @context is session context
4544 * @parent is XML node to append @element with @data as child
4545 * @ns is XML namespace of @element, use NULL to inherit from @parent
4546 * @element is UTF-8 encoded name of new element
4547 * @data is bit stream to encode into @element
4548 * @length is size of @data in bytes
4549 * @return standard error code and fill long error message if needed */
4550 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4551 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4552 const void *data, size_t length) {
4553 isds_error err = IE_SUCCESS;
4554 xmlNodePtr node;
4556 if (!context) return IE_INVALID_CONTEXT;
4557 if (!data && length > 0) return IE_INVAL;
4558 if (!parent || !element) return IE_INVAL;
4560 xmlChar *base64data = NULL;
4561 base64data = (xmlChar *) _isds_b64encode(data, length);
4562 if (!base64data) {
4563 isds_printf_message(context,
4564 ngettext("Not enough memory to encode %zd byte into Base64",
4565 "Not enough memory to encode %zd bytes into Base64",
4566 length),
4567 length);
4568 err = IE_NOMEM;
4569 goto leave;
4571 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4573 leave:
4574 free(base64data);
4575 return err;
4579 /* Convert isds_document structure into XML tree and append to dmFiles node.
4580 * @context is session context
4581 * @document is ISDS document
4582 * @dm_files is XML element the resulting tree will be appended to as a child.
4583 * @return error code, in case of error context' message is filled. */
4584 static isds_error insert_document(struct isds_ctx *context,
4585 struct isds_document *document, xmlNodePtr dm_files) {
4586 isds_error err = IE_SUCCESS;
4587 xmlNodePtr new_file = NULL, file = NULL, node;
4588 xmlAttrPtr attribute_node;
4590 if (!context) return IE_INVALID_CONTEXT;
4591 if (!document || !dm_files) return IE_INVAL;
4593 /* Allocate new dmFile */
4594 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4595 if (!new_file) {
4596 isds_printf_message(context, _("Could not allocate main dmFile"));
4597 err = IE_ERROR;
4598 goto leave;
4600 /* Append the new dmFile.
4601 * XXX: Main document must go first */
4602 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4603 file = xmlAddPrevSibling(dm_files->children, new_file);
4604 else
4605 file = xmlAddChild(dm_files, new_file);
4607 if (!file) {
4608 xmlFreeNode(new_file); new_file = NULL;
4609 isds_printf_message(context, _("Could not add dmFile child to "
4610 "%s element"), dm_files->name);
4611 err = IE_ERROR;
4612 goto leave;
4615 /* @dmMimeType is required */
4616 if (!document->dmMimeType) {
4617 isds_log_message(context,
4618 _("Document is missing mandatory MIME type definition"));
4619 err = IE_INVAL;
4620 goto leave;
4622 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4624 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4625 if (!string) {
4626 isds_printf_message(context,
4627 _("Document has unknown dmFileMetaType: %ld"),
4628 document->dmFileMetaType);
4629 err = IE_ENUM;
4630 goto leave;
4632 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4634 if (document->dmFileGuid) {
4635 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4637 if (document->dmUpFileGuid) {
4638 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4641 /* @dmFileDescr is required */
4642 if (!document->dmFileDescr) {
4643 isds_log_message(context,
4644 _("Document is missing mandatory description (title)"));
4645 err = IE_INVAL;
4646 goto leave;
4648 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4650 if (document->dmFormat) {
4651 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4655 /* Insert content (body) of the document. */
4656 if (document->is_xml) {
4657 /* XML document requested */
4659 /* Allocate new dmXMLContent */
4660 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4661 if (!xmlcontent) {
4662 isds_printf_message(context,
4663 _("Could not allocate dmXMLContent element"));
4664 err = IE_ERROR;
4665 goto leave;
4667 /* Append it */
4668 node = xmlAddChild(file, xmlcontent);
4669 if (!node) {
4670 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4671 isds_printf_message(context,
4672 _("Could not add dmXMLContent child to %s element"),
4673 file->name);
4674 err = IE_ERROR;
4675 goto leave;
4678 /* Copy non-empty node list */
4679 if (document->xml_node_list) {
4680 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4681 document->xml_node_list);
4682 if (!content) {
4683 isds_printf_message(context,
4684 _("Not enough memory to copy XML document"));
4685 err = IE_NOMEM;
4686 goto leave;
4689 if (!xmlAddChildList(node, content)) {
4690 xmlFreeNodeList(content);
4691 isds_printf_message(context,
4692 _("Error while adding XML document into dmXMLContent"));
4693 err = IE_XML;
4694 goto leave;
4696 /* XXX: We cannot free the content here because it's part of node's
4697 * document since now. It will be freed with it automatically. */
4699 } else {
4700 /* Binary document requested */
4701 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4702 document->data, document->data_length);
4703 if (err) goto leave;
4706 leave:
4707 return err;
4711 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4712 * The copy must be preallocated, the date are just appended into structure.
4713 * @context is ISDS context
4714 * @copy is message copy structure
4715 * @xpath_ctx is XPath context with current node as tMStatus */
4716 static isds_error append_TMStatus(struct isds_ctx *context,
4717 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4718 isds_error err = IE_SUCCESS;
4719 xmlXPathObjectPtr result = NULL;
4720 char *code = NULL, *message = NULL;
4722 if (!context) return IE_INVALID_CONTEXT;
4723 if (!copy || !xpath_ctx) return IE_INVAL;
4725 /* Free old values */
4726 zfree(copy->dmStatus);
4727 zfree(copy->dmID);
4729 /* Get error specific to this copy */
4730 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4731 if (!code) {
4732 isds_log_message(context,
4733 _("Missing isds:dmStatusCode under "
4734 "XSD:tMStatus type element"));
4735 err = IE_ISDS;
4736 goto leave;
4739 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4740 /* This copy failed */
4741 copy->error = IE_ISDS;
4742 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4743 if (message) {
4744 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4745 if (!copy->dmStatus) {
4746 copy->dmStatus = code;
4747 code = NULL;
4749 } else {
4750 copy->dmStatus = code;
4751 code = NULL;
4753 } else {
4754 /* This copy succeeded. In this case only, message ID is valid */
4755 copy->error = IE_SUCCESS;
4757 EXTRACT_STRING("isds:dmID", copy->dmID);
4758 if (!copy->dmID) {
4759 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4760 "but did not returned assigned message ID\n"));
4761 err = IE_ISDS;
4765 leave:
4766 free(code);
4767 free(message);
4768 xmlXPathFreeObject(result);
4769 return err;
4773 /* Insert struct isds_approval data (box approval) into XML tree
4774 * @context is session context
4775 * @approval is libisds structure with approval description. NULL is
4776 * acceptable.
4777 * @parent is XML element to append @approval to */
4778 static isds_error insert_GExtApproval(struct isds_ctx *context,
4779 const struct isds_approval *approval, xmlNodePtr parent) {
4781 isds_error err = IE_SUCCESS;
4782 xmlNodePtr node;
4784 if (!context) return IE_INVALID_CONTEXT;
4785 if (!parent) return IE_INVAL;
4787 if (!approval) return IE_SUCCESS;
4789 /* Build XSD:gExtApproval */
4790 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4791 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4793 leave:
4794 return err;
4798 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4799 * code
4800 * @context is session context
4801 * @service_name is name of SERVICE_DB_ACCESS
4802 * @response is reallocated server SOAP body response as XML document
4803 * @raw_response is reallocated bit stream with response body. Use
4804 * NULL if you don't care
4805 * @raw_response_length is size of @raw_response in bytes
4806 * @code is reallocated ISDS status code
4807 * @status_message is reallocated ISDS status message
4808 * @return error coded from lower layer, context message will be set up
4809 * appropriately. */
4810 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4811 const xmlChar *service_name,
4812 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4813 xmlChar **code, xmlChar **status_message) {
4815 isds_error err = IE_SUCCESS;
4816 char *service_name_locale = NULL;
4817 xmlNodePtr request = NULL, node;
4818 xmlNsPtr isds_ns = NULL;
4820 if (!context) return IE_INVALID_CONTEXT;
4821 if (!service_name) return IE_INVAL;
4822 if (!response || !code || !status_message) return IE_INVAL;
4823 if (!raw_response_length && raw_response) return IE_INVAL;
4825 /* Free output argument */
4826 xmlFreeDoc(*response); *response = NULL;
4827 if (raw_response) zfree(*raw_response);
4828 zfree(*code);
4829 zfree(*status_message);
4832 /* Check if connection is established
4833 * TODO: This check should be done downstairs. */
4834 if (!context->curl) return IE_CONNECTION_CLOSED;
4836 service_name_locale = _isds_utf82locale((char*)service_name);
4837 if (!service_name_locale) {
4838 err = IE_NOMEM;
4839 goto leave;
4842 /* Build request */
4843 request = xmlNewNode(NULL, service_name);
4844 if (!request) {
4845 isds_printf_message(context,
4846 _("Could not build %s request"), service_name_locale);
4847 err = IE_ERROR;
4848 goto leave;
4850 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4851 if(!isds_ns) {
4852 isds_log_message(context, _("Could not create ISDS name space"));
4853 err = IE_ERROR;
4854 goto leave;
4856 xmlSetNs(request, isds_ns);
4859 /* Add XSD:tDummyInput child */
4860 INSERT_STRING(request, "dbDummy", NULL);
4863 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4864 service_name_locale);
4866 /* Send request */
4867 err = _isds(context, SERVICE_DB_ACCESS, request, response,
4868 raw_response, raw_response_length);
4869 xmlFreeNode(request); request = NULL;
4871 if (err) {
4872 isds_log(ILF_ISDS, ILL_DEBUG,
4873 _("Processing ISDS response on %s request failed\n"),
4874 service_name_locale);
4875 goto leave;
4878 /* Check for response status */
4879 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4880 code, status_message, NULL);
4881 if (err) {
4882 isds_log(ILF_ISDS, ILL_DEBUG,
4883 _("ISDS response on %s request is missing status\n"),
4884 service_name_locale);
4885 goto leave;
4888 /* Request processed, but nothing found */
4889 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4890 char *code_locale = _isds_utf82locale((char*) *code);
4891 char *status_message_locale =
4892 _isds_utf82locale((char*) *status_message);
4893 isds_log(ILF_ISDS, ILL_DEBUG,
4894 _("Server refused %s request (code=%s, message=%s)\n"),
4895 service_name_locale, code_locale, status_message_locale);
4896 isds_log_message(context, status_message_locale);
4897 free(code_locale);
4898 free(status_message_locale);
4899 err = IE_ISDS;
4900 goto leave;
4903 leave:
4904 free(service_name_locale);
4905 xmlFreeNode(request);
4906 return err;
4908 #endif
4911 /* Get data about logged in user and his box. */
4912 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4913 struct isds_DbOwnerInfo **db_owner_info) {
4914 isds_error err = IE_SUCCESS;
4915 #if HAVE_LIBCURL
4916 xmlDocPtr response = NULL;
4917 xmlChar *code = NULL, *message = NULL;
4918 xmlXPathContextPtr xpath_ctx = NULL;
4919 xmlXPathObjectPtr result = NULL;
4920 char *string = NULL;
4921 #endif
4923 if (!context) return IE_INVALID_CONTEXT;
4924 zfree(context->long_message);
4925 if (!db_owner_info) return IE_INVAL;
4926 isds_DbOwnerInfo_free(db_owner_info);
4928 #if HAVE_LIBCURL
4929 /* Check if connection is established */
4930 if (!context->curl) return IE_CONNECTION_CLOSED;
4933 /* Do request and check for success */
4934 err = build_send_check_dbdummy_request(context,
4935 BAD_CAST "GetOwnerInfoFromLogin",
4936 &response, NULL, NULL, &code, &message);
4937 if (err) goto leave;
4940 /* Extract data */
4941 /* Prepare structure */
4942 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4943 if (!*db_owner_info) {
4944 err = IE_NOMEM;
4945 goto leave;
4947 xpath_ctx = xmlXPathNewContext(response);
4948 if (!xpath_ctx) {
4949 err = IE_ERROR;
4950 goto leave;
4952 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4953 err = IE_ERROR;
4954 goto leave;
4957 /* Set context node */
4958 result = xmlXPathEvalExpression(BAD_CAST
4959 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4960 if (!result) {
4961 err = IE_ERROR;
4962 goto leave;
4964 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4965 isds_log_message(context, _("Missing dbOwnerInfo element"));
4966 err = IE_ISDS;
4967 goto leave;
4969 if (result->nodesetval->nodeNr > 1) {
4970 isds_log_message(context, _("Multiple dbOwnerInfo element"));
4971 err = IE_ISDS;
4972 goto leave;
4974 xpath_ctx->node = result->nodesetval->nodeTab[0];
4975 xmlXPathFreeObject(result); result = NULL;
4977 /* Extract it */
4978 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
4981 leave:
4982 if (err) {
4983 isds_DbOwnerInfo_free(db_owner_info);
4986 free(string);
4987 xmlXPathFreeObject(result);
4988 xmlXPathFreeContext(xpath_ctx);
4990 free(code);
4991 free(message);
4992 xmlFreeDoc(response);
4994 if (!err)
4995 isds_log(ILF_ISDS, ILL_DEBUG,
4996 _("GetOwnerInfoFromLogin request processed by server "
4997 "successfully.\n"));
4998 #else /* not HAVE_LIBCURL */
4999 err = IE_NOTSUP;
5000 #endif
5002 return err;
5006 /* Get data about logged in user. */
5007 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
5008 struct isds_DbUserInfo **db_user_info) {
5009 isds_error err = IE_SUCCESS;
5010 #if HAVE_LIBCURL
5011 xmlDocPtr response = NULL;
5012 xmlChar *code = NULL, *message = NULL;
5013 xmlXPathContextPtr xpath_ctx = NULL;
5014 xmlXPathObjectPtr result = NULL;
5015 #endif
5017 if (!context) return IE_INVALID_CONTEXT;
5018 zfree(context->long_message);
5019 if (!db_user_info) return IE_INVAL;
5020 isds_DbUserInfo_free(db_user_info);
5022 #if HAVE_LIBCURL
5023 /* Check if connection is established */
5024 if (!context->curl) return IE_CONNECTION_CLOSED;
5027 /* Do request and check for success */
5028 err = build_send_check_dbdummy_request(context,
5029 BAD_CAST "GetUserInfoFromLogin",
5030 &response, NULL, NULL, &code, &message);
5031 if (err) goto leave;
5034 /* Extract data */
5035 /* Prepare structure */
5036 *db_user_info = calloc(1, sizeof(**db_user_info));
5037 if (!*db_user_info) {
5038 err = IE_NOMEM;
5039 goto leave;
5041 xpath_ctx = xmlXPathNewContext(response);
5042 if (!xpath_ctx) {
5043 err = IE_ERROR;
5044 goto leave;
5046 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5047 err = IE_ERROR;
5048 goto leave;
5051 /* Set context node */
5052 result = xmlXPathEvalExpression(BAD_CAST
5053 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
5054 if (!result) {
5055 err = IE_ERROR;
5056 goto leave;
5058 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5059 isds_log_message(context, _("Missing dbUserInfo element"));
5060 err = IE_ISDS;
5061 goto leave;
5063 if (result->nodesetval->nodeNr > 1) {
5064 isds_log_message(context, _("Multiple dbUserInfo element"));
5065 err = IE_ISDS;
5066 goto leave;
5068 xpath_ctx->node = result->nodesetval->nodeTab[0];
5069 xmlXPathFreeObject(result); result = NULL;
5071 /* Extract it */
5072 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5074 leave:
5075 if (err) {
5076 isds_DbUserInfo_free(db_user_info);
5079 xmlXPathFreeObject(result);
5080 xmlXPathFreeContext(xpath_ctx);
5082 free(code);
5083 free(message);
5084 xmlFreeDoc(response);
5086 if (!err)
5087 isds_log(ILF_ISDS, ILL_DEBUG,
5088 _("GetUserInfoFromLogin request processed by server "
5089 "successfully.\n"));
5090 #else /* not HAVE_LIBCURL */
5091 err = IE_NOTSUP;
5092 #endif
5094 return err;
5098 /* Get expiration time of current password
5099 * @context is session context
5100 * @expiration is automatically reallocated time when password expires. If
5101 * password expiration is disabled, NULL will be returned. In case of error
5102 * it will be nulled too. */
5103 isds_error isds_get_password_expiration(struct isds_ctx *context,
5104 struct timeval **expiration) {
5105 isds_error err = IE_SUCCESS;
5106 #if HAVE_LIBCURL
5107 xmlDocPtr response = NULL;
5108 xmlChar *code = NULL, *message = NULL;
5109 xmlXPathContextPtr xpath_ctx = NULL;
5110 xmlXPathObjectPtr result = NULL;
5111 char *string = NULL;
5112 #endif
5114 if (!context) return IE_INVALID_CONTEXT;
5115 zfree(context->long_message);
5116 if (!expiration) return IE_INVAL;
5117 zfree(*expiration);
5119 #if HAVE_LIBCURL
5120 /* Check if connection is established */
5121 if (!context->curl) return IE_CONNECTION_CLOSED;
5124 /* Do request and check for success */
5125 err = build_send_check_dbdummy_request(context,
5126 BAD_CAST "GetPasswordInfo",
5127 &response, NULL, NULL, &code, &message);
5128 if (err) goto leave;
5131 /* Extract data */
5132 xpath_ctx = xmlXPathNewContext(response);
5133 if (!xpath_ctx) {
5134 err = IE_ERROR;
5135 goto leave;
5137 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5138 err = IE_ERROR;
5139 goto leave;
5142 /* Set context node */
5143 result = xmlXPathEvalExpression(BAD_CAST
5144 "/isds:GetPasswordInfoResponse", xpath_ctx);
5145 if (!result) {
5146 err = IE_ERROR;
5147 goto leave;
5149 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5150 isds_log_message(context,
5151 _("Missing GetPasswordInfoResponse element"));
5152 err = IE_ISDS;
5153 goto leave;
5155 if (result->nodesetval->nodeNr > 1) {
5156 isds_log_message(context,
5157 _("Multiple GetPasswordInfoResponse element"));
5158 err = IE_ISDS;
5159 goto leave;
5161 xpath_ctx->node = result->nodesetval->nodeTab[0];
5162 xmlXPathFreeObject(result); result = NULL;
5164 /* Extract expiration date */
5165 EXTRACT_STRING("isds:pswExpDate", string);
5166 if (string) {
5167 /* And convert it if any returned. Otherwise expiration is disabled. */
5168 err = timestring2timeval((xmlChar *) string, expiration);
5169 if (err) {
5170 char *string_locale = _isds_utf82locale(string);
5171 if (err == IE_DATE) err = IE_ISDS;
5172 isds_printf_message(context,
5173 _("Could not convert pswExpDate as ISO time: %s"),
5174 string_locale);
5175 free(string_locale);
5176 goto leave;
5180 leave:
5181 if (err) {
5182 if (*expiration) {
5183 zfree(*expiration);
5187 free(string);
5188 xmlXPathFreeObject(result);
5189 xmlXPathFreeContext(xpath_ctx);
5191 free(code);
5192 free(message);
5193 xmlFreeDoc(response);
5195 if (!err)
5196 isds_log(ILF_ISDS, ILL_DEBUG,
5197 _("GetPasswordInfo request processed by server "
5198 "successfully.\n"));
5199 #else /* not HAVE_LIBCURL */
5200 err = IE_NOTSUP;
5201 #endif
5203 return err;
5207 #if HAVE_LIBCURL
5208 /* Request delivering new TOTP code from ISDS through side channel before
5209 * changing password.
5210 * @context is session context
5211 * @password is current password.
5212 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5213 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5214 * function for more details.
5215 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5216 * NULL, if you don't care.
5217 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5218 * error code. */
5219 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5220 const char *password, struct isds_otp *otp, char **refnumber) {
5221 isds_error err = IE_SUCCESS;
5222 char *saved_url = NULL; /* No copy */
5223 #if HAVE_CURL_REAUTHORIZATION_BUG
5224 CURL *saved_curl = NULL; /* No copy */
5225 #endif
5226 xmlNsPtr isds_ns = NULL;
5227 xmlNodePtr request = NULL;
5228 xmlDocPtr response = NULL;
5229 xmlChar *code = NULL, *message = NULL;
5230 const xmlChar *codes[] = {
5231 BAD_CAST "2300",
5232 BAD_CAST "2301",
5233 BAD_CAST "2302"
5235 const char *meanings[] = {
5236 N_("Unexpected error"),
5237 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5238 N_("One-time code could not been sent. Try later again.")
5240 const isds_otp_resolution resolutions[] = {
5241 OTP_RESOLUTION_UNKNOWN,
5242 OTP_RESOLUTION_TO_FAST,
5243 OTP_RESOLUTION_TOTP_NOT_SENT
5246 if (NULL == context) return IE_INVALID_CONTEXT;
5247 zfree(context->long_message);
5248 if (NULL == password) {
5249 isds_log_message(context,
5250 _("Second argument (password) of isds_change_password() "
5251 "is NULL"));
5252 return IE_INVAL;
5255 /* Check if connection is established
5256 * TODO: This check should be done downstairs. */
5257 if (!context->curl) return IE_CONNECTION_CLOSED;
5259 if (!context->otp) {
5260 isds_log_message(context, _("This function requires OTP-authenticated "
5261 "context"));
5262 return IE_INVALID_CONTEXT;
5264 if (NULL == otp) {
5265 isds_log_message(context, _("If one-time password authentication "
5266 "method is in use, requesting new OTP code requires "
5267 "one-time credentials argument either"));
5268 return IE_INVAL;
5270 if (otp->method != OTP_TIME) {
5271 isds_log_message(context, _("Requesting new time-based OTP code from "
5272 "server requires one-time password authentication "
5273 "method"));
5274 return IE_INVAL;
5276 if (otp->otp_code != NULL) {
5277 isds_log_message(context, _("Requesting new time-based OTP code from "
5278 "server requires undefined OTP code member in "
5279 "one-time credentials argument"));
5280 return IE_INVAL;
5284 /* Build request */
5285 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5286 if (!request) {
5287 isds_log_message(context, _("Could not build SendSMSCode request"));
5288 return IE_ERROR;
5290 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5291 if(!isds_ns) {
5292 isds_log_message(context, _("Could not create ISDS name space"));
5293 xmlFreeNode(request);
5294 return IE_ERROR;
5296 xmlSetNs(request, isds_ns);
5298 /* Change URL temporarily for sending this request only */
5300 char *new_url = NULL;
5301 if ((err = _isds_build_url_from_context(context,
5302 "%1$.*2$sasws/changePassword", &new_url))) {
5303 goto leave;
5305 saved_url = context->url;
5306 context->url = new_url;
5309 /* Store credentials for sending this request only */
5310 context->otp_credentials = otp;
5311 _isds_discard_credentials(context, 0);
5312 if ((err = _isds_store_credentials(context, context->saved_username,
5313 password, NULL))) {
5314 _isds_discard_credentials(context, 0);
5315 goto leave;
5317 #if HAVE_CURL_REAUTHORIZATION_BUG
5318 saved_curl = context->curl;
5319 context->curl = curl_easy_init();
5320 if (NULL == context->curl) {
5321 err = IE_ERROR;
5322 goto leave;
5324 if (context->timeout) {
5325 err = isds_set_timeout(context, context->timeout);
5326 if (err) goto leave;
5328 #endif
5330 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5332 /* Sent request */
5333 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5335 /* Remove temporal credentials */
5336 _isds_discard_credentials(context, 0);
5337 /* Detach pointer to OTP credentials from context */
5338 context->otp_credentials = NULL;
5339 /* Keep context->otp true to keep signaling this is OTP session */
5341 /* Destroy request */
5342 xmlFreeNode(request); request = NULL;
5344 if (err) {
5345 isds_log(ILF_ISDS, ILL_DEBUG,
5346 _("Processing ISDS response on SendSMSCode request failed\n"));
5347 goto leave;
5350 /* Check for response status */
5351 err = isds_response_status(context, SERVICE_ASWS, response,
5352 &code, &message, (xmlChar **)refnumber);
5353 if (err) {
5354 isds_log(ILF_ISDS, ILL_DEBUG,
5355 _("ISDS response on SendSMSCode request is missing "
5356 "status\n"));
5357 goto leave;
5360 /* Check for error */
5361 if (xmlStrcmp(code, BAD_CAST "0000")) {
5362 char *code_locale = _isds_utf82locale((char*)code);
5363 char *message_locale = _isds_utf82locale((char*)message);
5364 int i;
5365 isds_log(ILF_ISDS, ILL_DEBUG,
5366 _("Server refused to send new code on SendSMSCode "
5367 "request (code=%s, message=%s)\n"),
5368 code_locale, message_locale);
5370 /* Check for known error codes */
5371 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5372 if (!xmlStrcmp(code, codes[i])) break;
5374 if (i < sizeof(codes)/sizeof(*codes)) {
5375 isds_log_message(context, _(meanings[i]));
5376 /* Mimic otp->resolution according to the code, specification does
5377 * prescribe OTP header to be available. */
5378 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5379 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5380 otp->resolution = resolutions[i];
5381 } else
5382 isds_log_message(context, message_locale);
5384 free(code_locale);
5385 free(message_locale);
5387 err = IE_ISDS;
5388 goto leave;
5391 /* Otherwise new code sent successfully */
5392 /* Mimic otp->resolution according to the code, specification does
5393 * prescribe OTP header to be available. */
5394 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5395 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5397 leave:
5398 if (NULL != saved_url) {
5399 /* Revert URL to original one */
5400 zfree(context->url);
5401 context->url = saved_url;
5403 #if HAVE_CURL_REAUTHORIZATION_BUG
5404 if (NULL != saved_curl) {
5405 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5406 context->curl = saved_curl;
5408 #endif
5410 free(code);
5411 free(message);
5412 xmlFreeDoc(response);
5413 xmlFreeNode(request);
5415 if (!err)
5416 isds_log(ILF_ISDS, ILL_DEBUG,
5417 _("New OTP code has been sent successfully on SendSMSCode "
5418 "request.\n"));
5419 return err;
5423 /* Convert response status code to isds_error code and set long message
5424 * @context is context to save long message to
5425 * @map is mapping from codes to errors and messages. Pass NULL for generic
5426 * handling.
5427 * @code is status code to translate
5428 * @message is non-localized status message to put into long message in case
5429 * of uknown error. It can be NULL if server did not provide any.
5430 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5431 * invalid invocation. */
5432 static isds_error statuscode2isds_error(struct isds_ctx *context,
5433 const struct code_map_isds_error *map,
5434 const xmlChar *code, const xmlChar *message) {
5435 if (NULL == code) {
5436 isds_log_message(context,
5437 _("NULL status code passed to statuscode2isds_error()"));
5438 return IE_INVAL;
5441 if (NULL != map) {
5442 /* Check for known error codes */
5443 for (int i=0; map->codes[i] != NULL; i++) {
5444 if (!xmlStrcmp(code, map->codes[i])) {
5445 isds_log_message(context, _(map->meanings[i]));
5446 return map->errors[i];
5451 /* Other error */
5452 if (xmlStrcmp(code, BAD_CAST "0000")) {
5453 char *message_locale = _isds_utf82locale((char*)message);
5454 if (NULL == message_locale)
5455 isds_log_message(context, _("ISDS server returned unknown error"));
5456 else
5457 isds_log_message(context, message_locale);
5458 free(message_locale);
5459 return IE_ISDS;
5462 return IE_SUCCESS;
5464 #endif
5467 /* Change user password in ISDS.
5468 * User must supply old password, new password will takes effect after some
5469 * time, current session can continue. Password must fulfill some constraints.
5470 * @context is session context
5471 * @old_password is current password.
5472 * @new_password is requested new password
5473 * @otp auxiliary data required if one-time password authentication is in use,
5474 * defines OTP code (if known) and returns fine grade resolution of OTP
5475 * procedure. Pass NULL, if one-time password authentication is not needed.
5476 * Please note the @otp argument must match OTP method used at log-in time. See
5477 * isds_login() function for more details.
5478 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5479 * NULL, if you don't care.
5480 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5481 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5482 * awaiting OTP code that has been delivered by side channel to the user. */
5483 isds_error isds_change_password(struct isds_ctx *context,
5484 const char *old_password, const char *new_password,
5485 struct isds_otp *otp, char **refnumber) {
5486 isds_error err = IE_SUCCESS;
5487 #if HAVE_LIBCURL
5488 char *saved_url = NULL; /* No copy */
5489 #if HAVE_CURL_REAUTHORIZATION_BUG
5490 CURL *saved_curl = NULL; /* No copy */
5491 #endif
5492 xmlNsPtr isds_ns = NULL;
5493 xmlNodePtr request = NULL, node;
5494 xmlDocPtr response = NULL;
5495 xmlChar *code = NULL, *message = NULL;
5496 const xmlChar *codes[] = {
5497 BAD_CAST "1066",
5498 BAD_CAST "1067",
5499 BAD_CAST "1079",
5500 BAD_CAST "1080",
5501 BAD_CAST "1081",
5502 BAD_CAST "1082",
5503 BAD_CAST "1083",
5504 BAD_CAST "1090",
5505 BAD_CAST "1091",
5506 BAD_CAST "2300",
5507 BAD_CAST "9204"
5509 const char *meanings[] = {
5510 N_("Password length must be between 8 and 32 characters"),
5511 N_("Password cannot be reused"), /* Server does not distinguish 1067
5512 and 1091 on ChangePasswordOTP */
5513 N_("Password contains forbidden character"),
5514 N_("Password must contain at least one upper-case letter, "
5515 "one lower-case, and one digit"),
5516 N_("Password cannot contain sequence of three identical characters"),
5517 N_("Password cannot contain user identifier"),
5518 N_("Password is too simmple"),
5519 N_("Old password is not valid"),
5520 N_("Password cannot be reused"),
5521 N_("Unexpected error"),
5522 N_("LDAP update error")
5524 #endif
5526 if (!context) return IE_INVALID_CONTEXT;
5527 zfree(context->long_message);
5528 if (NULL != refnumber)
5529 zfree(*refnumber);
5530 if (NULL == old_password) {
5531 isds_log_message(context,
5532 _("Second argument (old password) of isds_change_password() "
5533 "is NULL"));
5534 return IE_INVAL;
5536 if (NULL == otp && NULL == new_password) {
5537 isds_log_message(context,
5538 _("Third argument (new password) of isds_change_password() "
5539 "is NULL"));
5540 return IE_INVAL;
5543 #if HAVE_LIBCURL
5544 /* Check if connection is established
5545 * TODO: This check should be done downstairs. */
5546 if (!context->curl) return IE_CONNECTION_CLOSED;
5548 if (context->otp && NULL == otp) {
5549 isds_log_message(context, _("If one-time password authentication "
5550 "method is in use, changing password requires one-time "
5551 "credentials either"));
5552 return IE_INVAL;
5555 /* Build ChangeISDSPassword request */
5556 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5557 BAD_CAST "ChangePasswordOTP");
5558 if (!request) {
5559 isds_log_message(context, (NULL == otp) ?
5560 _("Could not build ChangeISDSPassword request") :
5561 _("Could not build ChangePasswordOTP request"));
5562 return IE_ERROR;
5564 isds_ns = xmlNewNs(request,
5565 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5566 NULL);
5567 if(!isds_ns) {
5568 isds_log_message(context, _("Could not create ISDS name space"));
5569 xmlFreeNode(request);
5570 return IE_ERROR;
5572 xmlSetNs(request, isds_ns);
5574 INSERT_STRING(request, "dbOldPassword", old_password);
5575 INSERT_STRING(request, "dbNewPassword", new_password);
5577 if (NULL != otp) {
5578 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5579 switch (otp->method) {
5580 case OTP_HMAC:
5581 isds_log(ILF_SEC, ILL_INFO,
5582 _("Selected authentication method: "
5583 "HMAC-based one-time password\n"));
5584 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5585 break;
5586 case OTP_TIME:
5587 isds_log(ILF_SEC, ILL_INFO,
5588 _("Selected authentication method: "
5589 "Time-based one-time password\n"));
5590 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5591 if (otp->otp_code == NULL) {
5592 isds_log(ILF_SEC, ILL_INFO,
5593 _("OTP code has not been provided by "
5594 "application, requesting server for "
5595 "new one.\n"));
5596 err = _isds_request_totp_code(context, old_password, otp,
5597 refnumber);
5598 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5599 goto leave;
5601 } else {
5602 isds_log(ILF_SEC, ILL_INFO,
5603 _("OTP code has been provided by "
5604 "application, not requesting server "
5605 "for new one.\n"));
5607 break;
5608 default:
5609 isds_log_message(context,
5610 _("Unknown one-time password authentication "
5611 "method requested by application"));
5612 err = IE_ENUM;
5613 goto leave;
5616 /* Change URL temporarily for sending this request only */
5618 char *new_url = NULL;
5619 if ((err = _isds_build_url_from_context(context,
5620 "%1$.*2$sasws/changePassword", &new_url))) {
5621 goto leave;
5623 saved_url = context->url;
5624 context->url = new_url;
5627 /* Store credentials for sending this request only */
5628 context->otp_credentials = otp;
5629 _isds_discard_credentials(context, 0);
5630 if ((err = _isds_store_credentials(context, context->saved_username,
5631 old_password, NULL))) {
5632 _isds_discard_credentials(context, 0);
5633 goto leave;
5635 #if HAVE_CURL_REAUTHORIZATION_BUG
5636 saved_curl = context->curl;
5637 context->curl = curl_easy_init();
5638 if (NULL == context->curl) {
5639 err = IE_ERROR;
5640 goto leave;
5642 if (context->timeout) {
5643 err = isds_set_timeout(context, context->timeout);
5644 if (err) goto leave;
5646 #endif
5649 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5650 _("Sending ChangeISDSPassword request to ISDS\n") :
5651 _("Sending ChangePasswordOTP request to ISDS\n"));
5653 /* Sent request */
5654 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5655 request, &response, NULL, NULL);
5657 if (otp) {
5658 /* Remove temporal credentials */
5659 _isds_discard_credentials(context, 0);
5660 /* Detach pointer to OTP credentials from context */
5661 context->otp_credentials = NULL;
5662 /* Keep context->otp true to keep signaling this is OTP session */
5665 /* Destroy request */
5666 xmlFreeNode(request); request = NULL;
5668 if (err) {
5669 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5670 _("Processing ISDS response on ChangeISDSPassword "
5671 "request failed\n") :
5672 _("Processing ISDS response on ChangePasswordOTP "
5673 "request failed\n"));
5674 goto leave;
5677 /* Check for response status */
5678 err = isds_response_status(context,
5679 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5680 &code, &message, (xmlChar **)refnumber);
5681 if (err) {
5682 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5683 _("ISDS response on ChangeISDSPassword request is missing "
5684 "status\n") :
5685 _("ISDS response on ChangePasswordOTP request is missing "
5686 "status\n"));
5687 goto leave;
5690 /* Check for known error codes */
5691 for (int i=0; i < sizeof(codes)/sizeof(*codes); i++) {
5692 if (!xmlStrcmp(code, codes[i])) {
5693 char *code_locale = _isds_utf82locale((char*)code);
5694 char *message_locale = _isds_utf82locale((char*)message);
5695 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5696 _("Server refused to change password on ChangeISDSPassword "
5697 "request (code=%s, message=%s)\n") :
5698 _("Server refused to change password on ChangePasswordOTP "
5699 "request (code=%s, message=%s)\n"),
5700 code_locale, message_locale);
5701 free(code_locale);
5702 free(message_locale);
5703 isds_log_message(context, _(meanings[i]));
5704 err = IE_INVAL;
5705 goto leave;
5709 /* Other error */
5710 if (xmlStrcmp(code, BAD_CAST "0000")) {
5711 char *code_locale = _isds_utf82locale((char*)code);
5712 char *message_locale = _isds_utf82locale((char*)message);
5713 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5714 _("Server refused to change password on ChangeISDSPassword "
5715 "request (code=%s, message=%s)\n") :
5716 _("Server refused to change password on ChangePasswordOTP "
5717 "request (code=%s, message=%s)\n"),
5718 code_locale, message_locale);
5719 isds_log_message(context, message_locale);
5720 free(code_locale);
5721 free(message_locale);
5722 err = IE_ISDS;
5723 goto leave;
5726 /* Otherwise password changed successfully */
5728 leave:
5729 if (NULL != saved_url) {
5730 /* Revert URL to original one */
5731 zfree(context->url);
5732 context->url = saved_url;
5734 #if HAVE_CURL_REAUTHORIZATION_BUG
5735 if (NULL != saved_curl) {
5736 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5737 context->curl = saved_curl;
5739 #endif
5741 free(code);
5742 free(message);
5743 xmlFreeDoc(response);
5744 xmlFreeNode(request);
5746 if (!err)
5747 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5748 _("Password changed successfully on ChangeISDSPassword "
5749 "request.\n") :
5750 _("Password changed successfully on ChangePasswordOTP "
5751 "request.\n"));
5752 #else /* not HAVE_LIBCURL */
5753 err = IE_NOTSUP;
5754 #endif
5756 return err;
5760 #if HAVE_LIBCURL
5761 /* Generic middle part with request sending and response check.
5762 * It sends prepared request and checks for error code.
5763 * @context is ISDS session context.
5764 * @service is ISDS service handler
5765 * @service_name is name in scope of given @service
5766 * @request is XML tree with request. Will be freed to save memory.
5767 * @response is XML document outputting ISDS response.
5768 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5769 * @map is mapping from status code to library error. Pass NULL if no special
5770 * handling is requested.
5771 * NULL, if you don't care. */
5772 static isds_error send_destroy_request_check_response(
5773 struct isds_ctx *context,
5774 const isds_service service, const xmlChar *service_name,
5775 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5776 const struct code_map_isds_error *map) {
5777 isds_error err = IE_SUCCESS;
5778 char *service_name_locale = NULL;
5779 xmlChar *code = NULL, *message = NULL;
5782 if (!context) return IE_INVALID_CONTEXT;
5783 if (!service_name || *service_name == '\0' || !request || !*request ||
5784 !response)
5785 return IE_INVAL;
5787 /* Check if connection is established
5788 * TODO: This check should be done downstairs. */
5789 if (!context->curl) return IE_CONNECTION_CLOSED;
5791 service_name_locale = _isds_utf82locale((char*) service_name);
5792 if (!service_name_locale) {
5793 err = IE_NOMEM;
5794 goto leave;
5797 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5798 service_name_locale);
5800 /* Send request */
5801 err = _isds(context, service, *request, response, NULL, NULL);
5802 xmlFreeNode(*request); *request = NULL;
5804 if (err) {
5805 isds_log(ILF_ISDS, ILL_DEBUG,
5806 _("Processing ISDS response on %s request failed\n"),
5807 service_name_locale);
5808 goto leave;
5811 /* Check for response status */
5812 err = isds_response_status(context, service, *response,
5813 &code, &message, refnumber);
5814 if (err) {
5815 isds_log(ILF_ISDS, ILL_DEBUG,
5816 _("ISDS response on %s request is missing status\n"),
5817 service_name_locale);
5818 goto leave;
5821 err = statuscode2isds_error(context, map, code, message);
5823 /* Request processed, but server failed */
5824 if (xmlStrcmp(code, BAD_CAST "0000")) {
5825 char *code_locale = _isds_utf82locale((char*) code);
5826 char *message_locale = _isds_utf82locale((char*) message);
5827 isds_log(ILF_ISDS, ILL_DEBUG,
5828 _("Server refused %s request (code=%s, message=%s)\n"),
5829 service_name_locale, code_locale, message_locale);
5830 free(code_locale);
5831 free(message_locale);
5832 goto leave;
5836 leave:
5837 free(code);
5838 free(message);
5839 if (err && *response) {
5840 xmlFreeDoc(*response);
5841 *response = NULL;
5843 if (*request) {
5844 xmlFreeNode(*request);
5845 *request = NULL;
5847 free(service_name_locale);
5849 return err;
5853 /* Generic bottom half with request sending.
5854 * It sends prepared request, checks for error code, destroys response and
5855 * request and log success or failure.
5856 * @context is ISDS session context.
5857 * @service is ISDS service handler
5858 * @service_name is name in scope of given @service
5859 * @request is XML tree with request. Will be freed to save memory.
5860 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5861 * NULL, if you don't care. */
5862 static isds_error send_request_check_drop_response(
5863 struct isds_ctx *context,
5864 const isds_service service, const xmlChar *service_name,
5865 xmlNodePtr *request, xmlChar **refnumber) {
5866 isds_error err = IE_SUCCESS;
5867 xmlDocPtr response = NULL;
5870 if (!context) return IE_INVALID_CONTEXT;
5871 if (!service_name || *service_name == '\0' || !request || !*request)
5872 return IE_INVAL;
5874 /* Send request and check response*/
5875 err = send_destroy_request_check_response(context,
5876 service, service_name, request, &response, refnumber, NULL);
5878 xmlFreeDoc(response);
5880 if (*request) {
5881 xmlFreeNode(*request);
5882 *request = NULL;
5885 if (!err) {
5886 char *service_name_locale = _isds_utf82locale((char *) service_name);
5887 isds_log(ILF_ISDS, ILL_DEBUG,
5888 _("%s request processed by server successfully.\n"),
5889 service_name_locale);
5890 free(service_name_locale);
5893 return err;
5897 /* Insert isds_credentials_delivery structure into XML request if not NULL
5898 * @context is session context
5899 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5900 * credentials delivery. The email field is passed.
5901 * @parent is XML element where to insert */
5902 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5903 const struct isds_credentials_delivery *credentials_delivery,
5904 xmlNodePtr parent) {
5905 isds_error err = IE_SUCCESS;
5906 xmlNodePtr node;
5908 if (!context) return IE_INVALID_CONTEXT;
5909 if (!parent) return IE_INVAL;
5911 if (credentials_delivery) {
5912 /* Following elements are valid only for services:
5913 * NewAccessData, AddDataBoxUser, CreateDataBox */
5914 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5915 INSERT_STRING(parent, "email", credentials_delivery->email);
5918 leave:
5919 return err;
5923 /* Extract credentials delivery from ISDS response.
5924 * @context is session context
5925 * @credentials_delivery is pointer to valid structure to fill in returned
5926 * user's password (and new log-in name). If NULL, do not extract the data.
5927 * @response is pointer to XML document with ISDS response
5928 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5929 * @return IE_SUCCESS even if new user name has not been found because it's not
5930 * clear whether it's returned always. */
5931 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5932 struct isds_credentials_delivery *credentials_delivery,
5933 xmlDocPtr response, const char *request_name) {
5934 isds_error err = IE_SUCCESS;
5935 xmlXPathContextPtr xpath_ctx = NULL;
5936 xmlXPathObjectPtr result = NULL;
5937 char *xpath_query = NULL;
5939 if (!context) return IE_INVALID_CONTEXT;
5940 if (credentials_delivery) {
5941 zfree(credentials_delivery->token);
5942 zfree(credentials_delivery->new_user_name);
5944 if (!response || !request_name || !*request_name) return IE_INVAL;
5947 /* Extract optional token */
5948 if (credentials_delivery) {
5949 xpath_ctx = xmlXPathNewContext(response);
5950 if (!xpath_ctx) {
5951 err = IE_ERROR;
5952 goto leave;
5954 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5955 err = IE_ERROR;
5956 goto leave;
5959 /* Verify root element */
5960 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
5961 request_name)) {
5962 err = IE_NOMEM;
5963 goto leave;
5965 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
5966 if (!result) {
5967 err = IE_ERROR;
5968 goto leave;
5970 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5971 char *request_name_locale = _isds_utf82locale(request_name);
5972 isds_log(ILF_ISDS, ILL_WARNING,
5973 _("Wrong element in ISDS response for %s request "
5974 "while extracting credentials delivery details\n"),
5975 request_name_locale);
5976 free(request_name_locale);
5977 err = IE_ERROR;
5978 goto leave;
5980 xpath_ctx->node = result->nodesetval->nodeTab[0];
5983 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5984 * optional. */
5985 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
5987 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
5988 if (!credentials_delivery->token) {
5989 char *request_name_locale = _isds_utf82locale(request_name);
5990 isds_log(ILF_ISDS, ILL_ERR,
5991 _("ISDS did not return token on %s request "
5992 "even if requested\n"), request_name_locale);
5993 free(request_name_locale);
5994 err = IE_ERROR;
5998 leave:
5999 free(xpath_query);
6000 xmlXPathFreeObject(result);
6001 xmlXPathFreeContext(xpath_ctx);
6003 return err;
6007 /* Build XSD:tCreateDBInput request type for box creating.
6008 * @context is session context
6009 * @request outputs built XML tree
6010 * @service_name is request name of SERVICE_DB_MANIPULATION service
6011 * @box is box description to create including single primary user (in case of
6012 * FO box type)
6013 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6014 * box, or contact address of PFO box owner)
6015 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6016 * @upper_box_id is optional ID of supper box if currently created box is
6017 * subordinated.
6018 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6019 * don't care.
6020 * @credentials_delivery is valid pointer if ISDS should return token that box
6021 * owner can use to obtain his new credentials in on-line way. Then valid email
6022 * member value should be supplied.
6023 * @approval is optional external approval of box manipulation */
6024 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
6025 xmlNodePtr *request, const xmlChar *service_name,
6026 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6027 const xmlChar *former_names, const xmlChar *upper_box_id,
6028 const xmlChar *ceo_label,
6029 const struct isds_credentials_delivery *credentials_delivery,
6030 const struct isds_approval *approval) {
6031 isds_error err = IE_SUCCESS;
6032 xmlNsPtr isds_ns = NULL;
6033 xmlNodePtr node, dbPrimaryUsers;
6034 xmlChar *string = NULL;
6035 const struct isds_list *item;
6038 if (!context) return IE_INVALID_CONTEXT;
6039 if (!request || !service_name || service_name[0] == '\0' || !box)
6040 return IE_INVAL;
6043 /* Build CreateDataBox-similar request */
6044 *request = xmlNewNode(NULL, service_name);
6045 if (!*request) {
6046 char *service_name_locale = _isds_utf82locale((char*) service_name);
6047 isds_printf_message(context, _("Could build %s request"),
6048 service_name_locale);
6049 free(service_name_locale);
6050 return IE_ERROR;
6052 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
6053 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
6054 if (!isds_ns) {
6055 isds_log_message(context, _("Could not create ISDS1 name space"));
6056 xmlFreeNode(*request);
6057 return IE_ERROR;
6059 } else {
6060 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6061 if (!isds_ns) {
6062 isds_log_message(context, _("Could not create ISDS name space"));
6063 xmlFreeNode(*request);
6064 return IE_ERROR;
6067 xmlSetNs(*request, isds_ns);
6069 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6070 err = insert_DbOwnerInfo(context, box, node);
6071 if (err) goto leave;
6073 /* Insert users */
6074 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6075 * verbose documentation allows none dbUserInfo */
6076 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6077 for (item = users; item; item = item->next) {
6078 if (item->data) {
6079 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6080 err = insert_DbUserInfo(context,
6081 (struct isds_DbUserInfo *) item->data, node);
6082 if (err) goto leave;
6086 INSERT_STRING(*request, "dbFormerNames", former_names);
6087 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6088 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6090 err = insert_credentials_delivery(context, credentials_delivery, *request);
6091 if (err) goto leave;
6093 err = insert_GExtApproval(context, approval, *request);
6094 if (err) goto leave;
6096 leave:
6097 if (err) {
6098 xmlFreeNode(*request);
6099 *request = NULL;
6101 free(string);
6102 return err;
6104 #endif /* HAVE_LIBCURL */
6107 /* Create new box.
6108 * @context is session context
6109 * @box is box description to create including single primary user (in case of
6110 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6111 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6112 * box, or contact address of PFO box owner)
6113 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6114 * @upper_box_id is optional ID of supper box if currently created box is
6115 * subordinated.
6116 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6117 * @credentials_delivery is NULL if new password should be delivered off-line
6118 * to box owner. It is valid pointer if owner should obtain new password on-line
6119 * on dedicated web server. Then input @credentials_delivery.email value is
6120 * his e-mail address he must provide to dedicated web server together
6121 * with output reallocated @credentials_delivery.token member. Output
6122 * member @credentials_delivery.new_user_name is unused up on this call.
6123 * @approval is optional external approval of box manipulation
6124 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6125 * NULL, if you don't care.*/
6126 isds_error isds_add_box(struct isds_ctx *context,
6127 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6128 const char *former_names, const char *upper_box_id,
6129 const char *ceo_label,
6130 struct isds_credentials_delivery *credentials_delivery,
6131 const struct isds_approval *approval, char **refnumber) {
6132 isds_error err = IE_SUCCESS;
6133 #if HAVE_LIBCURL
6134 xmlNodePtr request = NULL;
6135 xmlDocPtr response = NULL;
6136 xmlXPathContextPtr xpath_ctx = NULL;
6137 xmlXPathObjectPtr result = NULL;
6138 #endif
6141 if (!context) return IE_INVALID_CONTEXT;
6142 zfree(context->long_message);
6143 if (credentials_delivery) {
6144 zfree(credentials_delivery->token);
6145 zfree(credentials_delivery->new_user_name);
6147 if (!box) return IE_INVAL;
6149 #if HAVE_LIBCURL
6150 /* Scratch box ID */
6151 zfree(box->dbID);
6153 /* Build CreateDataBox request */
6154 err = build_CreateDBInput_request(context,
6155 &request, BAD_CAST "CreateDataBox",
6156 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6157 (xmlChar *) ceo_label, credentials_delivery, approval);
6158 if (err) goto leave;
6160 /* Send it to server and process response */
6161 err = send_destroy_request_check_response(context,
6162 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6163 &response, (xmlChar **) refnumber, NULL);
6165 /* Extract box ID */
6166 xpath_ctx = xmlXPathNewContext(response);
6167 if (!xpath_ctx) {
6168 err = IE_ERROR;
6169 goto leave;
6171 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6172 err = IE_ERROR;
6173 goto leave;
6175 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6177 /* Extract optional token */
6178 err = extract_credentials_delivery(context, credentials_delivery, response,
6179 "CreateDataBox");
6181 leave:
6182 xmlXPathFreeObject(result);
6183 xmlXPathFreeContext(xpath_ctx);
6184 xmlFreeDoc(response);
6185 xmlFreeNode(request);
6187 if (!err) {
6188 isds_log(ILF_ISDS, ILL_DEBUG,
6189 _("CreateDataBox request processed by server successfully.\n"));
6191 #else /* not HAVE_LIBCURL */
6192 err = IE_NOTSUP;
6193 #endif
6195 return err;
6199 /* Notify ISDS about new PFO entity.
6200 * This function has no real effect.
6201 * @context is session context
6202 * @box is PFO description including single primary user.
6203 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6204 * @former_names is optional undocumented string. Pass NULL if you don't care.
6205 * @upper_box_id is optional ID of supper box if currently created box is
6206 * subordinated.
6207 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6208 * @approval is optional external approval of box manipulation
6209 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6210 * NULL, if you don't care.*/
6211 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6212 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6213 const char *former_names, const char *upper_box_id,
6214 const char *ceo_label, const struct isds_approval *approval,
6215 char **refnumber) {
6216 isds_error err = IE_SUCCESS;
6217 #if HAVE_LIBCURL
6218 xmlNodePtr request = NULL;
6219 #endif
6221 if (!context) return IE_INVALID_CONTEXT;
6222 zfree(context->long_message);
6223 if (!box) return IE_INVAL;
6225 #if HAVE_LIBCURL
6226 /* Build CreateDataBoxPFOInfo request */
6227 err = build_CreateDBInput_request(context,
6228 &request, BAD_CAST "CreateDataBoxPFOInfo",
6229 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6230 (xmlChar *) ceo_label, NULL, approval);
6231 if (err) goto leave;
6233 /* Send it to server and process response */
6234 err = send_request_check_drop_response(context,
6235 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6236 (xmlChar **) refnumber);
6237 /* XXX: XML Schema names output dbID element but textual documentation
6238 * states no box identifier is returned. */
6239 leave:
6240 xmlFreeNode(request);
6241 #else /* not HAVE_LIBCURL */
6242 err = IE_NOTSUP;
6243 #endif
6244 return err;
6248 /* Common implementation for removing given box.
6249 * @context is session context
6250 * @service_name is UTF-8 encoded name fo ISDS service
6251 * @box is box description to delete
6252 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6253 * carry sane value. If NULL, do not inject this information into request.
6254 * @approval is optional external approval of box manipulation
6255 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6256 * NULL, if you don't care.*/
6257 isds_error _isds_delete_box_common(struct isds_ctx *context,
6258 const xmlChar *service_name,
6259 const struct isds_DbOwnerInfo *box, const struct tm *since,
6260 const struct isds_approval *approval, char **refnumber) {
6261 isds_error err = IE_SUCCESS;
6262 #if HAVE_LIBCURL
6263 xmlNsPtr isds_ns = NULL;
6264 xmlNodePtr request = NULL;
6265 xmlNodePtr node;
6266 xmlChar *string = NULL;
6267 #endif
6270 if (!context) return IE_INVALID_CONTEXT;
6271 zfree(context->long_message);
6272 if (!service_name || !*service_name || !box) return IE_INVAL;
6275 #if HAVE_LIBCURL
6276 /* Build DeleteDataBox(Promptly) request */
6277 request = xmlNewNode(NULL, service_name);
6278 if (!request) {
6279 char *service_name_locale = _isds_utf82locale((char*)service_name);
6280 isds_printf_message(context,
6281 _("Could build %s request"), service_name_locale);
6282 free(service_name_locale);
6283 return IE_ERROR;
6285 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6286 if(!isds_ns) {
6287 isds_log_message(context, _("Could not create ISDS name space"));
6288 xmlFreeNode(request);
6289 return IE_ERROR;
6291 xmlSetNs(request, isds_ns);
6293 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6294 err = insert_DbOwnerInfo(context, box, node);
6295 if (err) goto leave;
6297 if (since) {
6298 err = tm2datestring(since, &string);
6299 if (err) {
6300 isds_log_message(context,
6301 _("Could not convert `since' argument to ISO date string"));
6302 goto leave;
6304 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6305 zfree(string);
6308 err = insert_GExtApproval(context, approval, request);
6309 if (err) goto leave;
6312 /* Send it to server and process response */
6313 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6314 service_name, &request, (xmlChar **) refnumber);
6316 leave:
6317 xmlFreeNode(request);
6318 free(string);
6319 #else /* not HAVE_LIBCURL */
6320 err = IE_NOTSUP;
6321 #endif
6322 return err;
6326 /* Remove given box permanently.
6327 * @context is session context
6328 * @box is box description to delete
6329 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6330 * carry sane value.
6331 * @approval is optional external approval of box manipulation
6332 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6333 * NULL, if you don't care.*/
6334 isds_error isds_delete_box(struct isds_ctx *context,
6335 const struct isds_DbOwnerInfo *box, const struct tm *since,
6336 const struct isds_approval *approval, char **refnumber) {
6337 if (!context) return IE_INVALID_CONTEXT;
6338 zfree(context->long_message);
6339 if (!box || !since) return IE_INVAL;
6341 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6342 box, since, approval, refnumber);
6346 /* Undocumented function.
6347 * @context is session context
6348 * @box is box description to delete
6349 * @approval is optional external approval of box manipulation
6350 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6351 * NULL, if you don't care.*/
6352 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6353 const struct isds_DbOwnerInfo *box,
6354 const struct isds_approval *approval, char **refnumber) {
6355 if (!context) return IE_INVALID_CONTEXT;
6356 zfree(context->long_message);
6357 if (!box) return IE_INVAL;
6359 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6360 box, NULL, approval, refnumber);
6364 /* Update data about given box.
6365 * @context is session context
6366 * @old_box current box description
6367 * @new_box are updated data about @old_box
6368 * @approval is optional external approval of box manipulation
6369 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6370 * NULL, if you don't care.*/
6371 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6372 const struct isds_DbOwnerInfo *old_box,
6373 const struct isds_DbOwnerInfo *new_box,
6374 const struct isds_approval *approval, char **refnumber) {
6375 isds_error err = IE_SUCCESS;
6376 #if HAVE_LIBCURL
6377 xmlNsPtr isds_ns = NULL;
6378 xmlNodePtr request = NULL;
6379 xmlNodePtr node;
6380 #endif
6383 if (!context) return IE_INVALID_CONTEXT;
6384 zfree(context->long_message);
6385 if (!old_box || !new_box) return IE_INVAL;
6388 #if HAVE_LIBCURL
6389 /* Build UpdateDataBoxDescr request */
6390 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6391 if (!request) {
6392 isds_log_message(context,
6393 _("Could build UpdateDataBoxDescr request"));
6394 return IE_ERROR;
6396 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6397 if(!isds_ns) {
6398 isds_log_message(context, _("Could not create ISDS name space"));
6399 xmlFreeNode(request);
6400 return IE_ERROR;
6402 xmlSetNs(request, isds_ns);
6404 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6405 err = insert_DbOwnerInfo(context, old_box, node);
6406 if (err) goto leave;
6408 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6409 err = insert_DbOwnerInfo(context, new_box, node);
6410 if (err) goto leave;
6412 err = insert_GExtApproval(context, approval, request);
6413 if (err) goto leave;
6416 /* Send it to server and process response */
6417 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6418 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6420 leave:
6421 xmlFreeNode(request);
6422 #else /* not HAVE_LIBCURL */
6423 err = IE_NOTSUP;
6424 #endif
6426 return err;
6430 #if HAVE_LIBCURL
6431 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6432 * code
6433 * @context is session context
6434 * @service is SOAP service
6435 * @service_name is name of request in @service
6436 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6437 * @box_id is box ID of interest
6438 * @approval is optional external approval of box manipulation
6439 * @response is server SOAP body response as XML document
6440 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6441 * NULL, if you don't care.
6442 * @return error coded from lower layer, context message will be set up
6443 * appropriately. */
6444 static isds_error build_send_dbid_request_check_response(
6445 struct isds_ctx *context, const isds_service service,
6446 const xmlChar *service_name, const xmlChar *box_id_element,
6447 const xmlChar *box_id, const struct isds_approval *approval,
6448 xmlDocPtr *response, xmlChar **refnumber) {
6450 isds_error err = IE_SUCCESS;
6451 char *service_name_locale = NULL, *box_id_locale = NULL;
6452 xmlNodePtr request = NULL, node;
6453 xmlNsPtr isds_ns = NULL;
6455 if (!context) return IE_INVALID_CONTEXT;
6456 if (!service_name || !box_id) return IE_INVAL;
6457 if (!response) return IE_INVAL;
6459 /* Free output argument */
6460 xmlFreeDoc(*response); *response = NULL;
6462 /* Prepare strings */
6463 service_name_locale = _isds_utf82locale((char*)service_name);
6464 if (!service_name_locale) {
6465 err = IE_NOMEM;
6466 goto leave;
6468 box_id_locale = _isds_utf82locale((char*)box_id);
6469 if (!box_id_locale) {
6470 err = IE_NOMEM;
6471 goto leave;
6474 /* Build request */
6475 request = xmlNewNode(NULL, service_name);
6476 if (!request) {
6477 isds_printf_message(context,
6478 _("Could not build %s request for %s box"), service_name_locale,
6479 box_id_locale);
6480 err = IE_ERROR;
6481 goto leave;
6483 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6484 if(!isds_ns) {
6485 isds_log_message(context, _("Could not create ISDS name space"));
6486 err = IE_ERROR;
6487 goto leave;
6489 xmlSetNs(request, isds_ns);
6491 /* Add XSD:tIdDbInput children */
6492 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6493 INSERT_STRING(request, box_id_element, box_id);
6494 err = insert_GExtApproval(context, approval, request);
6495 if (err) goto leave;
6497 /* Send request and check response*/
6498 err = send_destroy_request_check_response(context,
6499 service, service_name, &request, response, refnumber, NULL);
6501 leave:
6502 free(service_name_locale);
6503 free(box_id_locale);
6504 xmlFreeNode(request);
6505 return err;
6507 #endif /* HAVE_LIBCURL */
6510 /* Get data about all users assigned to given box.
6511 * @context is session context
6512 * @box_id is box ID
6513 * @users is automatically reallocated list of struct isds_DbUserInfo */
6514 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6515 struct isds_list **users) {
6516 isds_error err = IE_SUCCESS;
6517 #if HAVE_LIBCURL
6518 xmlDocPtr response = NULL;
6519 xmlXPathContextPtr xpath_ctx = NULL;
6520 xmlXPathObjectPtr result = NULL;
6521 int i;
6522 struct isds_list *item, *prev_item = NULL;
6523 #endif
6525 if (!context) return IE_INVALID_CONTEXT;
6526 zfree(context->long_message);
6527 if (!users || !box_id) return IE_INVAL;
6528 isds_list_free(users);
6531 #if HAVE_LIBCURL
6532 /* Do request and check for success */
6533 err = build_send_dbid_request_check_response(context,
6534 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6535 BAD_CAST box_id, NULL, &response, NULL);
6536 if (err) goto leave;
6539 /* Extract data */
6540 /* Prepare structure */
6541 xpath_ctx = xmlXPathNewContext(response);
6542 if (!xpath_ctx) {
6543 err = IE_ERROR;
6544 goto leave;
6546 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6547 err = IE_ERROR;
6548 goto leave;
6551 /* Set context node */
6552 result = xmlXPathEvalExpression(BAD_CAST
6553 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6554 xpath_ctx);
6555 if (!result) {
6556 err = IE_ERROR;
6557 goto leave;
6559 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6560 /* Iterate over all users */
6561 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6563 /* Prepare structure */
6564 item = calloc(1, sizeof(*item));
6565 if (!item) {
6566 err = IE_NOMEM;
6567 goto leave;
6569 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6570 if (i == 0) *users = item;
6571 else prev_item->next = item;
6572 prev_item = item;
6574 /* Extract it */
6575 xpath_ctx->node = result->nodesetval->nodeTab[i];
6576 err = extract_DbUserInfo(context,
6577 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6578 if (err) goto leave;
6582 leave:
6583 if (err) {
6584 isds_list_free(users);
6587 xmlXPathFreeObject(result);
6588 xmlXPathFreeContext(xpath_ctx);
6589 xmlFreeDoc(response);
6591 if (!err)
6592 isds_log(ILF_ISDS, ILL_DEBUG,
6593 _("GetDataBoxUsers request processed by server "
6594 "successfully.\n"));
6595 #else /* not HAVE_LIBCURL */
6596 err = IE_NOTSUP;
6597 #endif
6599 return err;
6603 /* Update data about user assigned to given box.
6604 * @context is session context
6605 * @box is box identification
6606 * @old_user identifies user to update
6607 * @new_user are updated data about @old_user
6608 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6609 * NULL, if you don't care.*/
6610 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6611 const struct isds_DbOwnerInfo *box,
6612 const struct isds_DbUserInfo *old_user,
6613 const struct isds_DbUserInfo *new_user,
6614 char **refnumber) {
6615 isds_error err = IE_SUCCESS;
6616 #if HAVE_LIBCURL
6617 xmlNsPtr isds_ns = NULL;
6618 xmlNodePtr request = NULL;
6619 xmlNodePtr node;
6620 #endif
6623 if (!context) return IE_INVALID_CONTEXT;
6624 zfree(context->long_message);
6625 if (!box || !old_user || !new_user) return IE_INVAL;
6628 #if HAVE_LIBCURL
6629 /* Build UpdateDataBoxUser request */
6630 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6631 if (!request) {
6632 isds_log_message(context,
6633 _("Could build UpdateDataBoxUser request"));
6634 return IE_ERROR;
6636 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6637 if(!isds_ns) {
6638 isds_log_message(context, _("Could not create ISDS name space"));
6639 xmlFreeNode(request);
6640 return IE_ERROR;
6642 xmlSetNs(request, isds_ns);
6644 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6645 err = insert_DbOwnerInfo(context, box, node);
6646 if (err) goto leave;
6648 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6649 err = insert_DbUserInfo(context, old_user, node);
6650 if (err) goto leave;
6652 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6653 err = insert_DbUserInfo(context, new_user, node);
6654 if (err) goto leave;
6656 /* Send it to server and process response */
6657 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6658 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6660 leave:
6661 xmlFreeNode(request);
6662 #else /* not HAVE_LIBCURL */
6663 err = IE_NOTSUP;
6664 #endif
6666 return err;
6670 /* Undocumented function.
6671 * @context is session context
6672 * @box_id is UTF-8 encoded box identifier
6673 * @token is UTF-8 encoded temporary password
6674 * @user_id outputs UTF-8 encoded reallocated user identifier
6675 * @password outpus UTF-8 encoded reallocated user password
6676 * Output arguments will be nulled in case of error */
6677 isds_error isds_activate(struct isds_ctx *context,
6678 const char *box_id, const char *token,
6679 char **user_id, char **password) {
6680 isds_error err = IE_SUCCESS;
6681 #if HAVE_LIBCURL
6682 xmlNsPtr isds_ns = NULL;
6683 xmlNodePtr request = NULL, node;
6684 xmlDocPtr response = NULL;
6685 xmlXPathContextPtr xpath_ctx = NULL;
6686 xmlXPathObjectPtr result = NULL;
6687 #endif
6690 if (!context) return IE_INVALID_CONTEXT;
6691 zfree(context->long_message);
6693 if (user_id) zfree(*user_id);
6694 if (password) zfree(*password);
6696 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6699 #if HAVE_LIBCURL
6700 /* Build Activate request */
6701 request = xmlNewNode(NULL, BAD_CAST "Activate");
6702 if (!request) {
6703 isds_log_message(context, _("Could build Activate request"));
6704 return IE_ERROR;
6706 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6707 if(!isds_ns) {
6708 isds_log_message(context, _("Could not create ISDS name space"));
6709 xmlFreeNode(request);
6710 return IE_ERROR;
6712 xmlSetNs(request, isds_ns);
6714 INSERT_STRING(request, "dbAccessDataId", token);
6715 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6716 INSERT_STRING(request, "dbID", box_id);
6719 /* Send request and check response*/
6720 err = send_destroy_request_check_response(context,
6721 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6722 &response, NULL, NULL);
6723 if (err) goto leave;
6726 /* Extract data */
6727 xpath_ctx = xmlXPathNewContext(response);
6728 if (!xpath_ctx) {
6729 err = IE_ERROR;
6730 goto leave;
6732 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6733 err = IE_ERROR;
6734 goto leave;
6736 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6737 xpath_ctx);
6738 if (!result) {
6739 err = IE_ERROR;
6740 goto leave;
6742 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6743 isds_log_message(context, _("Missing ActivateResponse element"));
6744 err = IE_ISDS;
6745 goto leave;
6747 if (result->nodesetval->nodeNr > 1) {
6748 isds_log_message(context, _("Multiple ActivateResponse element"));
6749 err = IE_ISDS;
6750 goto leave;
6752 xpath_ctx->node = result->nodesetval->nodeTab[0];
6753 xmlXPathFreeObject(result); result = NULL;
6755 EXTRACT_STRING("isds:userId", *user_id);
6756 if (!*user_id)
6757 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6758 "but did not return `userId' element.\n"));
6760 EXTRACT_STRING("isds:password", *password);
6761 if (!*password)
6762 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6763 "but did not return `password' element.\n"));
6765 leave:
6766 xmlXPathFreeObject(result);
6767 xmlXPathFreeContext(xpath_ctx);
6768 xmlFreeDoc(response);
6769 xmlFreeNode(request);
6771 if (!err)
6772 isds_log(ILF_ISDS, ILL_DEBUG,
6773 _("Activate request processed by server successfully.\n"));
6774 #else /* not HAVE_LIBCURL */
6775 err = IE_NOTSUP;
6776 #endif
6778 return err;
6782 /* Reset credentials of user assigned to given box.
6783 * @context is session context
6784 * @box is box identification
6785 * @user identifies user to reset password
6786 * @fee_paid is true if fee has been paid, false otherwise
6787 * @approval is optional external approval of box manipulation
6788 * @credentials_delivery is NULL if new password should be delivered off-line
6789 * to the user. It is valid pointer if user should obtain new password on-line
6790 * on dedicated web server. Then input @credentials_delivery.email value is
6791 * user's e-mail address user must provide to dedicated web server together
6792 * with @credentials_delivery.token. The output reallocated token user needs
6793 * to use to authorize on the web server to view his new password. Output
6794 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6795 * ISDS changed up on this call. (No reason why server could change the name
6796 * is known now.)
6797 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6798 * NULL, if you don't care.*/
6799 isds_error isds_reset_password(struct isds_ctx *context,
6800 const struct isds_DbOwnerInfo *box,
6801 const struct isds_DbUserInfo *user,
6802 const _Bool fee_paid, const struct isds_approval *approval,
6803 struct isds_credentials_delivery *credentials_delivery,
6804 char **refnumber) {
6805 isds_error err = IE_SUCCESS;
6806 #if HAVE_LIBCURL
6807 xmlNsPtr isds_ns = NULL;
6808 xmlNodePtr request = NULL, node;
6809 xmlDocPtr response = NULL;
6810 #endif
6813 if (!context) return IE_INVALID_CONTEXT;
6814 zfree(context->long_message);
6816 if (credentials_delivery) {
6817 zfree(credentials_delivery->token);
6818 zfree(credentials_delivery->new_user_name);
6820 if (!box || !user) return IE_INVAL;
6823 #if HAVE_LIBCURL
6824 /* Build NewAccessData request */
6825 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6826 if (!request) {
6827 isds_log_message(context,
6828 _("Could build NewAccessData request"));
6829 return IE_ERROR;
6831 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6832 if(!isds_ns) {
6833 isds_log_message(context, _("Could not create ISDS name space"));
6834 xmlFreeNode(request);
6835 return IE_ERROR;
6837 xmlSetNs(request, isds_ns);
6839 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6840 err = insert_DbOwnerInfo(context, box, node);
6841 if (err) goto leave;
6843 INSERT_ELEMENT(node, request, "dbUserInfo");
6844 err = insert_DbUserInfo(context, user, node);
6845 if (err) goto leave;
6847 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6849 err = insert_credentials_delivery(context, credentials_delivery, request);
6850 if (err) goto leave;
6852 err = insert_GExtApproval(context, approval, request);
6853 if (err) goto leave;
6855 /* Send request and check response*/
6856 err = send_destroy_request_check_response(context,
6857 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6858 &response, (xmlChar **) refnumber, NULL);
6859 if (err) goto leave;
6862 /* Extract optional token */
6863 err = extract_credentials_delivery(context, credentials_delivery,
6864 response, "NewAccessData");
6866 leave:
6867 xmlFreeDoc(response);
6868 xmlFreeNode(request);
6870 if (!err)
6871 isds_log(ILF_ISDS, ILL_DEBUG,
6872 _("NewAccessData request processed by server "
6873 "successfully.\n"));
6874 #else /* not HAVE_LIBCURL */
6875 err = IE_NOTSUP;
6876 #endif
6878 return err;
6882 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6883 * code, destroy response and log success.
6884 * @context is ISDS session context.
6885 * @service_name is name of SERVICE_DB_MANIPULATION service
6886 * @box is box identification
6887 * @user identifies user to remove
6888 * @credentials_delivery is NULL if new user's password should be delivered
6889 * off-line to the user. It is valid pointer if user should obtain new
6890 * password on-line on dedicated web server. Then input
6891 * @credentials_delivery.email value is user's e-mail address user must
6892 * provide to dedicated web server together with @credentials_delivery.token.
6893 * The output reallocated token user needs to use to authorize on the web
6894 * server to view his new password. Output reallocated
6895 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6896 * assingned or changed up on this call.
6897 * @approval is optional external approval of box manipulation
6898 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6899 * NULL, if you don't care. */
6900 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6901 struct isds_ctx *context, const xmlChar *service_name,
6902 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6903 struct isds_credentials_delivery *credentials_delivery,
6904 const struct isds_approval *approval, xmlChar **refnumber) {
6905 isds_error err = IE_SUCCESS;
6906 #if HAVE_LIBCURL
6907 xmlNsPtr isds_ns = NULL;
6908 xmlNodePtr request = NULL, node;
6909 xmlDocPtr response = NULL;
6910 #endif
6913 if (!context) return IE_INVALID_CONTEXT;
6914 zfree(context->long_message);
6915 if (credentials_delivery) {
6916 zfree(credentials_delivery->token);
6917 zfree(credentials_delivery->new_user_name);
6919 if (!service_name || service_name[0] == '\0' || !box || !user)
6920 return IE_INVAL;
6923 #if HAVE_LIBCURL
6924 /* Build NewAccessData or similar request */
6925 request = xmlNewNode(NULL, service_name);
6926 if (!request) {
6927 char *service_name_locale = _isds_utf82locale((char *) service_name);
6928 isds_printf_message(context, _("Could not build %s request"),
6929 service_name_locale);
6930 free(service_name_locale);
6931 return IE_ERROR;
6933 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6934 if(!isds_ns) {
6935 isds_log_message(context, _("Could not create ISDS name space"));
6936 xmlFreeNode(request);
6937 return IE_ERROR;
6939 xmlSetNs(request, isds_ns);
6941 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6942 err = insert_DbOwnerInfo(context, box, node);
6943 if (err) goto leave;
6945 INSERT_ELEMENT(node, request, "dbUserInfo");
6946 err = insert_DbUserInfo(context, user, node);
6947 if (err) goto leave;
6949 err = insert_credentials_delivery(context, credentials_delivery, request);
6950 if (err) goto leave;
6952 err = insert_GExtApproval(context, approval, request);
6953 if (err) goto leave;
6956 /* Send request and check response*/
6957 err = send_destroy_request_check_response(context,
6958 SERVICE_DB_MANIPULATION, service_name, &request, &response,
6959 refnumber, NULL);
6961 xmlFreeNode(request);
6962 request = NULL;
6964 /* Pick up credentials_delivery if requested */
6965 err = extract_credentials_delivery(context, credentials_delivery, response,
6966 (char *)service_name);
6968 leave:
6969 xmlFreeDoc(response);
6970 if (request) xmlFreeNode(request);
6972 if (!err) {
6973 char *service_name_locale = _isds_utf82locale((char *) service_name);
6974 isds_log(ILF_ISDS, ILL_DEBUG,
6975 _("%s request processed by server successfully.\n"),
6976 service_name_locale);
6977 free(service_name_locale);
6979 #else /* not HAVE_LIBCURL */
6980 err = IE_NOTSUP;
6981 #endif
6983 return err;
6987 /* Assign new user to given box.
6988 * @context is session context
6989 * @box is box identification
6990 * @user defines new user to add
6991 * @credentials_delivery is NULL if new user's password should be delivered
6992 * off-line to the user. It is valid pointer if user should obtain new
6993 * password on-line on dedicated web server. Then input
6994 * @credentials_delivery.email value is user's e-mail address user must
6995 * provide to dedicated web server together with @credentials_delivery.token.
6996 * The output reallocated token user needs to use to authorize on the web
6997 * server to view his new password. Output reallocated
6998 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6999 * assingned up on this call.
7000 * @approval is optional external approval of box manipulation
7001 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7002 * NULL, if you don't care.*/
7003 isds_error isds_add_user(struct isds_ctx *context,
7004 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7005 struct isds_credentials_delivery *credentials_delivery,
7006 const struct isds_approval *approval, char **refnumber) {
7007 return build_send_manipulationboxuser_request_check_drop_response(context,
7008 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
7009 approval, (xmlChar **) refnumber);
7013 /* Remove user assigned to given box.
7014 * @context is session context
7015 * @box is box identification
7016 * @user identifies user to remove
7017 * @approval is optional external approval of box manipulation
7018 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7019 * NULL, if you don't care.*/
7020 isds_error isds_delete_user(struct isds_ctx *context,
7021 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7022 const struct isds_approval *approval, char **refnumber) {
7023 return build_send_manipulationboxuser_request_check_drop_response(context,
7024 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
7025 (xmlChar **) refnumber);
7029 /* Get list of boxes in ZIP archive.
7030 * @context is session context
7031 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7032 * System recognizes following values currently: ALL (all boxes), UPG
7033 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
7034 * receiving commercial messages). This argument is a string because
7035 * specification states new values can appear in the future. Not all list
7036 * types are available to all users.
7037 * @buffer is automatically reallocated memory to store the list of boxes. The
7038 * list is zipped CSV file.
7039 * @buffer_length is size of @buffer data in bytes.
7040 * In case of error @buffer will be freed and @buffer_length will be
7041 * undefined.*/
7042 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7043 const char *list_identifier, void **buffer, size_t *buffer_length) {
7044 isds_error err = IE_SUCCESS;
7045 #if HAVE_LIBCURL
7046 xmlNsPtr isds_ns = NULL;
7047 xmlNodePtr request = NULL, node;
7048 xmlDocPtr response = NULL;
7049 xmlXPathContextPtr xpath_ctx = NULL;
7050 xmlXPathObjectPtr result = NULL;
7051 char *string = NULL;
7052 #endif
7055 if (!context) return IE_INVALID_CONTEXT;
7056 zfree(context->long_message);
7057 if (buffer) zfree(*buffer);
7058 if (!buffer || !buffer_length) return IE_INVAL;
7061 #if HAVE_LIBCURL
7062 /* Check if connection is established
7063 * TODO: This check should be done downstairs. */
7064 if (!context->curl) return IE_CONNECTION_CLOSED;
7067 /* Build AuthenticateMessage request */
7068 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7069 if (!request) {
7070 isds_log_message(context,
7071 _("Could not build GetDataBoxList request"));
7072 return IE_ERROR;
7074 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7075 if(!isds_ns) {
7076 isds_log_message(context, _("Could not create ISDS name space"));
7077 xmlFreeNode(request);
7078 return IE_ERROR;
7080 xmlSetNs(request, isds_ns);
7081 INSERT_STRING(request, "dblType", list_identifier);
7083 /* Send request to server and process response */
7084 err = send_destroy_request_check_response(context,
7085 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7086 &response, NULL, NULL);
7087 if (err) goto leave;
7090 /* Extract Base-64 encoded ZIP file */
7091 xpath_ctx = xmlXPathNewContext(response);
7092 if (!xpath_ctx) {
7093 err = IE_ERROR;
7094 goto leave;
7096 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7097 err = IE_ERROR;
7098 goto leave;
7100 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7102 /* Decode non-empty archive */
7103 if (string && string[0] != '\0') {
7104 *buffer_length = _isds_b64decode(string, buffer);
7105 if (*buffer_length == (size_t) -1) {
7106 isds_printf_message(context,
7107 _("Error while Base64-decoding box list archive"));
7108 err = IE_ERROR;
7109 goto leave;
7114 leave:
7115 free(string);
7116 xmlXPathFreeObject(result);
7117 xmlXPathFreeContext(xpath_ctx);
7118 xmlFreeDoc(response);
7119 xmlFreeNode(request);
7121 if (!err) {
7122 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7123 "processed by server successfully.\n"));
7125 #else /* not HAVE_LIBCURL */
7126 err = IE_NOTSUP;
7127 #endif
7129 return err;
7133 /* Find boxes suiting given criteria.
7134 * @criteria is filter. You should fill in at least some members.
7135 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7136 * possibly empty. Input NULL or valid old structure.
7137 * @return:
7138 * IE_SUCCESS if search succeeded, @boxes contains useful data
7139 * IE_NOEXIST if no such box exists, @boxes will be NULL
7140 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7141 * contains still valid data
7142 * other code if something bad happens. @boxes will be NULL. */
7143 isds_error isds_FindDataBox(struct isds_ctx *context,
7144 const struct isds_DbOwnerInfo *criteria,
7145 struct isds_list **boxes) {
7146 isds_error err = IE_SUCCESS;
7147 #if HAVE_LIBCURL
7148 _Bool truncated = 0;
7149 xmlNsPtr isds_ns = NULL;
7150 xmlNodePtr request = NULL;
7151 xmlDocPtr response = NULL;
7152 xmlChar *code = NULL, *message = NULL;
7153 xmlNodePtr db_owner_info;
7154 xmlXPathContextPtr xpath_ctx = NULL;
7155 xmlXPathObjectPtr result = NULL;
7156 xmlChar *string = NULL;
7157 #endif
7160 if (!context) return IE_INVALID_CONTEXT;
7161 zfree(context->long_message);
7162 if (!boxes) return IE_INVAL;
7163 isds_list_free(boxes);
7165 if (!criteria) {
7166 return IE_INVAL;
7169 #if HAVE_LIBCURL
7170 /* Check if connection is established
7171 * TODO: This check should be done downstairs. */
7172 if (!context->curl) return IE_CONNECTION_CLOSED;
7175 /* Build FindDataBox request */
7176 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
7177 if (!request) {
7178 isds_log_message(context,
7179 _("Could build FindDataBox request"));
7180 return IE_ERROR;
7182 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7183 if(!isds_ns) {
7184 isds_log_message(context, _("Could not create ISDS name space"));
7185 xmlFreeNode(request);
7186 return IE_ERROR;
7188 xmlSetNs(request, isds_ns);
7189 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7190 if (!db_owner_info) {
7191 isds_log_message(context, _("Could not add dbOwnerInfo child to "
7192 "FindDataBox element"));
7193 xmlFreeNode(request);
7194 return IE_ERROR;
7197 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
7198 if (err) goto leave;
7201 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
7203 /* Sent request */
7204 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7206 /* Destroy request */
7207 xmlFreeNode(request); request = NULL;
7209 if (err) {
7210 isds_log(ILF_ISDS, ILL_DEBUG,
7211 _("Processing ISDS response on FindDataBox "
7212 "request failed\n"));
7213 goto leave;
7216 /* Check for response status */
7217 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7218 &code, &message, NULL);
7219 if (err) {
7220 isds_log(ILF_ISDS, ILL_DEBUG,
7221 _("ISDS response on FindDataBox request is missing status\n"));
7222 goto leave;
7225 /* Request processed, but nothing found */
7226 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7227 !xmlStrcmp(code, BAD_CAST "5001")) {
7228 char *code_locale = _isds_utf82locale((char*)code);
7229 char *message_locale = _isds_utf82locale((char*)message);
7230 isds_log(ILF_ISDS, ILL_DEBUG,
7231 _("Server did not found any box on FindDataBox request "
7232 "(code=%s, message=%s)\n"), code_locale, message_locale);
7233 isds_log_message(context, message_locale);
7234 free(code_locale);
7235 free(message_locale);
7236 err = IE_NOEXIST;
7237 goto leave;
7240 /* Warning, not a error */
7241 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7242 char *code_locale = _isds_utf82locale((char*)code);
7243 char *message_locale = _isds_utf82locale((char*)message);
7244 isds_log(ILF_ISDS, ILL_DEBUG,
7245 _("Server truncated response on FindDataBox request "
7246 "(code=%s, message=%s)\n"), code_locale, message_locale);
7247 isds_log_message(context, message_locale);
7248 free(code_locale);
7249 free(message_locale);
7250 truncated = 1;
7253 /* Other error */
7254 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7255 char *code_locale = _isds_utf82locale((char*)code);
7256 char *message_locale = _isds_utf82locale((char*)message);
7257 isds_log(ILF_ISDS, ILL_DEBUG,
7258 _("Server refused FindDataBox request "
7259 "(code=%s, message=%s)\n"), code_locale, message_locale);
7260 isds_log_message(context, message_locale);
7261 free(code_locale);
7262 free(message_locale);
7263 err = IE_ISDS;
7264 goto leave;
7267 xpath_ctx = xmlXPathNewContext(response);
7268 if (!xpath_ctx) {
7269 err = IE_ERROR;
7270 goto leave;
7272 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7273 err = IE_ERROR;
7274 goto leave;
7277 /* Extract boxes if they present */
7278 result = xmlXPathEvalExpression(BAD_CAST
7279 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7280 xpath_ctx);
7281 if (!result) {
7282 err = IE_ERROR;
7283 goto leave;
7285 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7286 struct isds_list *item, *prev_item = NULL;
7287 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7288 item = calloc(1, sizeof(*item));
7289 if (!item) {
7290 err = IE_NOMEM;
7291 goto leave;
7294 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7295 if (i == 0) *boxes = item;
7296 else prev_item->next = item;
7297 prev_item = item;
7299 xpath_ctx->node = result->nodesetval->nodeTab[i];
7300 err = extract_DbOwnerInfo(context,
7301 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7302 if (err) goto leave;
7306 leave:
7307 if (err) {
7308 isds_list_free(boxes);
7309 } else {
7310 if (truncated) err = IE_2BIG;
7313 free(string);
7314 xmlFreeNode(request);
7315 xmlXPathFreeObject(result);
7316 xmlXPathFreeContext(xpath_ctx);
7318 free(code);
7319 free(message);
7320 xmlFreeDoc(response);
7322 if (!err)
7323 isds_log(ILF_ISDS, ILL_DEBUG,
7324 _("FindDataBox request processed by server successfully.\n"));
7325 #else /* not HAVE_LIBCURL */
7326 err = IE_NOTSUP;
7327 #endif
7329 return err;
7333 #if HAVE_LIBCURL
7334 /* Convert a string with match markers into a plain string with list of
7335 * pointers to the matches
7336 * @string is an UTF-8 encoded non-constant string with match markers
7337 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7338 * The markers will be removed from the string.
7339 * @starts is a reallocated list of static pointers into the @string pointing
7340 * to places where match start markers occured.
7341 * @ends is a reallocated list of static pointers into the @string pointing
7342 * to places where match end markers occured.
7343 * @return IE_SUCCESS in case of no failure. */
7344 static isds_error interpret_matches(xmlChar *string,
7345 struct isds_list **starts, struct isds_list **ends) {
7346 isds_error err = IE_SUCCESS;
7347 xmlChar *pointer, *destination, *source;
7348 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7350 isds_list_free(starts);
7351 isds_list_free(ends);
7352 if (NULL == starts || NULL == ends) return IE_INVAL;
7353 if (NULL == string) return IE_SUCCESS;
7355 for (pointer = string; *pointer != '\0';) {
7356 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7357 /* Remove the start marker */
7358 for (source = pointer + 14, destination = pointer;
7359 *source != '\0'; source++, destination++) {
7360 *destination = *source;
7362 *destination = '\0';
7363 /* Append the pointer into the list */
7364 item = calloc(1, sizeof(*item));
7365 if (!item) {
7366 err = IE_NOMEM;
7367 goto leave;
7369 item->destructor = (void (*)(void **))NULL;
7370 item->data = pointer;
7371 if (NULL == prev_start) *starts = item;
7372 else prev_start->next = item;
7373 prev_start = item;
7374 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7375 /* Remove the end marker */
7376 for (source = pointer + 12, destination = pointer;
7377 *source != '\0'; source++, destination++) {
7378 *destination = *source;
7380 *destination = '\0';
7381 /* Append the pointer into the list */
7382 item = calloc(1, sizeof(*item));
7383 if (!item) {
7384 err = IE_NOMEM;
7385 goto leave;
7387 item->destructor = (void (*)(void **))NULL;
7388 item->data = pointer;
7389 if (NULL == prev_end) *ends = item;
7390 else prev_end->next = item;
7391 prev_end = item;
7392 } else {
7393 pointer++;
7397 leave:
7398 if (err) {
7399 isds_list_free(starts);
7400 isds_list_free(ends);
7402 return err;
7406 /* Convert isds:dbResult XML tree into structure
7407 * @context is ISDS context.
7408 * @fulltext_result is automatically reallocated found box structure.
7409 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7410 * @collect_matches is true to interpret match markers.
7411 * In case of error @result will be freed. */
7412 static isds_error extract_dbResult(struct isds_ctx *context,
7413 struct isds_fulltext_result **fulltext_result,
7414 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7415 isds_error err = IE_SUCCESS;
7416 xmlXPathObjectPtr result = NULL;
7417 char *string = NULL;
7419 if (NULL == context) return IE_INVALID_CONTEXT;
7420 if (NULL == fulltext_result) return IE_INVAL;
7421 isds_fulltext_result_free(fulltext_result);
7422 if (!xpath_ctx) return IE_INVAL;
7425 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7426 if (NULL == *fulltext_result) {
7427 err = IE_NOMEM;
7428 goto leave;
7431 /* Extract data */
7432 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7434 EXTRACT_STRING("isds:dbType", string);
7435 if (NULL == string) {
7436 err = IE_ISDS;
7437 isds_log_message(context, _("Empty isds:dbType element"));
7438 goto leave;
7440 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7441 if (err) {
7442 if (err == IE_ENUM) {
7443 err = IE_ISDS;
7444 char *string_locale = _isds_utf82locale(string);
7445 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7446 string_locale);
7447 free(string_locale);
7449 goto leave;
7451 zfree(string);
7453 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7454 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7456 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7457 if (err) goto leave;
7459 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7460 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7461 (*fulltext_result)->dbEffectiveOVM);
7463 EXTRACT_STRING("isds:dbSendOptions", string);
7464 if (NULL == string) {
7465 err = IE_ISDS;
7466 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7467 goto leave;
7469 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7470 (*fulltext_result)->active = 1;
7471 (*fulltext_result)->public_sending = 1;
7472 (*fulltext_result)->commercial_sending = 0;
7473 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7474 (*fulltext_result)->active = 1;
7475 (*fulltext_result)->public_sending = 1;
7476 (*fulltext_result)->commercial_sending = 1;
7477 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7478 (*fulltext_result)->active = 1;
7479 (*fulltext_result)->public_sending = 0;
7480 (*fulltext_result)->commercial_sending = 0;
7481 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7482 (*fulltext_result)->active = 1;
7483 (*fulltext_result)->public_sending = 0;
7484 (*fulltext_result)->commercial_sending = 0;
7485 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7486 (*fulltext_result)->active = 0;
7487 (*fulltext_result)->public_sending = 0;
7488 (*fulltext_result)->commercial_sending = 0;
7489 } else {
7490 err = IE_ISDS;
7491 char *string_locale = _isds_utf82locale(string);
7492 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7493 string_locale);
7494 free(string_locale);
7495 goto leave;
7497 zfree(string);
7499 /* Interpret match marks */
7500 if (collect_matches) {
7501 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7502 &((*fulltext_result)->name_match_start),
7503 &((*fulltext_result)->name_match_end));
7504 if (err) goto leave;
7505 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7506 &((*fulltext_result)->address_match_start),
7507 &((*fulltext_result)->address_match_end));
7508 if (err) goto leave;
7511 leave:
7512 if (err) isds_fulltext_result_free(fulltext_result);
7513 free(string);
7514 xmlXPathFreeObject(result);
7515 return err;
7517 #endif /* HAVE_LIBCURL */
7520 /* Find boxes matching a given full-text criteria.
7521 * @context is a session context
7522 * @query is a non-empty string which consists of words to search
7523 * @target selects box attributes to search for @query words. Pass NULL if you
7524 * don't care.
7525 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7526 * to search in all box types. Pass NULL to let server to use default value
7527 * which is DBTYPE_SYSTEM.
7528 * @page_size defines count of boxes to constitute a response page. It counts
7529 * from zero. Pass NULL to let server to use a default value (50 now).
7530 * @page_number defines ordinar number of the response page to return. It
7531 * counts from zero. Pass NULL to let server to use a default value (0 now).
7532 * @track_matches points to true for marking @query words found in the box
7533 * attributes. It points to false for not marking. Pass NULL to let the server
7534 * to use default value (false now).
7535 * @total_matching_boxes outputs number of all boxes matching the query. Pass
7536 * NULL if you don't care.
7537 * @current_page_beginning outputs ordinar number of first box in this @boxes
7538 * page. It counts from zero. Pass NULL if you don't care.
7539 * @current_page_size outputs count of boxes in the this @boxes page. Pass
7540 * NULL if you don't care.
7541 * @last_page outputs true if this page is the last one, false otherwise. Pass
7542 * NULL if you don't care.
7543 * @boxes is automatically reallocated list of isds_fulltext_result structures,
7544 * possibly empty.
7545 * @return:
7546 * IE_SUCCESS if search succeeded, @boxes contains useful data
7547 * IE_2BIG if @page_size is too large
7548 * other code if something bad happens. @boxes will be NULL. */
7549 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7550 const char *query,
7551 const isds_fulltext_target *target,
7552 const isds_DbType *box_type,
7553 const unsigned long int *page_size,
7554 const unsigned long int *page_number,
7555 const _Bool *track_matches,
7556 unsigned long int *total_matching_boxes,
7557 unsigned long int *current_page_beginning,
7558 unsigned long int *current_page_size,
7559 _Bool *last_page,
7560 struct isds_list **boxes) {
7561 isds_error err = IE_SUCCESS;
7562 #if HAVE_LIBCURL
7563 xmlNsPtr isds_ns = NULL;
7564 xmlNodePtr request = NULL;
7565 xmlDocPtr response = NULL;
7566 xmlNodePtr node;
7567 xmlXPathContextPtr xpath_ctx = NULL;
7568 xmlXPathObjectPtr result = NULL;
7569 const xmlChar *static_string = NULL;
7570 xmlChar *string = NULL;
7572 const xmlChar *codes[] = {
7573 BAD_CAST "1004",
7574 BAD_CAST "1152",
7575 BAD_CAST "1153",
7576 BAD_CAST "1154",
7577 BAD_CAST "1155",
7578 BAD_CAST "1156",
7579 BAD_CAST "9002",
7580 NULL
7582 const char *meanings[] = {
7583 N_("You are not allowed to perform the search"),
7584 N_("The query string is empty"),
7585 N_("Searched box ID is malformed"),
7586 N_("Searched organization ID is malformed"),
7587 N_("Invalid input"),
7588 N_("Requested page size is too large"),
7589 N_("Search engine internal error")
7591 const isds_error errors[] = {
7592 IE_ISDS,
7593 IE_INVAL,
7594 IE_INVAL,
7595 IE_INVAL,
7596 IE_INVAL,
7597 IE_2BIG,
7598 IE_ISDS
7600 struct code_map_isds_error map = {
7601 .codes = codes,
7602 .meanings = meanings,
7603 .errors = errors
7605 #endif
7608 if (NULL == context) return IE_INVALID_CONTEXT;
7609 zfree(context->long_message);
7611 if (NULL == boxes) return IE_INVAL;
7612 isds_list_free(boxes);
7614 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
7615 isds_log_message(context, _("Query string must be non-empty"));
7616 return IE_INVAL;
7619 #if HAVE_LIBCURL
7620 /* Check if connection is established
7621 * TODO: This check should be done downstairs. */
7622 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7624 /* Build FindDataBox request */
7625 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
7626 if (NULL == request) {
7627 isds_log_message(context,
7628 _("Could build ISDSSearch2 request"));
7629 return IE_ERROR;
7631 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7632 if(NULL == isds_ns) {
7633 isds_log_message(context, _("Could not create ISDS name space"));
7634 xmlFreeNode(request);
7635 return IE_ERROR;
7637 xmlSetNs(request, isds_ns);
7639 INSERT_STRING(request, "searchText", query);
7641 if (NULL != target) {
7642 static_string = isds_fulltext_target2string(*(target));
7643 if (NULL == static_string) {
7644 isds_printf_message(context, _("Invalid target value: %d"),
7645 *(target));
7646 err = IE_ENUM;
7647 goto leave;
7650 INSERT_STRING(request, "searchType", static_string);
7651 static_string = NULL;
7653 if (NULL != box_type) {
7654 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7655 if (DBTYPE_SYSTEM == *box_type) {
7656 static_string = BAD_CAST "ALL";
7657 } else {
7658 static_string = isds_DbType2string(*(box_type));
7659 if (NULL == static_string) {
7660 isds_printf_message(context, _("Invalid box type value: %d"),
7661 *(box_type));
7662 err = IE_ENUM;
7663 goto leave;
7667 INSERT_STRING(request, "searchScope", static_string);
7668 static_string = NULL;
7670 INSERT_ULONGINT(request, "page", page_number, string);
7671 INSERT_ULONGINT(request, "pageSize", page_size, string);
7672 INSERT_BOOLEAN(request, "highlighting", track_matches);
7674 /* Send request and check response */
7675 err = send_destroy_request_check_response(context,
7676 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
7677 &request, &response, NULL, &map);
7678 if (err) goto leave;
7680 /* Parse response */
7681 xpath_ctx = xmlXPathNewContext(response);
7682 if (NULL == xpath_ctx) {
7683 err = IE_ERROR;
7684 goto leave;
7686 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7687 err = IE_ERROR;
7688 goto leave;
7690 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
7691 xpath_ctx);
7692 if (!result) {
7693 err = IE_ERROR;
7694 goto leave;
7696 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7697 isds_log_message(context, _("Missing ISDSSearch2 element"));
7698 err = IE_ISDS;
7699 goto leave;
7701 if (result->nodesetval->nodeNr > 1) {
7702 isds_log_message(context, _("Multiple ISDSSearch2 element"));
7703 err = IE_ISDS;
7704 goto leave;
7706 xpath_ctx->node = result->nodesetval->nodeTab[0];
7707 xmlXPathFreeObject(result); result = NULL;
7710 /* Extract counters */
7711 if (NULL != total_matching_boxes) {
7712 EXTRACT_ULONGINT("isds:totalCount", total_matching_boxes, 1);
7714 if (NULL != current_page_size) {
7715 EXTRACT_ULONGINT("isds:currentCount", current_page_size, 1);
7717 if (NULL != current_page_beginning) {
7718 EXTRACT_ULONGINT("isds:position", current_page_beginning, 1);
7720 if (NULL != last_page) {
7721 EXTRACT_BOOLEAN("isds:lastPage", last_page);
7724 /* Extract boxes if they present */
7725 result = xmlXPathEvalExpression(BAD_CAST
7726 "isds:dbResults/isds:dbResult", xpath_ctx);
7727 if (NULL == result) {
7728 err = IE_ERROR;
7729 goto leave;
7731 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7732 struct isds_list *item, *prev_item = NULL;
7733 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7734 item = calloc(1, sizeof(*item));
7735 if (!item) {
7736 err = IE_NOMEM;
7737 goto leave;
7740 item->destructor = (void (*)(void **))isds_fulltext_result_free;
7741 if (i == 0) *boxes = item;
7742 else prev_item->next = item;
7743 prev_item = item;
7745 xpath_ctx->node = result->nodesetval->nodeTab[i];
7746 err = extract_dbResult(context,
7747 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
7748 (NULL == track_matches) ? 0 : *track_matches);
7749 if (err) goto leave;
7753 leave:
7754 if (err) {
7755 isds_list_free(boxes);
7758 free(string);
7759 xmlFreeNode(request);
7760 xmlXPathFreeObject(result);
7761 xmlXPathFreeContext(xpath_ctx);
7762 xmlFreeDoc(response);
7764 if (!err)
7765 isds_log(ILF_ISDS, ILL_DEBUG,
7766 _("ISDSSearch2 request processed by server successfully.\n"));
7767 #else /* not HAVE_LIBCURL */
7768 err = IE_NOTSUP;
7769 #endif
7771 return err;
7775 /* Get status of a box.
7776 * @context is ISDS session context.
7777 * @box_id is UTF-8 encoded box identifier as zero terminated string
7778 * @box_status is return value of box status.
7779 * @return:
7780 * IE_SUCCESS if box has been found and its status retrieved
7781 * IE_NOEXIST if box is not known to ISDS server
7782 * or other appropriate error.
7783 * You can use isds_DbState to enumerate box status. However out of enum
7784 * range value can be returned too. This is feature because ISDS
7785 * specification leaves the set of values open.
7786 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7787 * the box has been deleted, but ISDS still lists its former existence. */
7788 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7789 long int *box_status) {
7790 isds_error err = IE_SUCCESS;
7791 #if HAVE_LIBCURL
7792 xmlNsPtr isds_ns = NULL;
7793 xmlNodePtr request = NULL, db_id;
7794 xmlDocPtr response = NULL;
7795 xmlXPathContextPtr xpath_ctx = NULL;
7796 xmlXPathObjectPtr result = NULL;
7797 xmlChar *string = NULL;
7799 const xmlChar *codes[] = {
7800 BAD_CAST "5001",
7801 BAD_CAST "1007",
7802 BAD_CAST "2011",
7803 NULL
7805 const char *meanings[] = {
7806 "The box does not exist",
7807 "Box ID is malformed",
7808 "Box ID malformed",
7810 const isds_error errors[] = {
7811 IE_NOEXIST,
7812 IE_INVAL,
7813 IE_INVAL,
7815 struct code_map_isds_error map = {
7816 .codes = codes,
7817 .meanings = meanings,
7818 .errors = errors
7820 #endif
7822 if (!context) return IE_INVALID_CONTEXT;
7823 zfree(context->long_message);
7824 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7826 #if HAVE_LIBCURL
7827 /* Check if connection is established
7828 * TODO: This check should be done downstairs. */
7829 if (!context->curl) return IE_CONNECTION_CLOSED;
7832 /* Build CheckDataBox request */
7833 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7834 if (!request) {
7835 isds_log_message(context,
7836 _("Could build CheckDataBox request"));
7837 return IE_ERROR;
7839 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7840 if(!isds_ns) {
7841 isds_log_message(context, _("Could not create ISDS name space"));
7842 xmlFreeNode(request);
7843 return IE_ERROR;
7845 xmlSetNs(request, isds_ns);
7846 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7847 if (!db_id) {
7848 isds_log_message(context, _("Could not add dbID child to "
7849 "CheckDataBox element"));
7850 xmlFreeNode(request);
7851 return IE_ERROR;
7855 /* Send request and check response*/
7856 err = send_destroy_request_check_response(context,
7857 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7858 &request, &response, NULL, &map);
7859 if (err) goto leave;
7862 /* Extract data */
7863 xpath_ctx = xmlXPathNewContext(response);
7864 if (!xpath_ctx) {
7865 err = IE_ERROR;
7866 goto leave;
7868 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7869 err = IE_ERROR;
7870 goto leave;
7872 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7873 xpath_ctx);
7874 if (!result) {
7875 err = IE_ERROR;
7876 goto leave;
7878 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7879 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7880 err = IE_ISDS;
7881 goto leave;
7883 if (result->nodesetval->nodeNr > 1) {
7884 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7885 err = IE_ISDS;
7886 goto leave;
7888 xpath_ctx->node = result->nodesetval->nodeTab[0];
7889 xmlXPathFreeObject(result); result = NULL;
7891 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7894 leave:
7895 free(string);
7896 xmlXPathFreeObject(result);
7897 xmlXPathFreeContext(xpath_ctx);
7899 xmlFreeDoc(response);
7901 if (!err)
7902 isds_log(ILF_ISDS, ILL_DEBUG,
7903 _("CheckDataBox request processed by server successfully.\n"));
7904 #else /* not HAVE_LIBCURL */
7905 err = IE_NOTSUP;
7906 #endif
7908 return err;
7912 /* Get list of permissions to send commercial messages.
7913 * @context is ISDS session context.
7914 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7915 * @permissions is a reallocated list of permissions (struct
7916 * isds_commercial_permission*) to send commercial messages from @box_id. The
7917 * order of permissions is significant as the server applies the permissions
7918 * and associated pre-paid credits in the order. Empty list means no
7919 * permission.
7920 * @return:
7921 * IE_SUCCESS if the list has been obtained correctly,
7922 * or other appropriate error. */
7923 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7924 const char *box_id, struct isds_list **permissions) {
7925 isds_error err = IE_SUCCESS;
7926 #if HAVE_LIBCURL
7927 xmlDocPtr response = NULL;
7928 xmlXPathContextPtr xpath_ctx = NULL;
7929 xmlXPathObjectPtr result = NULL;
7930 #endif
7932 if (!context) return IE_INVALID_CONTEXT;
7933 zfree(context->long_message);
7934 if (NULL == permissions) return IE_INVAL;
7935 isds_list_free(permissions);
7936 if (NULL == box_id) return IE_INVAL;
7938 #if HAVE_LIBCURL
7939 /* Check if connection is established */
7940 if (!context->curl) return IE_CONNECTION_CLOSED;
7942 /* Do request and check for success */
7943 err = build_send_dbid_request_check_response(context,
7944 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7945 BAD_CAST box_id, NULL, &response, NULL);
7946 if (!err) {
7947 isds_log(ILF_ISDS, ILL_DEBUG,
7948 _("PDZInfo request processed by server successfully.\n"));
7951 /* Extract data */
7952 /* Prepare structure */
7953 xpath_ctx = xmlXPathNewContext(response);
7954 if (!xpath_ctx) {
7955 err = IE_ERROR;
7956 goto leave;
7958 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7959 err = IE_ERROR;
7960 goto leave;
7963 /* Set context node */
7964 result = xmlXPathEvalExpression(BAD_CAST
7965 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7966 xpath_ctx);
7967 if (!result) {
7968 err = IE_ERROR;
7969 goto leave;
7971 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7972 struct isds_list *prev_item = NULL;
7974 /* Iterate over all permission records */
7975 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7976 struct isds_list *item;
7978 /* Prepare structure */
7979 item = calloc(1, sizeof(*item));
7980 if (!item) {
7981 err = IE_NOMEM;
7982 goto leave;
7984 item->destructor = (void(*)(void**))isds_commercial_permission_free;
7985 if (i == 0) *permissions = item;
7986 else prev_item->next = item;
7987 prev_item = item;
7989 /* Extract it */
7990 xpath_ctx->node = result->nodesetval->nodeTab[i];
7991 err = extract_DbPDZRecord(context,
7992 (struct isds_commercial_permission **) (&item->data),
7993 xpath_ctx);
7994 if (err) goto leave;
7998 leave:
7999 if (err) {
8000 isds_list_free(permissions);
8003 xmlXPathFreeObject(result);
8004 xmlXPathFreeContext(xpath_ctx);
8005 xmlFreeDoc(response);
8007 #else /* not HAVE_LIBCURL */
8008 err = IE_NOTSUP;
8009 #endif
8011 return err;
8015 /* Get details about credit for sending pre-paid commercial messages.
8016 * @context is ISDS session context.
8017 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8018 * @from_date is first day of credit history to return in @history. Only
8019 * tm_year, tm_mon and tm_mday carry sane value.
8020 * @to_date is last day of credit history to return in @history. Only
8021 * tm_year, tm_mon and tm_mday carry sane value.
8022 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8023 * if you don't care. This and all other credit values are integers in
8024 * hundredths of Czech Crowns.
8025 * @email outputs notification e-mail address where notifications about credit
8026 * are sent. This is automatically reallocated string. Pass NULL if you don't
8027 * care. It can return NULL if no address is defined.
8028 * @history outputs auto-reallocated list of pointers to struct
8029 * isds_credit_event. Events in closed interval @from_time to @to_time are
8030 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8031 * are sorted by time.
8032 * @return:
8033 * IE_SUCCESS if the credit details have been obtained correctly,
8034 * or other appropriate error. Please note that server allows to retrieve
8035 * only limited history of events. */
8036 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8037 const char *box_id,
8038 const struct tm *from_date, const struct tm *to_date,
8039 long int *credit, char **email, struct isds_list **history) {
8040 isds_error err = IE_SUCCESS;
8041 #if HAVE_LIBCURL
8042 char *box_id_locale = NULL;
8043 xmlNodePtr request = NULL, node;
8044 xmlNsPtr isds_ns = NULL;
8045 xmlChar *string = NULL;
8047 xmlDocPtr response = NULL;
8048 xmlXPathContextPtr xpath_ctx = NULL;
8049 xmlXPathObjectPtr result = NULL;
8051 const xmlChar *codes[] = {
8052 BAD_CAST "1004",
8053 BAD_CAST "2011",
8054 BAD_CAST "1093",
8055 BAD_CAST "1137",
8056 BAD_CAST "1058",
8057 NULL
8059 const char *meanings[] = {
8060 "Insufficient priviledges for the box",
8061 "The box does not exist",
8062 "Date is too long (history is not available after 15 months)",
8063 "Interval is too long (limit is 3 months)",
8064 "Invalid date"
8066 const isds_error errors[] = {
8067 IE_ISDS,
8068 IE_NOEXIST,
8069 IE_DATE,
8070 IE_DATE,
8071 IE_DATE,
8073 struct code_map_isds_error map = {
8074 .codes = codes,
8075 .meanings = meanings,
8076 .errors = errors
8078 #endif
8080 if (!context) return IE_INVALID_CONTEXT;
8081 zfree(context->long_message);
8083 /* Free output argument */
8084 if (NULL != credit) *credit = 0;
8085 if (NULL != email) zfree(*email);
8086 isds_list_free(history);
8088 if (NULL == box_id) return IE_INVAL;
8090 #if HAVE_LIBCURL
8091 /* Check if connection is established */
8092 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8094 box_id_locale = _isds_utf82locale((char*)box_id);
8095 if (NULL == box_id_locale) {
8096 err = IE_NOMEM;
8097 goto leave;
8100 /* Build request */
8101 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8102 if (NULL == request) {
8103 isds_printf_message(context,
8104 _("Could not build DataBoxCreditInfo request for %s box"),
8105 box_id_locale);
8106 err = IE_ERROR;
8107 goto leave;
8109 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8110 if(!isds_ns) {
8111 isds_log_message(context, _("Could not create ISDS name space"));
8112 err = IE_ERROR;
8113 goto leave;
8115 xmlSetNs(request, isds_ns);
8117 /* Add mandatory XSD:tIdDbInput child */
8118 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8119 /* Add mandatory dates elements with optional values */
8120 if (from_date) {
8121 err = tm2datestring(from_date, &string);
8122 if (err) {
8123 isds_log_message(context,
8124 _("Could not convert `from_date' argument to ISO date "
8125 "string"));
8126 goto leave;
8128 INSERT_STRING(request, "ciFromDate", string);
8129 zfree(string);
8130 } else {
8131 INSERT_STRING(request, "ciFromDate", NULL);
8133 if (to_date) {
8134 err = tm2datestring(to_date, &string);
8135 if (err) {
8136 isds_log_message(context,
8137 _("Could not convert `to_date' argument to ISO date "
8138 "string"));
8139 goto leave;
8141 INSERT_STRING(request, "ciTodate", string);
8142 zfree(string);
8143 } else {
8144 INSERT_STRING(request, "ciTodate", NULL);
8147 /* Send request and check response*/
8148 err = send_destroy_request_check_response(context,
8149 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8150 &request, &response, NULL, &map);
8151 if (err) goto leave;
8154 /* Extract data */
8155 /* Set context to the root */
8156 xpath_ctx = xmlXPathNewContext(response);
8157 if (!xpath_ctx) {
8158 err = IE_ERROR;
8159 goto leave;
8161 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8162 err = IE_ERROR;
8163 goto leave;
8165 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8166 xpath_ctx);
8167 if (!result) {
8168 err = IE_ERROR;
8169 goto leave;
8171 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8172 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8173 err = IE_ISDS;
8174 goto leave;
8176 if (result->nodesetval->nodeNr > 1) {
8177 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8178 err = IE_ISDS;
8179 goto leave;
8181 xpath_ctx->node = result->nodesetval->nodeTab[0];
8182 xmlXPathFreeObject(result); result = NULL;
8184 /* Extract common data */
8185 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8186 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8188 /* Extract records */
8189 if (NULL == history) goto leave;
8190 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8191 xpath_ctx);
8192 if (!result) {
8193 err = IE_ERROR;
8194 goto leave;
8196 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8197 struct isds_list *prev_item = NULL;
8199 /* Iterate over all records */
8200 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
8201 struct isds_list *item;
8203 /* Prepare structure */
8204 item = calloc(1, sizeof(*item));
8205 if (!item) {
8206 err = IE_NOMEM;
8207 goto leave;
8209 item->destructor = (void(*)(void**))isds_credit_event_free;
8210 if (i == 0) *history = item;
8211 else prev_item->next = item;
8212 prev_item = item;
8214 /* Extract it */
8215 xpath_ctx->node = result->nodesetval->nodeTab[i];
8216 err = extract_CiRecord(context,
8217 (struct isds_credit_event **) (&item->data),
8218 xpath_ctx);
8219 if (err) goto leave;
8223 leave:
8224 if (!err) {
8225 isds_log(ILF_ISDS, ILL_DEBUG,
8226 _("DataBoxCreditInfo request processed by server successfully.\n"));
8228 if (err) {
8229 isds_list_free(history);
8230 if (NULL != email) zfree(*email)
8233 free(box_id_locale);
8234 xmlXPathFreeObject(result);
8235 xmlXPathFreeContext(xpath_ctx);
8236 xmlFreeDoc(response);
8238 #else /* not HAVE_LIBCURL */
8239 err = IE_NOTSUP;
8240 #endif
8242 return err;
8246 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8247 * code, destroy response and log success.
8248 * @context is ISDS session context.
8249 * @service_name is name of SERVICE_DB_MANIPULATION service
8250 * @box_id is UTF-8 encoded box identifier as zero terminated string
8251 * @approval is optional external approval of box manipulation
8252 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8253 * NULL, if you don't care. */
8254 static isds_error build_send_manipulationdbid_request_check_drop_response(
8255 struct isds_ctx *context, const xmlChar *service_name,
8256 const xmlChar *box_id, const struct isds_approval *approval,
8257 xmlChar **refnumber) {
8258 isds_error err = IE_SUCCESS;
8259 #if HAVE_LIBCURL
8260 xmlDocPtr response = NULL;
8261 #endif
8263 if (!context) return IE_INVALID_CONTEXT;
8264 zfree(context->long_message);
8265 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8267 #if HAVE_LIBCURL
8268 /* Check if connection is established */
8269 if (!context->curl) return IE_CONNECTION_CLOSED;
8271 /* Do request and check for success */
8272 err = build_send_dbid_request_check_response(context,
8273 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8274 &response, refnumber);
8275 xmlFreeDoc(response);
8277 if (!err) {
8278 char *service_name_locale = _isds_utf82locale((char *) service_name);
8279 isds_log(ILF_ISDS, ILL_DEBUG,
8280 _("%s request processed by server successfully.\n"),
8281 service_name_locale);
8282 free(service_name_locale);
8284 #else /* not HAVE_LIBCURL */
8285 err = IE_NOTSUP;
8286 #endif
8288 return err;
8292 /* Switch box into state where box can receive commercial messages (off by
8293 * default)
8294 * @context is ISDS session context.
8295 * @box_id is UTF-8 encoded box identifier as zero terminated string
8296 * @allow is true for enable, false for disable commercial messages income
8297 * @approval is optional external approval of box manipulation
8298 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8299 * NULL, if you don't care. */
8300 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8301 const char *box_id, const _Bool allow,
8302 const struct isds_approval *approval, char **refnumber) {
8303 return build_send_manipulationdbid_request_check_drop_response(context,
8304 (allow) ? BAD_CAST "SetOpenAddressing" :
8305 BAD_CAST "ClearOpenAddressing",
8306 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8310 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8311 * message acceptance). This is just a box permission. Sender must apply
8312 * such role by sending each message.
8313 * @context is ISDS session context.
8314 * @box_id is UTF-8 encoded box identifier as zero terminated string
8315 * @allow is true for enable, false for disable OVM role permission
8316 * @approval is optional external approval of box manipulation
8317 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8318 * NULL, if you don't care. */
8319 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
8320 const char *box_id, const _Bool allow,
8321 const struct isds_approval *approval, char **refnumber) {
8322 return build_send_manipulationdbid_request_check_drop_response(context,
8323 (allow) ? BAD_CAST "SetEffectiveOVM" :
8324 BAD_CAST "ClearEffectiveOVM",
8325 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8329 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8330 * code, destroy response and log success.
8331 * @context is ISDS session context.
8332 * @service_name is name of SERVICE_DB_MANIPULATION service
8333 * @owner is structure describing box
8334 * @approval is optional external approval of box manipulation
8335 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8336 * NULL, if you don't care. */
8337 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8338 struct isds_ctx *context, const xmlChar *service_name,
8339 const struct isds_DbOwnerInfo *owner,
8340 const struct isds_approval *approval, xmlChar **refnumber) {
8341 isds_error err = IE_SUCCESS;
8342 #if HAVE_LIBCURL
8343 char *service_name_locale = NULL;
8344 xmlNodePtr request = NULL, db_owner_info;
8345 xmlNsPtr isds_ns = NULL;
8346 #endif
8349 if (!context) return IE_INVALID_CONTEXT;
8350 zfree(context->long_message);
8351 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8353 #if HAVE_LIBCURL
8354 service_name_locale = _isds_utf82locale((char*)service_name);
8355 if (!service_name_locale) {
8356 err = IE_NOMEM;
8357 goto leave;
8360 /* Build request */
8361 request = xmlNewNode(NULL, service_name);
8362 if (!request) {
8363 isds_printf_message(context,
8364 _("Could not build %s request"), service_name_locale);
8365 err = IE_ERROR;
8366 goto leave;
8368 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8369 if(!isds_ns) {
8370 isds_log_message(context, _("Could not create ISDS name space"));
8371 err = IE_ERROR;
8372 goto leave;
8374 xmlSetNs(request, isds_ns);
8377 /* Add XSD:tOwnerInfoInput child*/
8378 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8379 err = insert_DbOwnerInfo(context, owner, db_owner_info);
8380 if (err) goto leave;
8382 /* Add XSD:gExtApproval*/
8383 err = insert_GExtApproval(context, approval, request);
8384 if (err) goto leave;
8386 /* Send it to server and process response */
8387 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8388 service_name, &request, refnumber);
8390 leave:
8391 xmlFreeNode(request);
8392 free(service_name_locale);
8393 #else /* not HAVE_LIBCURL */
8394 err = IE_NOTSUP;
8395 #endif
8397 return err;
8401 /* Switch box accessibility state on request of box owner.
8402 * Despite the name, owner must do the request off-line. This function is
8403 * designed for such off-line meeting points (e.g. Czech POINT).
8404 * @context is ISDS session context.
8405 * @box identifies box to switch accessibility state.
8406 * @allow is true for making accessible, false to disallow access.
8407 * @approval is optional external approval of box manipulation
8408 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8409 * NULL, if you don't care. */
8410 isds_error isds_switch_box_accessibility_on_owner_request(
8411 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8412 const _Bool allow, const struct isds_approval *approval,
8413 char **refnumber) {
8414 return build_send_manipulationdbowner_request_check_drop_response(context,
8415 (allow) ? BAD_CAST "EnableOwnDataBox" :
8416 BAD_CAST "DisableOwnDataBox",
8417 box, approval, (xmlChar **) refnumber);
8421 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8422 * date.
8423 * @context is ISDS session context.
8424 * @box identifies box to switch accessibility state.
8425 * @since is date since accessibility has been denied. This can be past too.
8426 * Only tm_year, tm_mon and tm_mday carry sane value.
8427 * @approval is optional external approval of box manipulation
8428 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8429 * NULL, if you don't care. */
8430 isds_error isds_disable_box_accessibility_externaly(
8431 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8432 const struct tm *since, const struct isds_approval *approval,
8433 char **refnumber) {
8434 isds_error err = IE_SUCCESS;
8435 #if HAVE_LIBCURL
8436 char *service_name_locale = NULL;
8437 xmlNodePtr request = NULL, node;
8438 xmlNsPtr isds_ns = NULL;
8439 xmlChar *string = NULL;
8440 #endif
8443 if (!context) return IE_INVALID_CONTEXT;
8444 zfree(context->long_message);
8445 if (!box || !since) return IE_INVAL;
8447 #if HAVE_LIBCURL
8448 /* Build request */
8449 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
8450 if (!request) {
8451 isds_printf_message(context,
8452 _("Could not build %s request"), "DisableDataBoxExternally");
8453 err = IE_ERROR;
8454 goto leave;
8456 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8457 if(!isds_ns) {
8458 isds_log_message(context, _("Could not create ISDS name space"));
8459 err = IE_ERROR;
8460 goto leave;
8462 xmlSetNs(request, isds_ns);
8465 /* Add @box identification */
8466 INSERT_ELEMENT(node, request, "dbOwnerInfo");
8467 err = insert_DbOwnerInfo(context, box, node);
8468 if (err) goto leave;
8470 /* Add @since date */
8471 err = tm2datestring(since, &string);
8472 if(err) {
8473 isds_log_message(context,
8474 _("Could not convert `since' argument to ISO date string"));
8475 goto leave;
8477 INSERT_STRING(request, "dbOwnerDisableDate", string);
8478 zfree(string);
8480 /* Add @approval */
8481 err = insert_GExtApproval(context, approval, request);
8482 if (err) goto leave;
8484 /* Send it to server and process response */
8485 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8486 BAD_CAST "DisableDataBoxExternally", &request,
8487 (xmlChar **) refnumber);
8489 leave:
8490 free(string);
8491 xmlFreeNode(request);
8492 free(service_name_locale);
8493 #else /* not HAVE_LIBCURL */
8494 err = IE_NOTSUP;
8495 #endif
8497 return err;
8501 #if HAVE_LIBCURL
8502 /* Insert struct isds_message data (envelope (recipient data optional) and
8503 * documents into XML tree
8504 * @context is session context
8505 * @outgoing_message is libisds structure with message data
8506 * @create_message is XML CreateMessage or CreateMultipleMessage element
8507 * @process_recipient true for recipient data serialization, false for no
8508 * serialization */
8509 static isds_error insert_envelope_files(struct isds_ctx *context,
8510 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8511 const _Bool process_recipient) {
8513 isds_error err = IE_SUCCESS;
8514 xmlNodePtr envelope, dm_files, node;
8515 xmlChar *string = NULL;
8517 if (!context) return IE_INVALID_CONTEXT;
8518 if (!outgoing_message || !create_message) return IE_INVAL;
8521 /* Build envelope */
8522 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8523 if (!envelope) {
8524 isds_printf_message(context, _("Could not add dmEnvelope child to "
8525 "%s element"), create_message->name);
8526 return IE_ERROR;
8529 if (!outgoing_message->envelope) {
8530 isds_log_message(context, _("Outgoing message is missing envelope"));
8531 err = IE_INVAL;
8532 goto leave;
8535 /* Insert optional message type */
8536 err = insert_message_type(context, outgoing_message->envelope->dmType,
8537 envelope);
8538 if (err) goto leave;
8540 INSERT_STRING(envelope, "dmSenderOrgUnit",
8541 outgoing_message->envelope->dmSenderOrgUnit);
8542 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8543 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8545 if (process_recipient) {
8546 if (!outgoing_message->envelope->dbIDRecipient) {
8547 isds_log_message(context,
8548 _("Outgoing message is missing recipient box identifier"));
8549 err = IE_INVAL;
8550 goto leave;
8552 INSERT_STRING(envelope, "dbIDRecipient",
8553 outgoing_message->envelope->dbIDRecipient);
8555 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8556 outgoing_message->envelope->dmRecipientOrgUnit);
8557 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8558 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8559 INSERT_STRING(envelope, "dmToHands",
8560 outgoing_message->envelope->dmToHands);
8563 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8564 "dmAnnotation");
8565 INSERT_STRING(envelope, "dmAnnotation",
8566 outgoing_message->envelope->dmAnnotation);
8568 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8569 0, 50, "dmRecipientRefNumber");
8570 INSERT_STRING(envelope, "dmRecipientRefNumber",
8571 outgoing_message->envelope->dmRecipientRefNumber);
8573 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8574 0, 50, "dmSenderRefNumber");
8575 INSERT_STRING(envelope, "dmSenderRefNumber",
8576 outgoing_message->envelope->dmSenderRefNumber);
8578 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8579 0, 50, "dmRecipientIdent");
8580 INSERT_STRING(envelope, "dmRecipientIdent",
8581 outgoing_message->envelope->dmRecipientIdent);
8583 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8584 0, 50, "dmSenderIdent");
8585 INSERT_STRING(envelope, "dmSenderIdent",
8586 outgoing_message->envelope->dmSenderIdent);
8588 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8589 outgoing_message->envelope->dmLegalTitleLaw, string);
8590 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8591 outgoing_message->envelope->dmLegalTitleYear, string);
8592 INSERT_STRING(envelope, "dmLegalTitleSect",
8593 outgoing_message->envelope->dmLegalTitleSect);
8594 INSERT_STRING(envelope, "dmLegalTitlePar",
8595 outgoing_message->envelope->dmLegalTitlePar);
8596 INSERT_STRING(envelope, "dmLegalTitlePoint",
8597 outgoing_message->envelope->dmLegalTitlePoint);
8599 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8600 outgoing_message->envelope->dmPersonalDelivery);
8601 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8602 outgoing_message->envelope->dmAllowSubstDelivery);
8604 /* ???: Should we require value for dbEffectiveOVM sender?
8605 * ISDS has default as true */
8606 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8607 INSERT_BOOLEAN(envelope, "dmOVM",
8608 outgoing_message->envelope->dmPublishOwnID);
8611 /* Append dmFiles */
8612 if (!outgoing_message->documents) {
8613 isds_log_message(context,
8614 _("Outgoing message is missing list of documents"));
8615 err = IE_INVAL;
8616 goto leave;
8618 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8619 if (!dm_files) {
8620 isds_printf_message(context, _("Could not add dmFiles child to "
8621 "%s element"), create_message->name);
8622 err = IE_ERROR;
8623 goto leave;
8626 /* Check for document hierarchy */
8627 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8628 if (err) goto leave;
8630 /* Process each document */
8631 for (struct isds_list *item =
8632 (struct isds_list *) outgoing_message->documents;
8633 item; item = item->next) {
8634 if (!item->data) {
8635 isds_log_message(context,
8636 _("List of documents contains empty item"));
8637 err = IE_INVAL;
8638 goto leave;
8640 /* FIXME: Check for dmFileMetaType and for document references.
8641 * Only first document can be of MAIN type */
8642 err = insert_document(context, (struct isds_document*) item->data,
8643 dm_files);
8645 if (err) goto leave;
8648 leave:
8649 free(string);
8650 return err;
8652 #endif /* HAVE_LIBCURL */
8655 /* Send a message via ISDS to a recipient
8656 * @context is session context
8657 * @outgoing_message is message to send; Some members are mandatory (like
8658 * dbIDRecipient), some are optional and some are irrelevant (especially data
8659 * about sender). Included pointer to isds_list documents must contain at
8660 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8661 * members will be filled with valid data from ISDS. Exact list of write
8662 * members is subject to change. Currently dmID is changed.
8663 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8664 isds_error isds_send_message(struct isds_ctx *context,
8665 struct isds_message *outgoing_message) {
8667 isds_error err = IE_SUCCESS;
8668 #if HAVE_LIBCURL
8669 xmlNsPtr isds_ns = NULL;
8670 xmlNodePtr request = NULL;
8671 xmlDocPtr response = NULL;
8672 xmlChar *code = NULL, *message = NULL;
8673 xmlXPathContextPtr xpath_ctx = NULL;
8674 xmlXPathObjectPtr result = NULL;
8675 /*_Bool message_is_complete = 0;*/
8676 #endif
8678 if (!context) return IE_INVALID_CONTEXT;
8679 zfree(context->long_message);
8680 if (!outgoing_message) return IE_INVAL;
8682 #if HAVE_LIBCURL
8683 /* Check if connection is established
8684 * TODO: This check should be done downstairs. */
8685 if (!context->curl) return IE_CONNECTION_CLOSED;
8688 /* Build CreateMessage request */
8689 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8690 if (!request) {
8691 isds_log_message(context,
8692 _("Could not build CreateMessage request"));
8693 return IE_ERROR;
8695 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8696 if(!isds_ns) {
8697 isds_log_message(context, _("Could not create ISDS name space"));
8698 xmlFreeNode(request);
8699 return IE_ERROR;
8701 xmlSetNs(request, isds_ns);
8703 /* Append envelope and files */
8704 err = insert_envelope_files(context, outgoing_message, request, 1);
8705 if (err) goto leave;
8708 /* Signal we can serialize message since now */
8709 /*message_is_complete = 1;*/
8712 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8714 /* Sent request */
8715 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8717 /* Don't' destroy request, we want to provide it to application later */
8719 if (err) {
8720 isds_log(ILF_ISDS, ILL_DEBUG,
8721 _("Processing ISDS response on CreateMessage "
8722 "request failed\n"));
8723 goto leave;
8726 /* Check for response status */
8727 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8728 &code, &message, NULL);
8729 if (err) {
8730 isds_log(ILF_ISDS, ILL_DEBUG,
8731 _("ISDS response on CreateMessage request "
8732 "is missing status\n"));
8733 goto leave;
8736 /* Request processed, but refused by server or server failed */
8737 if (xmlStrcmp(code, BAD_CAST "0000")) {
8738 char *box_id_locale =
8739 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8740 char *code_locale = _isds_utf82locale((char*)code);
8741 char *message_locale = _isds_utf82locale((char*)message);
8742 isds_log(ILF_ISDS, ILL_DEBUG,
8743 _("Server did not accept message for %s on CreateMessage "
8744 "request (code=%s, message=%s)\n"),
8745 box_id_locale, code_locale, message_locale);
8746 isds_log_message(context, message_locale);
8747 free(box_id_locale);
8748 free(code_locale);
8749 free(message_locale);
8750 err = IE_ISDS;
8751 goto leave;
8755 /* Extract data */
8756 xpath_ctx = xmlXPathNewContext(response);
8757 if (!xpath_ctx) {
8758 err = IE_ERROR;
8759 goto leave;
8761 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8762 err = IE_ERROR;
8763 goto leave;
8765 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8766 xpath_ctx);
8767 if (!result) {
8768 err = IE_ERROR;
8769 goto leave;
8771 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8772 isds_log_message(context, _("Missing CreateMessageResponse element"));
8773 err = IE_ISDS;
8774 goto leave;
8776 if (result->nodesetval->nodeNr > 1) {
8777 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8778 err = IE_ISDS;
8779 goto leave;
8781 xpath_ctx->node = result->nodesetval->nodeTab[0];
8782 xmlXPathFreeObject(result); result = NULL;
8784 if (outgoing_message->envelope->dmID) {
8785 free(outgoing_message->envelope->dmID);
8786 outgoing_message->envelope->dmID = NULL;
8788 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8789 if (!outgoing_message->envelope->dmID) {
8790 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8791 "but did not return assigned message ID\n"));
8794 leave:
8795 /* TODO: Serialize message into structure member raw */
8796 /* XXX: Each web service transport message in different format.
8797 * Therefore it's not possible to save them directly.
8798 * To save them, one must figure out common format.
8799 * We can leave it on application, or we can implement the ESS format. */
8800 /*if (message_is_complete) {
8801 if (outgoing_message->envelope->dmID) {
8803 /* Add assigned message ID as first child*/
8804 /*xmlNodePtr dmid_text = xmlNewText(
8805 (xmlChar *) outgoing_message->envelope->dmID);
8806 if (!dmid_text) goto serialization_failed;
8808 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8809 BAD_CAST "dmID");
8810 if (!dmid_element) {
8811 xmlFreeNode(dmid_text);
8812 goto serialization_failed;
8815 xmlNodePtr dmid_element_with_text =
8816 xmlAddChild(dmid_element, dmid_text);
8817 if (!dmid_element_with_text) {
8818 xmlFreeNode(dmid_element);
8819 xmlFreeNode(dmid_text);
8820 goto serialization_failed;
8823 node = xmlAddPrevSibling(envelope->childern,
8824 dmid_element_with_text);
8825 if (!node) {
8826 xmlFreeNodeList(dmid_element_with_text);
8827 goto serialization_failed;
8831 /* Serialize message with ID into raw */
8832 /*buffer = serialize_element(envelope)*/
8833 /* }
8835 serialization_failed:
8839 /* Clean up */
8840 xmlXPathFreeObject(result);
8841 xmlXPathFreeContext(xpath_ctx);
8843 free(code);
8844 free(message);
8845 xmlFreeDoc(response);
8846 xmlFreeNode(request);
8848 if (!err)
8849 isds_log(ILF_ISDS, ILL_DEBUG,
8850 _("CreateMessage request processed by server "
8851 "successfully.\n"));
8852 #else /* not HAVE_LIBCURL */
8853 err = IE_NOTSUP;
8854 #endif
8856 return err;
8860 /* Send a message via ISDS to a multiple recipients
8861 * @context is session context
8862 * @outgoing_message is message to send; Some members are mandatory,
8863 * some are optional and some are irrelevant (especially data
8864 * about sender). Data about recipient will be substituted by ISDS from
8865 * @copies. Included pointer to isds_list documents must
8866 * contain at least one document of FILEMETATYPE_MAIN.
8867 * @copies is list of isds_message_copy structures addressing all desired
8868 * recipients. This is read-write structure, some members will be filled with
8869 * valid data from ISDS (message IDs, error codes, error descriptions).
8870 * @return
8871 * ISDS_SUCCESS if all messages have been sent
8872 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8873 * succeeded messages can be identified by copies->data->error),
8874 * or other error code if something other goes wrong. */
8875 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8876 const struct isds_message *outgoing_message,
8877 struct isds_list *copies) {
8879 isds_error err = IE_SUCCESS;
8880 #if HAVE_LIBCURL
8881 isds_error append_err;
8882 xmlNsPtr isds_ns = NULL;
8883 xmlNodePtr request = NULL, recipients, recipient, node;
8884 struct isds_list *item;
8885 struct isds_message_copy *copy;
8886 xmlDocPtr response = NULL;
8887 xmlChar *code = NULL, *message = NULL;
8888 xmlXPathContextPtr xpath_ctx = NULL;
8889 xmlXPathObjectPtr result = NULL;
8890 xmlChar *string = NULL;
8891 int i;
8892 #endif
8894 if (!context) return IE_INVALID_CONTEXT;
8895 zfree(context->long_message);
8896 if (!outgoing_message || !copies) return IE_INVAL;
8898 #if HAVE_LIBCURL
8899 /* Check if connection is established
8900 * TODO: This check should be done downstairs. */
8901 if (!context->curl) return IE_CONNECTION_CLOSED;
8904 /* Build CreateMultipleMessage request */
8905 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
8906 if (!request) {
8907 isds_log_message(context,
8908 _("Could not build CreateMultipleMessage request"));
8909 return IE_ERROR;
8911 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8912 if(!isds_ns) {
8913 isds_log_message(context, _("Could not create ISDS name space"));
8914 xmlFreeNode(request);
8915 return IE_ERROR;
8917 xmlSetNs(request, isds_ns);
8920 /* Build recipients */
8921 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8922 if (!recipients) {
8923 isds_log_message(context, _("Could not add dmRecipients child to "
8924 "CreateMultipleMessage element"));
8925 xmlFreeNode(request);
8926 return IE_ERROR;
8929 /* Insert each recipient */
8930 for (item = copies; item; item = item->next) {
8931 copy = (struct isds_message_copy *) item->data;
8932 if (!copy) {
8933 isds_log_message(context,
8934 _("`copies' list item contains empty data"));
8935 err = IE_INVAL;
8936 goto leave;
8939 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
8940 if (!recipient) {
8941 isds_log_message(context, _("Could not add dmRecipient child to "
8942 "dmRecipients element"));
8943 err = IE_ERROR;
8944 goto leave;
8947 if (!copy->dbIDRecipient) {
8948 isds_log_message(context,
8949 _("Message copy is missing recipient box identifier"));
8950 err = IE_INVAL;
8951 goto leave;
8953 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
8954 INSERT_STRING(recipient, "dmRecipientOrgUnit",
8955 copy->dmRecipientOrgUnit);
8956 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
8957 copy->dmRecipientOrgUnitNum, string);
8958 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
8961 /* Append envelope and files */
8962 err = insert_envelope_files(context, outgoing_message, request, 0);
8963 if (err) goto leave;
8966 isds_log(ILF_ISDS, ILL_DEBUG,
8967 _("Sending CreateMultipleMessage request to ISDS\n"));
8969 /* Sent request */
8970 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8971 if (err) {
8972 isds_log(ILF_ISDS, ILL_DEBUG,
8973 _("Processing ISDS response on CreateMultipleMessage "
8974 "request failed\n"));
8975 goto leave;
8978 /* Check for response status */
8979 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8980 &code, &message, NULL);
8981 if (err) {
8982 isds_log(ILF_ISDS, ILL_DEBUG,
8983 _("ISDS response on CreateMultipleMessage request "
8984 "is missing status\n"));
8985 goto leave;
8988 /* Request processed, but some copies failed */
8989 if (!xmlStrcmp(code, BAD_CAST "0004")) {
8990 char *box_id_locale =
8991 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8992 char *code_locale = _isds_utf82locale((char*)code);
8993 char *message_locale = _isds_utf82locale((char*)message);
8994 isds_log(ILF_ISDS, ILL_DEBUG,
8995 _("Server did accept message for multiple recipients "
8996 "on CreateMultipleMessage request but delivery to "
8997 "some of them failed (code=%s, message=%s)\n"),
8998 box_id_locale, code_locale, message_locale);
8999 isds_log_message(context, message_locale);
9000 free(box_id_locale);
9001 free(code_locale);
9002 free(message_locale);
9003 err = IE_PARTIAL_SUCCESS;
9006 /* Request refused by server as whole */
9007 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9008 char *box_id_locale =
9009 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9010 char *code_locale = _isds_utf82locale((char*)code);
9011 char *message_locale = _isds_utf82locale((char*)message);
9012 isds_log(ILF_ISDS, ILL_DEBUG,
9013 _("Server did not accept message for multiple recipients "
9014 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9015 box_id_locale, code_locale, message_locale);
9016 isds_log_message(context, message_locale);
9017 free(box_id_locale);
9018 free(code_locale);
9019 free(message_locale);
9020 err = IE_ISDS;
9021 goto leave;
9025 /* Extract data */
9026 xpath_ctx = xmlXPathNewContext(response);
9027 if (!xpath_ctx) {
9028 err = IE_ERROR;
9029 goto leave;
9031 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9032 err = IE_ERROR;
9033 goto leave;
9035 result = xmlXPathEvalExpression(
9036 BAD_CAST "/isds:CreateMultipleMessageResponse"
9037 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9038 xpath_ctx);
9039 if (!result) {
9040 err = IE_ERROR;
9041 goto leave;
9043 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9044 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9045 err = IE_ISDS;
9046 goto leave;
9049 /* Extract message ID and delivery status for each copy */
9050 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9051 item = item->next, i++) {
9052 copy = (struct isds_message_copy *) item->data;
9053 xpath_ctx->node = result->nodesetval->nodeTab[i];
9055 append_err = append_TMStatus(context, copy, xpath_ctx);
9056 if (append_err) {
9057 err = append_err;
9058 goto leave;
9061 if (item || i < result->nodesetval->nodeNr) {
9062 isds_printf_message(context, _("ISDS returned unexpected number of "
9063 "message copy delivery states: %d"),
9064 result->nodesetval->nodeNr);
9065 err = IE_ISDS;
9066 goto leave;
9070 leave:
9071 /* Clean up */
9072 free(string);
9073 xmlXPathFreeObject(result);
9074 xmlXPathFreeContext(xpath_ctx);
9076 free(code);
9077 free(message);
9078 xmlFreeDoc(response);
9079 xmlFreeNode(request);
9081 if (!err)
9082 isds_log(ILF_ISDS, ILL_DEBUG,
9083 _("CreateMultipleMessageResponse request processed by server "
9084 "successfully.\n"));
9085 #else /* not HAVE_LIBCURL */
9086 err = IE_NOTSUP;
9087 #endif
9089 return err;
9093 /* Get list of messages. This is common core for getting sent or received
9094 * messages.
9095 * Any criterion argument can be NULL, if you don't care about it.
9096 * @context is session context. Must not be NULL.
9097 * @outgoing_direction is true if you want list of outgoing messages,
9098 * it's false if you want incoming messages.
9099 * @from_time is minimal time and date of message sending inclusive.
9100 * @to_time is maximal time and date of message sending inclusive
9101 * @organization_unit_number is number of sender/recipient respectively.
9102 * @status_filter is bit field of isds_message_status values. Use special
9103 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9104 * all values, you can use bit-wise arithmetic if you want.)
9105 * @offset is index of first message we are interested in. First message is 1.
9106 * Set to 0 (or 1) if you don't care.
9107 * @number is maximal length of list you want to get as input value, outputs
9108 * number of messages matching these criteria. Can be NULL if you don't care
9109 * (applies to output value either).
9110 * @messages is automatically reallocated list of isds_message's. Be ware that
9111 * it returns only brief overview (envelope and some other fields) about each
9112 * message, not the complete message. FIXME: Specify exact fields.
9113 * The list is sorted by delivery time in ascending order.
9114 * Use NULL if you don't care about don't need the data (useful if you want to
9115 * know only the @number). If you provide &NULL, list will be allocated on
9116 * heap, if you provide pointer to non-NULL, list will be freed automatically
9117 * at first. Also in case of error the list will be NULLed.
9118 * @return IE_SUCCESS or appropriate error code. */
9119 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9120 _Bool outgoing_direction,
9121 const struct timeval *from_time, const struct timeval *to_time,
9122 const long int *organization_unit_number,
9123 const unsigned int status_filter,
9124 const unsigned long int offset, unsigned long int *number,
9125 struct isds_list **messages) {
9127 isds_error err = IE_SUCCESS;
9128 #if HAVE_LIBCURL
9129 xmlNsPtr isds_ns = NULL;
9130 xmlNodePtr request = NULL, node;
9131 xmlDocPtr response = NULL;
9132 xmlChar *code = NULL, *message = NULL;
9133 xmlXPathContextPtr xpath_ctx = NULL;
9134 xmlXPathObjectPtr result = NULL;
9135 xmlChar *string = NULL;
9136 long unsigned int count = 0;
9137 #endif
9139 if (!context) return IE_INVALID_CONTEXT;
9140 zfree(context->long_message);
9142 /* Free former message list if any */
9143 if (messages) isds_list_free(messages);
9145 #if HAVE_LIBCURL
9146 /* Check if connection is established
9147 * TODO: This check should be done downstairs. */
9148 if (!context->curl) return IE_CONNECTION_CLOSED;
9150 /* Build GetListOf*Messages request */
9151 request = xmlNewNode(NULL,
9152 (outgoing_direction) ?
9153 BAD_CAST "GetListOfSentMessages" :
9154 BAD_CAST "GetListOfReceivedMessages"
9156 if (!request) {
9157 isds_log_message(context,
9158 (outgoing_direction) ?
9159 _("Could not build GetListOfSentMessages request") :
9160 _("Could not build GetListOfReceivedMessages request")
9162 return IE_ERROR;
9164 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9165 if(!isds_ns) {
9166 isds_log_message(context, _("Could not create ISDS name space"));
9167 xmlFreeNode(request);
9168 return IE_ERROR;
9170 xmlSetNs(request, isds_ns);
9173 if (from_time) {
9174 err = timeval2timestring(from_time, &string);
9175 if (err) goto leave;
9177 INSERT_STRING(request, "dmFromTime", string);
9178 free(string); string = NULL;
9180 if (to_time) {
9181 err = timeval2timestring(to_time, &string);
9182 if (err) goto leave;
9184 INSERT_STRING(request, "dmToTime", string);
9185 free(string); string = NULL;
9187 if (outgoing_direction) {
9188 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9189 organization_unit_number, string);
9190 } else {
9191 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9192 organization_unit_number, string);
9195 if (status_filter > MESSAGESTATE_ANY) {
9196 isds_printf_message(context,
9197 _("Invalid message state filter value: %ld"), status_filter);
9198 err = IE_INVAL;
9199 goto leave;
9201 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9203 if (offset > 0 ) {
9204 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9205 } else {
9206 INSERT_STRING(request, "dmOffset", "1");
9209 /* number 0 means no limit */
9210 if (number && *number == 0) {
9211 INSERT_STRING(request, "dmLimit", NULL);
9212 } else {
9213 INSERT_ULONGINT(request, "dmLimit", number, string);
9217 isds_log(ILF_ISDS, ILL_DEBUG,
9218 (outgoing_direction) ?
9219 _("Sending GetListOfSentMessages request to ISDS\n") :
9220 _("Sending GetListOfReceivedMessages request to ISDS\n")
9223 /* Sent request */
9224 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9225 xmlFreeNode(request); request = NULL;
9227 if (err) {
9228 isds_log(ILF_ISDS, ILL_DEBUG,
9229 (outgoing_direction) ?
9230 _("Processing ISDS response on GetListOfSentMessages "
9231 "request failed\n") :
9232 _("Processing ISDS response on GetListOfReceivedMessages "
9233 "request failed\n")
9235 goto leave;
9238 /* Check for response status */
9239 err = isds_response_status(context, SERVICE_DM_INFO, response,
9240 &code, &message, NULL);
9241 if (err) {
9242 isds_log(ILF_ISDS, ILL_DEBUG,
9243 (outgoing_direction) ?
9244 _("ISDS response on GetListOfSentMessages request "
9245 "is missing status\n") :
9246 _("ISDS response on GetListOfReceivedMessages request "
9247 "is missing status\n")
9249 goto leave;
9252 /* Request processed, but nothing found */
9253 if (xmlStrcmp(code, BAD_CAST "0000")) {
9254 char *code_locale = _isds_utf82locale((char*)code);
9255 char *message_locale = _isds_utf82locale((char*)message);
9256 isds_log(ILF_ISDS, ILL_DEBUG,
9257 (outgoing_direction) ?
9258 _("Server refused GetListOfSentMessages request "
9259 "(code=%s, message=%s)\n") :
9260 _("Server refused GetListOfReceivedMessages request "
9261 "(code=%s, message=%s)\n"),
9262 code_locale, message_locale);
9263 isds_log_message(context, message_locale);
9264 free(code_locale);
9265 free(message_locale);
9266 err = IE_ISDS;
9267 goto leave;
9271 /* Extract data */
9272 xpath_ctx = xmlXPathNewContext(response);
9273 if (!xpath_ctx) {
9274 err = IE_ERROR;
9275 goto leave;
9277 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9278 err = IE_ERROR;
9279 goto leave;
9281 result = xmlXPathEvalExpression(
9282 (outgoing_direction) ?
9283 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9284 "isds:dmRecords/isds:dmRecord" :
9285 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9286 "isds:dmRecords/isds:dmRecord",
9287 xpath_ctx);
9288 if (!result) {
9289 err = IE_ERROR;
9290 goto leave;
9293 /* Fill output arguments in */
9294 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9295 struct isds_envelope *envelope;
9296 struct isds_list *item = NULL, *last_item = NULL;
9298 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9299 /* Create new message */
9300 item = calloc(1, sizeof(*item));
9301 if (!item) {
9302 err = IE_NOMEM;
9303 goto leave;
9305 item->destructor = (void(*)(void**)) &isds_message_free;
9306 item->data = calloc(1, sizeof(struct isds_message));
9307 if (!item->data) {
9308 isds_list_free(&item);
9309 err = IE_NOMEM;
9310 goto leave;
9313 /* Extract envelope data */
9314 xpath_ctx->node = result->nodesetval->nodeTab[count];
9315 envelope = NULL;
9316 err = extract_DmRecord(context, &envelope, xpath_ctx);
9317 if (err) {
9318 isds_list_free(&item);
9319 goto leave;
9322 /* Attach extracted envelope */
9323 ((struct isds_message *) item->data)->envelope = envelope;
9325 /* Append new message into the list */
9326 if (!*messages) {
9327 *messages = last_item = item;
9328 } else {
9329 last_item->next = item;
9330 last_item = item;
9334 if (number) *number = count;
9336 leave:
9337 if (err) {
9338 isds_list_free(messages);
9341 free(string);
9342 xmlXPathFreeObject(result);
9343 xmlXPathFreeContext(xpath_ctx);
9345 free(code);
9346 free(message);
9347 xmlFreeDoc(response);
9348 xmlFreeNode(request);
9350 if (!err)
9351 isds_log(ILF_ISDS, ILL_DEBUG,
9352 (outgoing_direction) ?
9353 _("GetListOfSentMessages request processed by server "
9354 "successfully.\n") :
9355 _("GetListOfReceivedMessages request processed by server "
9356 "successfully.\n")
9358 #else /* not HAVE_LIBCURL */
9359 err = IE_NOTSUP;
9360 #endif
9361 return err;
9365 /* Get list of outgoing (already sent) messages.
9366 * Any criterion argument can be NULL, if you don't care about it.
9367 * @context is session context. Must not be NULL.
9368 * @from_time is minimal time and date of message sending inclusive.
9369 * @to_time is maximal time and date of message sending inclusive
9370 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9371 * @status_filter is bit field of isds_message_status values. Use special
9372 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9373 * all values, you can use bit-wise arithmetic if you want.)
9374 * @offset is index of first message we are interested in. First message is 1.
9375 * Set to 0 (or 1) if you don't care.
9376 * @number is maximal length of list you want to get as input value, outputs
9377 * number of messages matching these criteria. Can be NULL if you don't care
9378 * (applies to output value either).
9379 * @messages is automatically reallocated list of isds_message's. Be ware that
9380 * it returns only brief overview (envelope and some other fields) about each
9381 * message, not the complete message. FIXME: Specify exact fields.
9382 * The list is sorted by delivery time in ascending order.
9383 * Use NULL if you don't care about the meta data (useful if you want to know
9384 * only the @number). If you provide &NULL, list will be allocated on heap,
9385 * if you provide pointer to non-NULL, list will be freed automatically at
9386 * first. Also in case of error the list will be NULLed.
9387 * @return IE_SUCCESS or appropriate error code. */
9388 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9389 const struct timeval *from_time, const struct timeval *to_time,
9390 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9391 const unsigned long int offset, unsigned long int *number,
9392 struct isds_list **messages) {
9394 return isds_get_list_of_messages(
9395 context, 1,
9396 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9397 offset, number,
9398 messages);
9402 /* Get list of incoming (addressed to you) messages.
9403 * Any criterion argument can be NULL, if you don't care about it.
9404 * @context is session context. Must not be NULL.
9405 * @from_time is minimal time and date of message sending inclusive.
9406 * @to_time is maximal time and date of message sending inclusive
9407 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9408 * @status_filter is bit field of isds_message_status values. Use special
9409 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9410 * all values, you can use bit-wise arithmetic if you want.)
9411 * @offset is index of first message we are interested in. First message is 1.
9412 * Set to 0 (or 1) if you don't care.
9413 * @number is maximal length of list you want to get as input value, outputs
9414 * number of messages matching these criteria. Can be NULL if you don't care
9415 * (applies to output value either).
9416 * @messages is automatically reallocated list of isds_message's. Be ware that
9417 * it returns only brief overview (envelope and some other fields) about each
9418 * message, not the complete message. FIXME: Specify exact fields.
9419 * Use NULL if you don't care about the meta data (useful if you want to know
9420 * only the @number). If you provide &NULL, list will be allocated on heap,
9421 * if you provide pointer to non-NULL, list will be freed automatically at
9422 * first. Also in case of error the list will be NULLed.
9423 * @return IE_SUCCESS or appropriate error code. */
9424 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9425 const struct timeval *from_time, const struct timeval *to_time,
9426 const long int *dmRecipientOrgUnitNum,
9427 const unsigned int status_filter,
9428 const unsigned long int offset, unsigned long int *number,
9429 struct isds_list **messages) {
9431 return isds_get_list_of_messages(
9432 context, 0,
9433 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9434 offset, number,
9435 messages);
9439 /* Get list of sent message state changes.
9440 * Any criterion argument can be NULL, if you don't care about it.
9441 * @context is session context. Must not be NULL.
9442 * @from_time is minimal time and date of status changes inclusive
9443 * @to_time is maximal time and date of status changes inclusive
9444 * @changed_states is automatically reallocated list of
9445 * isds_message_status_change's. If you provide &NULL, list will be allocated
9446 * on heap, if you provide pointer to non-NULL, list will be freed
9447 * automatically at first. Also in case of error the list will be NULLed.
9448 * XXX: The list item ordering is not specified.
9449 * XXX: Server provides only `recent' changes.
9450 * @return IE_SUCCESS or appropriate error code. */
9451 isds_error isds_get_list_of_sent_message_state_changes(
9452 struct isds_ctx *context,
9453 const struct timeval *from_time, const struct timeval *to_time,
9454 struct isds_list **changed_states) {
9456 isds_error err = IE_SUCCESS;
9457 #if HAVE_LIBCURL
9458 xmlNsPtr isds_ns = NULL;
9459 xmlNodePtr request = NULL, node;
9460 xmlDocPtr response = NULL;
9461 xmlXPathContextPtr xpath_ctx = NULL;
9462 xmlXPathObjectPtr result = NULL;
9463 xmlChar *string = NULL;
9464 long unsigned int count = 0;
9465 #endif
9467 if (!context) return IE_INVALID_CONTEXT;
9468 zfree(context->long_message);
9470 /* Free former message list if any */
9471 isds_list_free(changed_states);
9473 #if HAVE_LIBCURL
9474 /* Check if connection is established
9475 * TODO: This check should be done downstairs. */
9476 if (!context->curl) return IE_CONNECTION_CLOSED;
9478 /* Build GetMessageStateChanges request */
9479 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
9480 if (!request) {
9481 isds_log_message(context,
9482 _("Could not build GetMessageStateChanges request"));
9483 return IE_ERROR;
9485 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9486 if(!isds_ns) {
9487 isds_log_message(context, _("Could not create ISDS name space"));
9488 xmlFreeNode(request);
9489 return IE_ERROR;
9491 xmlSetNs(request, isds_ns);
9494 if (from_time) {
9495 err = timeval2timestring(from_time, &string);
9496 if (err) goto leave;
9498 INSERT_STRING(request, "dmFromTime", string);
9499 zfree(string);
9501 if (to_time) {
9502 err = timeval2timestring(to_time, &string);
9503 if (err) goto leave;
9505 INSERT_STRING(request, "dmToTime", string);
9506 zfree(string);
9509 /* Sent request */
9510 err = send_destroy_request_check_response(context,
9511 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9512 &response, NULL, NULL);
9513 if (err) goto leave;
9516 /* Extract data */
9517 xpath_ctx = xmlXPathNewContext(response);
9518 if (!xpath_ctx) {
9519 err = IE_ERROR;
9520 goto leave;
9522 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9523 err = IE_ERROR;
9524 goto leave;
9526 result = xmlXPathEvalExpression(
9527 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9528 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9529 if (!result) {
9530 err = IE_ERROR;
9531 goto leave;
9534 /* Fill output arguments in */
9535 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9536 struct isds_list *item = NULL, *last_item = NULL;
9538 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9539 /* Create new status change */
9540 item = calloc(1, sizeof(*item));
9541 if (!item) {
9542 err = IE_NOMEM;
9543 goto leave;
9545 item->destructor =
9546 (void(*)(void**)) &isds_message_status_change_free;
9548 /* Extract message status change */
9549 xpath_ctx->node = result->nodesetval->nodeTab[count];
9550 err = extract_StateChangesRecord(context,
9551 (struct isds_message_status_change **) &item->data,
9552 xpath_ctx);
9553 if (err) {
9554 isds_list_free(&item);
9555 goto leave;
9558 /* Append new message status change into the list */
9559 if (!*changed_states) {
9560 *changed_states = last_item = item;
9561 } else {
9562 last_item->next = item;
9563 last_item = item;
9568 leave:
9569 if (err) {
9570 isds_list_free(changed_states);
9573 free(string);
9574 xmlXPathFreeObject(result);
9575 xmlXPathFreeContext(xpath_ctx);
9576 xmlFreeDoc(response);
9577 xmlFreeNode(request);
9579 if (!err)
9580 isds_log(ILF_ISDS, ILL_DEBUG,
9581 _("GetMessageStateChanges request processed by server "
9582 "successfully.\n"));
9583 #else /* not HAVE_LIBCURL */
9584 err = IE_NOTSUP;
9585 #endif
9586 return err;
9590 #if HAVE_LIBCURL
9591 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9592 * code
9593 * @context is session context
9594 * @service is ISDS WS service handler
9595 * @service_name is name of SERVICE_DM_OPERATIONS
9596 * @message_id is message ID to send as service argument to ISDS
9597 * @response is reallocated server SOAP body response as XML document
9598 * @raw_response is reallocated bit stream with response body. Use
9599 * NULL if you don't care
9600 * @raw_response_length is size of @raw_response in bytes
9601 * @code is reallocated ISDS status code
9602 * @status_message is reallocated ISDS status message
9603 * @return error coded from lower layer, context message will be set up
9604 * appropriately. */
9605 static isds_error build_send_check_message_request(struct isds_ctx *context,
9606 const isds_service service, const xmlChar *service_name,
9607 const char *message_id,
9608 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9609 xmlChar **code, xmlChar **status_message) {
9611 isds_error err = IE_SUCCESS;
9612 char *service_name_locale = NULL, *message_id_locale = NULL;
9613 xmlNodePtr request = NULL, node;
9614 xmlNsPtr isds_ns = NULL;
9616 if (!context) return IE_INVALID_CONTEXT;
9617 if (!service_name || !message_id) return IE_INVAL;
9618 if (!response || !code || !status_message) return IE_INVAL;
9619 if (!raw_response_length && raw_response) return IE_INVAL;
9621 /* Free output argument */
9622 xmlFreeDoc(*response); *response = NULL;
9623 if (raw_response) zfree(*raw_response);
9624 zfree(*code);
9625 zfree(*status_message);
9628 /* Check if connection is established
9629 * TODO: This check should be done downstairs. */
9630 if (!context->curl) return IE_CONNECTION_CLOSED;
9632 service_name_locale = _isds_utf82locale((char*)service_name);
9633 message_id_locale = _isds_utf82locale(message_id);
9634 if (!service_name_locale || !message_id_locale) {
9635 err = IE_NOMEM;
9636 goto leave;
9639 /* Build request */
9640 request = xmlNewNode(NULL, service_name);
9641 if (!request) {
9642 isds_printf_message(context,
9643 _("Could not build %s request for %s message ID"),
9644 service_name_locale, message_id_locale);
9645 err = IE_ERROR;
9646 goto leave;
9648 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9649 if(!isds_ns) {
9650 isds_log_message(context, _("Could not create ISDS name space"));
9651 err = IE_ERROR;
9652 goto leave;
9654 xmlSetNs(request, isds_ns);
9657 /* Add requested ID */
9658 err = validate_message_id_length(context, (xmlChar *) message_id);
9659 if (err) goto leave;
9660 INSERT_STRING(request, "dmID", message_id);
9663 isds_log(ILF_ISDS, ILL_DEBUG,
9664 _("Sending %s request for %s message ID to ISDS\n"),
9665 service_name_locale, message_id_locale);
9667 /* Send request */
9668 err = _isds(context, service, request, response,
9669 raw_response, raw_response_length);
9670 xmlFreeNode(request); request = NULL;
9672 if (err) {
9673 isds_log(ILF_ISDS, ILL_DEBUG,
9674 _("Processing ISDS response on %s request failed\n"),
9675 service_name_locale);
9676 goto leave;
9679 /* Check for response status */
9680 err = isds_response_status(context, service, *response,
9681 code, status_message, NULL);
9682 if (err) {
9683 isds_log(ILF_ISDS, ILL_DEBUG,
9684 _("ISDS response on %s request is missing status\n"),
9685 service_name_locale);
9686 goto leave;
9689 /* Request processed, but nothing found */
9690 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9691 char *code_locale = _isds_utf82locale((char*) *code);
9692 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9693 isds_log(ILF_ISDS, ILL_DEBUG,
9694 _("Server refused %s request for %s message ID "
9695 "(code=%s, message=%s)\n"),
9696 service_name_locale, message_id_locale,
9697 code_locale, status_message_locale);
9698 isds_log_message(context, status_message_locale);
9699 free(code_locale);
9700 free(status_message_locale);
9701 err = IE_ISDS;
9702 goto leave;
9705 leave:
9706 free(message_id_locale);
9707 free(service_name_locale);
9708 xmlFreeNode(request);
9709 return err;
9713 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9714 * signed data and free ISDS response.
9715 * @context is session context
9716 * @message_id is UTF-8 encoded message ID for logging purpose
9717 * @response is parsed XML document. It will be freed and NULLed in the middle
9718 * of function run to save memory. This is not guaranteed in case of error.
9719 * @request_name is name of ISDS request used to construct response root
9720 * element name and for logging purpose.
9721 * @raw is reallocated output buffer with DER encoded CMS data
9722 * @raw_length is size of @raw buffer in bytes
9723 * @returns standard error codes, in case of error, @raw will be freed and
9724 * NULLed, @response sometimes. */
9725 static isds_error find_extract_signed_data_free_response(
9726 struct isds_ctx *context, const xmlChar *message_id,
9727 xmlDocPtr *response, const xmlChar *request_name,
9728 void **raw, size_t *raw_length) {
9730 isds_error err = IE_SUCCESS;
9731 char *xpath_expression = NULL;
9732 xmlXPathContextPtr xpath_ctx = NULL;
9733 xmlXPathObjectPtr result = NULL;
9734 char *encoded_structure = NULL;
9736 if (!context) return IE_INVALID_CONTEXT;
9737 if (!raw) return IE_INVAL;
9738 zfree(*raw);
9739 if (!message_id || !response || !*response || !request_name || !raw_length)
9740 return IE_INVAL;
9742 /* Build XPath expression */
9743 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9744 "Response/isds:dmSignature");
9745 if (!xpath_expression) return IE_NOMEM;
9747 /* Extract data */
9748 xpath_ctx = xmlXPathNewContext(*response);
9749 if (!xpath_ctx) {
9750 err = IE_ERROR;
9751 goto leave;
9753 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9754 err = IE_ERROR;
9755 goto leave;
9757 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9758 if (!result) {
9759 err = IE_ERROR;
9760 goto leave;
9762 /* Empty response */
9763 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9764 char *message_id_locale = _isds_utf82locale((char*) message_id);
9765 isds_printf_message(context,
9766 _("Server did not return any signed data for message ID `%s' "
9767 "on %s request"),
9768 message_id_locale, request_name);
9769 free(message_id_locale);
9770 err = IE_ISDS;
9771 goto leave;
9773 /* More responses */
9774 if (result->nodesetval->nodeNr > 1) {
9775 char *message_id_locale = _isds_utf82locale((char*) message_id);
9776 isds_printf_message(context,
9777 _("Server did return more signed data for message ID `%s' "
9778 "on %s request"),
9779 message_id_locale, request_name);
9780 free(message_id_locale);
9781 err = IE_ISDS;
9782 goto leave;
9784 /* One response */
9785 xpath_ctx->node = result->nodesetval->nodeTab[0];
9787 /* Extract PKCS#7 structure */
9788 EXTRACT_STRING(".", encoded_structure);
9789 if (!encoded_structure) {
9790 isds_log_message(context, _("dmSignature element is empty"));
9793 /* Here we have delivery info as standalone CMS in encoded_structure.
9794 * We don't need any other data, free them: */
9795 xmlXPathFreeObject(result); result = NULL;
9796 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9797 xmlFreeDoc(*response); *response = NULL;
9800 /* Decode PKCS#7 to DER format */
9801 *raw_length = _isds_b64decode(encoded_structure, raw);
9802 if (*raw_length == (size_t) -1) {
9803 isds_log_message(context,
9804 _("Error while Base64-decoding PKCS#7 structure"));
9805 err = IE_ERROR;
9806 goto leave;
9809 leave:
9810 if (err) {
9811 zfree(*raw);
9812 raw_length = 0;
9815 free(encoded_structure);
9816 xmlXPathFreeObject(result);
9817 xmlXPathFreeContext(xpath_ctx);
9818 free(xpath_expression);
9820 return err;
9822 #endif /* HAVE_LIBCURL */
9825 /* Download incoming message envelope identified by ID.
9826 * @context is session context
9827 * @message_id is message identifier (you can get them from
9828 * isds_get_list_of_received_messages())
9829 * @message is automatically reallocated message retrieved from ISDS.
9830 * It will miss documents per se. Use isds_get_received_message(), if you are
9831 * interested in documents (content) too.
9832 * Returned hash and timestamp require documents to be verifiable. */
9833 isds_error isds_get_received_envelope(struct isds_ctx *context,
9834 const char *message_id, struct isds_message **message) {
9836 isds_error err = IE_SUCCESS;
9837 #if HAVE_LIBCURL
9838 xmlDocPtr response = NULL;
9839 xmlChar *code = NULL, *status_message = NULL;
9840 xmlXPathContextPtr xpath_ctx = NULL;
9841 xmlXPathObjectPtr result = NULL;
9842 #endif
9844 if (!context) return IE_INVALID_CONTEXT;
9845 zfree(context->long_message);
9847 /* Free former message if any */
9848 if (!message) return IE_INVAL;
9849 isds_message_free(message);
9851 #if HAVE_LIBCURL
9852 /* Do request and check for success */
9853 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9854 BAD_CAST "MessageEnvelopeDownload", message_id,
9855 &response, NULL, NULL, &code, &status_message);
9856 if (err) goto leave;
9858 /* Extract data */
9859 xpath_ctx = xmlXPathNewContext(response);
9860 if (!xpath_ctx) {
9861 err = IE_ERROR;
9862 goto leave;
9864 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9865 err = IE_ERROR;
9866 goto leave;
9868 result = xmlXPathEvalExpression(
9869 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9870 "isds:dmReturnedMessageEnvelope",
9871 xpath_ctx);
9872 if (!result) {
9873 err = IE_ERROR;
9874 goto leave;
9876 /* Empty response */
9877 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9878 char *message_id_locale = _isds_utf82locale((char*) message_id);
9879 isds_printf_message(context,
9880 _("Server did not return any envelope for ID `%s' "
9881 "on MessageEnvelopeDownload request"), message_id_locale);
9882 free(message_id_locale);
9883 err = IE_ISDS;
9884 goto leave;
9886 /* More envelops */
9887 if (result->nodesetval->nodeNr > 1) {
9888 char *message_id_locale = _isds_utf82locale((char*) message_id);
9889 isds_printf_message(context,
9890 _("Server did return more envelopes for ID `%s' "
9891 "on MessageEnvelopeDownload request"), message_id_locale);
9892 free(message_id_locale);
9893 err = IE_ISDS;
9894 goto leave;
9896 /* One message */
9897 xpath_ctx->node = result->nodesetval->nodeTab[0];
9899 /* Extract the envelope (= message without documents, hence 0) */
9900 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9901 if (err) goto leave;
9903 /* Save XML blob */
9904 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9905 &(*message)->raw_length);
9907 leave:
9908 if (err) {
9909 isds_message_free(message);
9912 xmlXPathFreeObject(result);
9913 xmlXPathFreeContext(xpath_ctx);
9915 free(code);
9916 free(status_message);
9917 if (!*message || !(*message)->xml) {
9918 xmlFreeDoc(response);
9921 if (!err)
9922 isds_log(ILF_ISDS, ILL_DEBUG,
9923 _("MessageEnvelopeDownload request processed by server "
9924 "successfully.\n")
9926 #else /* not HAVE_LIBCURL */
9927 err = IE_NOTSUP;
9928 #endif
9929 return err;
9933 /* Load delivery info of any format from buffer.
9934 * @context is session context
9935 * @raw_type advertises format of @buffer content. Only delivery info types
9936 * are accepted.
9937 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9938 * retrieve such data from message->raw after calling
9939 * isds_get_signed_delivery_info().
9940 * @length is length of buffer in bytes.
9941 * @message is automatically reallocated message parsed from @buffer.
9942 * @strategy selects how buffer will be attached into raw isds_message member.
9943 * */
9944 isds_error isds_load_delivery_info(struct isds_ctx *context,
9945 const isds_raw_type raw_type,
9946 const void *buffer, const size_t length,
9947 struct isds_message **message, const isds_buffer_strategy strategy) {
9949 isds_error err = IE_SUCCESS;
9950 message_ns_type message_ns;
9951 xmlDocPtr message_doc = NULL;
9952 xmlXPathContextPtr xpath_ctx = NULL;
9953 xmlXPathObjectPtr result = NULL;
9954 void *xml_stream = NULL;
9955 size_t xml_stream_length = 0;
9957 if (!context) return IE_INVALID_CONTEXT;
9958 zfree(context->long_message);
9959 if (!message) return IE_INVAL;
9960 isds_message_free(message);
9961 if (!buffer) return IE_INVAL;
9964 /* Select buffer format and extract XML from CMS*/
9965 switch (raw_type) {
9966 case RAWTYPE_DELIVERYINFO:
9967 message_ns = MESSAGE_NS_UNSIGNED;
9968 xml_stream = (void *) buffer;
9969 xml_stream_length = length;
9970 break;
9972 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
9973 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9974 xml_stream = (void *) buffer;
9975 xml_stream_length = length;
9976 break;
9978 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
9979 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9980 err = _isds_extract_cms_data(context, buffer, length,
9981 &xml_stream, &xml_stream_length);
9982 if (err) goto leave;
9983 break;
9985 default:
9986 isds_log_message(context, _("Bad raw delivery representation type"));
9987 return IE_INVAL;
9988 break;
9991 isds_log(ILF_ISDS, ILL_DEBUG,
9992 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
9993 xml_stream_length, xml_stream);
9995 /* Convert delivery info XML stream into XPath context */
9996 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9997 if (!message_doc) {
9998 err = IE_XML;
9999 goto leave;
10001 xpath_ctx = xmlXPathNewContext(message_doc);
10002 if (!xpath_ctx) {
10003 err = IE_ERROR;
10004 goto leave;
10006 /* XXX: Name spaces mangled for signed delivery info:
10007 * http://isds.czechpoint.cz/v20/delivery:
10009 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10010 * <q:dmDelivery>
10011 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10012 * <p:dmID>170272</p:dmID>
10013 * ...
10014 * </p:dmDm>
10015 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10016 * ...
10017 * </q:dmEvents>...</q:dmEvents>
10018 * </q:dmDelivery>
10019 * </q:GetDeliveryInfoResponse>
10020 * */
10021 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10022 err = IE_ERROR;
10023 goto leave;
10025 result = xmlXPathEvalExpression(
10026 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10027 xpath_ctx);
10028 if (!result) {
10029 err = IE_ERROR;
10030 goto leave;
10032 /* Empty delivery info */
10033 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10034 isds_printf_message(context,
10035 _("XML document is not sisds:dmDelivery document"));
10036 err = IE_ISDS;
10037 goto leave;
10039 /* More delivery info's */
10040 if (result->nodesetval->nodeNr > 1) {
10041 isds_printf_message(context,
10042 _("XML document has more sisds:dmDelivery elements"));
10043 err = IE_ISDS;
10044 goto leave;
10046 /* One delivery info */
10047 xpath_ctx->node = result->nodesetval->nodeTab[0];
10049 /* Extract the envelope (= message without documents, hence 0).
10050 * XXX: extract_TReturnedMessage() can obtain attachments size,
10051 * but delivery info carries none. It's coded as option elements,
10052 * so it should work. */
10053 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10054 if (err) goto leave;
10056 /* Extract events */
10057 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10058 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10059 if (err) { err = IE_ERROR; goto leave; }
10060 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10061 if (err) goto leave;
10063 /* Append raw CMS structure into message */
10064 (*message)->raw_type = raw_type;
10065 switch (strategy) {
10066 case BUFFER_DONT_STORE:
10067 break;
10068 case BUFFER_COPY:
10069 (*message)->raw = malloc(length);
10070 if (!(*message)->raw) {
10071 err = IE_NOMEM;
10072 goto leave;
10074 memcpy((*message)->raw, buffer, length);
10075 (*message)->raw_length = length;
10076 break;
10077 case BUFFER_MOVE:
10078 (*message)->raw = (void *) buffer;
10079 (*message)->raw_length = length;
10080 break;
10081 default:
10082 err = IE_ENUM;
10083 goto leave;
10086 leave:
10087 if (err) {
10088 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10089 isds_message_free(message);
10092 xmlXPathFreeObject(result);
10093 xmlXPathFreeContext(xpath_ctx);
10094 if (!*message || !(*message)->xml) {
10095 xmlFreeDoc(message_doc);
10097 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10099 if (!err)
10100 isds_log(ILF_ISDS, ILL_DEBUG,
10101 _("Delivery info loaded successfully.\n"));
10102 return err;
10106 /* Download signed delivery info-sheet of given message identified by ID.
10107 * @context is session context
10108 * @message_id is message identifier (you can get them from
10109 * isds_get_list_of_{sent,received}_messages())
10110 * @message is automatically reallocated message retrieved from ISDS.
10111 * It will miss documents per se. Use isds_get_signed_received_message(),
10112 * if you are interested in documents (content). OTOH, only this function
10113 * can get list events message has gone through. */
10114 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10115 const char *message_id, struct isds_message **message) {
10117 isds_error err = IE_SUCCESS;
10118 #if HAVE_LIBCURL
10119 xmlDocPtr response = NULL;
10120 xmlChar *code = NULL, *status_message = NULL;
10121 void *raw = NULL;
10122 size_t raw_length = 0;
10123 #endif
10125 if (!context) return IE_INVALID_CONTEXT;
10126 zfree(context->long_message);
10128 /* Free former message if any */
10129 if (!message) return IE_INVAL;
10130 isds_message_free(message);
10132 #if HAVE_LIBCURL
10133 /* Do request and check for success */
10134 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10135 BAD_CAST "GetSignedDeliveryInfo", message_id,
10136 &response, NULL, NULL, &code, &status_message);
10137 if (err) goto leave;
10139 /* Find signed delivery info, extract it into raw and maybe free
10140 * response */
10141 err = find_extract_signed_data_free_response(context,
10142 (xmlChar *)message_id, &response,
10143 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10144 if (err) goto leave;
10146 /* Parse delivery info */
10147 err = isds_load_delivery_info(context,
10148 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10149 message, BUFFER_MOVE);
10150 if (err) goto leave;
10152 raw = NULL;
10154 leave:
10155 if (err) {
10156 isds_message_free(message);
10159 free(raw);
10160 free(code);
10161 free(status_message);
10162 xmlFreeDoc(response);
10164 if (!err)
10165 isds_log(ILF_ISDS, ILL_DEBUG,
10166 _("GetSignedDeliveryInfo request processed by server "
10167 "successfully.\n")
10169 #else /* not HAVE_LIBCURL */
10170 err = IE_NOTSUP;
10171 #endif
10172 return err;
10176 /* Download delivery info-sheet of given message identified by ID.
10177 * @context is session context
10178 * @message_id is message identifier (you can get them from
10179 * isds_get_list_of_{sent,received}_messages())
10180 * @message is automatically reallocated message retrieved from ISDS.
10181 * It will miss documents per se. Use isds_get_received_message(), if you are
10182 * interested in documents (content). OTOH, only this function can get list
10183 * of events message has gone through. */
10184 isds_error isds_get_delivery_info(struct isds_ctx *context,
10185 const char *message_id, struct isds_message **message) {
10187 isds_error err = IE_SUCCESS;
10188 #if HAVE_LIBCURL
10189 xmlDocPtr response = NULL;
10190 xmlChar *code = NULL, *status_message = NULL;
10191 xmlNodePtr delivery_node = NULL;
10192 void *raw = NULL;
10193 size_t raw_length = 0;
10194 #endif
10196 if (!context) return IE_INVALID_CONTEXT;
10197 zfree(context->long_message);
10199 /* Free former message if any */
10200 if (!message) return IE_INVAL;
10201 isds_message_free(message);
10203 #if HAVE_LIBCURL
10204 /* Do request and check for success */
10205 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10206 BAD_CAST "GetDeliveryInfo", message_id,
10207 &response, NULL, NULL, &code, &status_message);
10208 if (err) goto leave;
10211 /* Serialize delivery info */
10212 delivery_node = xmlDocGetRootElement(response);
10213 if (!delivery_node) {
10214 char *message_id_locale = _isds_utf82locale((char*) message_id);
10215 isds_printf_message(context,
10216 _("Server did not return any delivery info for ID `%s' "
10217 "on GetDeliveryInfo request"), message_id_locale);
10218 free(message_id_locale);
10219 err = IE_ISDS;
10220 goto leave;
10222 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10223 if (err) goto leave;
10225 /* Parse delivery info */
10226 /* TODO: Here we parse the response second time. We could single delivery
10227 * parser from isds_load_delivery_info() to make things faster. */
10228 err = isds_load_delivery_info(context,
10229 RAWTYPE_DELIVERYINFO, raw, raw_length,
10230 message, BUFFER_MOVE);
10231 if (err) goto leave;
10233 raw = NULL;
10236 leave:
10237 if (err) {
10238 isds_message_free(message);
10241 free(raw);
10242 free(code);
10243 free(status_message);
10244 xmlFreeDoc(response);
10246 if (!err)
10247 isds_log(ILF_ISDS, ILL_DEBUG,
10248 _("GetDeliveryInfo request processed by server "
10249 "successfully.\n")
10251 #else /* not HAVE_LIBCURL */
10252 err = IE_NOTSUP;
10253 #endif
10254 return err;
10258 /* Download incoming message identified by ID.
10259 * @context is session context
10260 * @message_id is message identifier (you can get them from
10261 * isds_get_list_of_received_messages())
10262 * @message is automatically reallocated message retrieved from ISDS */
10263 isds_error isds_get_received_message(struct isds_ctx *context,
10264 const char *message_id, struct isds_message **message) {
10266 isds_error err = IE_SUCCESS;
10267 #if HAVE_LIBCURL
10268 xmlDocPtr response = NULL;
10269 void *xml_stream = NULL;
10270 size_t xml_stream_length;
10271 xmlChar *code = NULL, *status_message = NULL;
10272 xmlXPathContextPtr xpath_ctx = NULL;
10273 xmlXPathObjectPtr result = NULL;
10274 char *phys_path = NULL;
10275 size_t phys_start, phys_end;
10276 #endif
10278 if (!context) return IE_INVALID_CONTEXT;
10279 zfree(context->long_message);
10281 /* Free former message if any */
10282 if (NULL == message) return IE_INVAL;
10283 if (message) isds_message_free(message);
10285 #if HAVE_LIBCURL
10286 /* Do request and check for success */
10287 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10288 BAD_CAST "MessageDownload", message_id,
10289 &response, &xml_stream, &xml_stream_length,
10290 &code, &status_message);
10291 if (err) goto leave;
10293 /* Extract data */
10294 xpath_ctx = xmlXPathNewContext(response);
10295 if (!xpath_ctx) {
10296 err = IE_ERROR;
10297 goto leave;
10299 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10300 err = IE_ERROR;
10301 goto leave;
10303 result = xmlXPathEvalExpression(
10304 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10305 xpath_ctx);
10306 if (!result) {
10307 err = IE_ERROR;
10308 goto leave;
10310 /* Empty response */
10311 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10312 char *message_id_locale = _isds_utf82locale((char*) message_id);
10313 isds_printf_message(context,
10314 _("Server did not return any message for ID `%s' "
10315 "on MessageDownload request"), message_id_locale);
10316 free(message_id_locale);
10317 err = IE_ISDS;
10318 goto leave;
10320 /* More messages */
10321 if (result->nodesetval->nodeNr > 1) {
10322 char *message_id_locale = _isds_utf82locale((char*) message_id);
10323 isds_printf_message(context,
10324 _("Server did return more messages for ID `%s' "
10325 "on MessageDownload request"), message_id_locale);
10326 free(message_id_locale);
10327 err = IE_ISDS;
10328 goto leave;
10330 /* One message */
10331 xpath_ctx->node = result->nodesetval->nodeTab[0];
10333 /* Extract the message */
10334 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10335 if (err) goto leave;
10337 /* Locate raw XML blob */
10338 phys_path = strdup(
10339 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10340 PHYSXML_ELEMENT_SEPARATOR
10341 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10342 PHYSXML_ELEMENT_SEPARATOR
10343 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10345 if (!phys_path) {
10346 err = IE_NOMEM;
10347 goto leave;
10349 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10350 phys_path, &phys_start, &phys_end);
10351 zfree(phys_path);
10352 if (err) {
10353 isds_log_message(context,
10354 _("Substring with isds:MessageDownloadResponse element "
10355 "could not be located in raw SOAP message"));
10356 goto leave;
10358 /* Save XML blob */
10359 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10360 &(*message)->raw_length);*/
10361 /* TODO: Store name space declarations from ancestors */
10362 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10363 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10364 (*message)->raw_length = phys_end - phys_start + 1;
10365 (*message)->raw = malloc((*message)->raw_length);
10366 if (!(*message)->raw) {
10367 err = IE_NOMEM;
10368 goto leave;
10370 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10373 leave:
10374 if (err) {
10375 isds_message_free(message);
10378 free(phys_path);
10380 xmlXPathFreeObject(result);
10381 xmlXPathFreeContext(xpath_ctx);
10383 free(code);
10384 free(status_message);
10385 free(xml_stream);
10386 if (!*message || !(*message)->xml) {
10387 xmlFreeDoc(response);
10390 if (!err)
10391 isds_log(ILF_ISDS, ILL_DEBUG,
10392 _("MessageDownload request processed by server "
10393 "successfully.\n")
10395 #else /* not HAVE_LIBCURL */
10396 err = IE_NOTSUP;
10397 #endif
10398 return err;
10402 /* Load message of any type from buffer.
10403 * @context is session context
10404 * @raw_type defines content type of @buffer. Only message types are allowed.
10405 * @buffer is message raw representation. Format (CMS, plain signed,
10406 * message direction) is defined in @raw_type. You can retrieve such data
10407 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10408 * @length is length of buffer in bytes.
10409 * @message is automatically reallocated message parsed from @buffer.
10410 * @strategy selects how buffer will be attached into raw isds_message member.
10411 * */
10412 isds_error isds_load_message(struct isds_ctx *context,
10413 const isds_raw_type raw_type, const void *buffer, const size_t length,
10414 struct isds_message **message, const isds_buffer_strategy strategy) {
10416 isds_error err = IE_SUCCESS;
10417 void *xml_stream = NULL;
10418 size_t xml_stream_length = 0;
10419 message_ns_type message_ns;
10420 xmlDocPtr message_doc = NULL;
10421 xmlXPathContextPtr xpath_ctx = NULL;
10422 xmlXPathObjectPtr result = NULL;
10424 if (!context) return IE_INVALID_CONTEXT;
10425 zfree(context->long_message);
10426 if (!message) return IE_INVAL;
10427 isds_message_free(message);
10428 if (!buffer) return IE_INVAL;
10431 /* Select buffer format and extract XML from CMS*/
10432 switch (raw_type) {
10433 case RAWTYPE_INCOMING_MESSAGE:
10434 message_ns = MESSAGE_NS_UNSIGNED;
10435 xml_stream = (void *) buffer;
10436 xml_stream_length = length;
10437 break;
10439 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10440 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10441 xml_stream = (void *) buffer;
10442 xml_stream_length = length;
10443 break;
10445 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10446 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10447 err = _isds_extract_cms_data(context, buffer, length,
10448 &xml_stream, &xml_stream_length);
10449 if (err) goto leave;
10450 break;
10452 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10453 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10454 xml_stream = (void *) buffer;
10455 xml_stream_length = length;
10456 break;
10458 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10459 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10460 err = _isds_extract_cms_data(context, buffer, length,
10461 &xml_stream, &xml_stream_length);
10462 if (err) goto leave;
10463 break;
10465 default:
10466 isds_log_message(context, _("Bad raw message representation type"));
10467 return IE_INVAL;
10468 break;
10471 isds_log(ILF_ISDS, ILL_DEBUG,
10472 _("Loading message:\n%.*s\nEnd of message\n"),
10473 xml_stream_length, xml_stream);
10475 /* Convert messages XML stream into XPath context */
10476 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10477 if (!message_doc) {
10478 err = IE_XML;
10479 goto leave;
10481 xpath_ctx = xmlXPathNewContext(message_doc);
10482 if (!xpath_ctx) {
10483 err = IE_ERROR;
10484 goto leave;
10486 /* XXX: Standard name space for unsigned incoming direction:
10487 * http://isds.czechpoint.cz/v20/
10489 * XXX: Name spaces mangled for signed outgoing direction:
10490 * http://isds.czechpoint.cz/v20/SentMessage:
10492 * <q:MessageDownloadResponse
10493 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10494 * <q:dmReturnedMessage>
10495 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10496 * <p:dmID>151916</p:dmID>
10497 * ...
10498 * </p:dmDm>
10499 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10500 * ...
10501 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10502 * </q:dmReturnedMessage>
10503 * </q:MessageDownloadResponse>
10505 * XXX: Name spaces mangled for signed incoming direction:
10506 * http://isds.czechpoint.cz/v20/message:
10508 * <q:MessageDownloadResponse
10509 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10510 * <q:dmReturnedMessage>
10511 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10512 * <p:dmID>151916</p:dmID>
10513 * ...
10514 * </p:dmDm>
10515 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10516 * ...
10517 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10518 * </q:dmReturnedMessage>
10519 * </q:MessageDownloadResponse>
10521 * Stupidity of ISDS developers is unlimited */
10522 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10523 err = IE_ERROR;
10524 goto leave;
10526 result = xmlXPathEvalExpression(
10527 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10528 xpath_ctx);
10529 if (!result) {
10530 err = IE_ERROR;
10531 goto leave;
10533 /* Empty message */
10534 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10535 isds_printf_message(context,
10536 _("XML document does not contain "
10537 "sisds:dmReturnedMessage element"));
10538 err = IE_ISDS;
10539 goto leave;
10541 /* More messages */
10542 if (result->nodesetval->nodeNr > 1) {
10543 isds_printf_message(context,
10544 _("XML document has more sisds:dmReturnedMessage elements"));
10545 err = IE_ISDS;
10546 goto leave;
10548 /* One message */
10549 xpath_ctx->node = result->nodesetval->nodeTab[0];
10551 /* Extract the message */
10552 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10553 if (err) goto leave;
10555 /* Append raw buffer into message */
10556 (*message)->raw_type = raw_type;
10557 switch (strategy) {
10558 case BUFFER_DONT_STORE:
10559 break;
10560 case BUFFER_COPY:
10561 (*message)->raw = malloc(length);
10562 if (!(*message)->raw) {
10563 err = IE_NOMEM;
10564 goto leave;
10566 memcpy((*message)->raw, buffer, length);
10567 (*message)->raw_length = length;
10568 break;
10569 case BUFFER_MOVE:
10570 (*message)->raw = (void *) buffer;
10571 (*message)->raw_length = length;
10572 break;
10573 default:
10574 err = IE_ENUM;
10575 goto leave;
10579 leave:
10580 if (err) {
10581 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10582 isds_message_free(message);
10585 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10586 xmlXPathFreeObject(result);
10587 xmlXPathFreeContext(xpath_ctx);
10588 if (!*message || !(*message)->xml) {
10589 xmlFreeDoc(message_doc);
10592 if (!err)
10593 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10594 return err;
10598 /* Determine type of raw message or delivery info according some heuristics.
10599 * It does not validate the raw blob.
10600 * @context is session context
10601 * @raw_type returns content type of @buffer. Valid only if exit code of this
10602 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10603 * reallocated memory.
10604 * @buffer is message raw representation.
10605 * @length is length of buffer in bytes. */
10606 isds_error isds_guess_raw_type(struct isds_ctx *context,
10607 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10608 isds_error err;
10609 void *xml_stream = NULL;
10610 size_t xml_stream_length = 0;
10611 xmlDocPtr document = NULL;
10612 xmlNodePtr root = NULL;
10614 if (!context) return IE_INVALID_CONTEXT;
10615 zfree(context->long_message);
10616 if (length == 0 || !buffer) return IE_INVAL;
10617 if (!raw_type) return IE_INVAL;
10619 /* Try CMS */
10620 err = _isds_extract_cms_data(context, buffer, length,
10621 &xml_stream, &xml_stream_length);
10622 if (err) {
10623 xml_stream = (void *) buffer;
10624 xml_stream_length = (size_t) length;
10625 err = IE_SUCCESS;
10628 /* Try XML */
10629 document = xmlParseMemory(xml_stream, xml_stream_length);
10630 if (!document) {
10631 isds_printf_message(context,
10632 _("Could not parse data as XML document"));
10633 err = IE_NOTSUP;
10634 goto leave;
10637 /* Get root element */
10638 root = xmlDocGetRootElement(document);
10639 if (!root) {
10640 isds_printf_message(context,
10641 _("XML document is missing root element"));
10642 err = IE_XML;
10643 goto leave;
10646 if (!root->ns || !root->ns->href) {
10647 isds_printf_message(context,
10648 _("Root element does not belong to any name space"));
10649 err = IE_NOTSUP;
10650 goto leave;
10653 /* Test name space */
10654 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10655 if (xml_stream == buffer)
10656 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10657 else
10658 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10659 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10660 if (xml_stream == buffer)
10661 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10662 else
10663 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10664 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10665 if (xml_stream == buffer)
10666 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10667 else
10668 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10669 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10670 if (xml_stream != buffer) {
10671 isds_printf_message(context,
10672 _("Document in ISDS name space is encapsulated into CMS" ));
10673 err = IE_NOTSUP;
10674 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10675 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10676 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10677 *raw_type = RAWTYPE_DELIVERYINFO;
10678 else {
10679 isds_printf_message(context,
10680 _("Unknown root element in ISDS name space"));
10681 err = IE_NOTSUP;
10683 } else {
10684 isds_printf_message(context,
10685 _("Unknown name space"));
10686 err = IE_NOTSUP;
10689 leave:
10690 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10691 xmlFreeDoc(document);
10692 return err;
10696 /* Download signed incoming/outgoing message identified by ID.
10697 * @context is session context
10698 * @output is true for outgoing message, false for incoming message
10699 * @message_id is message identifier (you can get them from
10700 * isds_get_list_of_{sent,received}_messages())
10701 * @message is automatically reallocated message retrieved from ISDS. The raw
10702 * member will be filled with PKCS#7 structure in DER format. */
10703 static isds_error isds_get_signed_message(struct isds_ctx *context,
10704 const _Bool outgoing, const char *message_id,
10705 struct isds_message **message) {
10707 isds_error err = IE_SUCCESS;
10708 #if HAVE_LIBCURL
10709 xmlDocPtr response = NULL;
10710 xmlChar *code = NULL, *status_message = NULL;
10711 xmlXPathContextPtr xpath_ctx = NULL;
10712 xmlXPathObjectPtr result = NULL;
10713 char *encoded_structure = NULL;
10714 void *raw = NULL;
10715 size_t raw_length = 0;
10716 #endif
10718 if (!context) return IE_INVALID_CONTEXT;
10719 zfree(context->long_message);
10720 if (!message) return IE_INVAL;
10721 isds_message_free(message);
10723 #if HAVE_LIBCURL
10724 /* Do request and check for success */
10725 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10726 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10727 BAD_CAST "SignedMessageDownload",
10728 message_id, &response, NULL, NULL, &code, &status_message);
10729 if (err) goto leave;
10731 /* Find signed message, extract it into raw and maybe free
10732 * response */
10733 err = find_extract_signed_data_free_response(context,
10734 (xmlChar *)message_id, &response,
10735 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10736 BAD_CAST "SignedMessageDownload",
10737 &raw, &raw_length);
10738 if (err) goto leave;
10740 /* Parse message */
10741 err = isds_load_message(context,
10742 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10743 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10744 raw, raw_length, message, BUFFER_MOVE);
10745 if (err) goto leave;
10747 raw = NULL;
10749 leave:
10750 if (err) {
10751 isds_message_free(message);
10754 free(encoded_structure);
10755 xmlXPathFreeObject(result);
10756 xmlXPathFreeContext(xpath_ctx);
10757 free(raw);
10759 free(code);
10760 free(status_message);
10761 xmlFreeDoc(response);
10763 if (!err)
10764 isds_log(ILF_ISDS, ILL_DEBUG,
10765 (outgoing) ?
10766 _("SignedSentMessageDownload request processed by server "
10767 "successfully.\n") :
10768 _("SignedMessageDownload request processed by server "
10769 "successfully.\n")
10771 #else /* not HAVE_LIBCURL */
10772 err = IE_NOTSUP;
10773 #endif
10774 return err;
10778 /* Download signed incoming message identified by ID.
10779 * @context is session context
10780 * @message_id is message identifier (you can get them from
10781 * isds_get_list_of_received_messages())
10782 * @message is automatically reallocated message retrieved from ISDS. The raw
10783 * member will be filled with PKCS#7 structure in DER format. */
10784 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10785 const char *message_id, struct isds_message **message) {
10786 return isds_get_signed_message(context, 0, message_id, message);
10790 /* Download signed outgoing message identified by ID.
10791 * @context is session context
10792 * @message_id is message identifier (you can get them from
10793 * isds_get_list_of_sent_messages())
10794 * @message is automatically reallocated message retrieved from ISDS. The raw
10795 * member will be filled with PKCS#7 structure in DER format. */
10796 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10797 const char *message_id, struct isds_message **message) {
10798 return isds_get_signed_message(context, 1, message_id, message);
10802 /* Get type and name of user who sent a message identified by ID.
10803 * @context is session context
10804 * @message_id is message identifier
10805 * @sender_type is pointer to automatically allocated type of sender detected
10806 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10807 * library or to the server, NULL will be returned. Pass NULL if you don't
10808 * care about it.
10809 * @raw_sender_type is automatically reallocated UTF-8 string describing
10810 * sender type or NULL if not known to server. Pass NULL if you don't care.
10811 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10812 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10813 isds_error isds_get_message_sender(struct isds_ctx *context,
10814 const char *message_id, isds_sender_type **sender_type,
10815 char **raw_sender_type, char **sender_name) {
10816 isds_error err = IE_SUCCESS;
10817 #if HAVE_LIBCURL
10818 xmlDocPtr response = NULL;
10819 xmlChar *code = NULL, *status_message = NULL;
10820 xmlXPathContextPtr xpath_ctx = NULL;
10821 xmlXPathObjectPtr result = NULL;
10822 char *type_string = NULL;
10823 #endif
10825 if (!context) return IE_INVALID_CONTEXT;
10826 zfree(context->long_message);
10827 if (sender_type) zfree(*sender_type);
10828 if (raw_sender_type) zfree(*raw_sender_type);
10829 if (sender_name) zfree(*sender_name);
10830 if (!message_id) return IE_INVAL;
10832 #if HAVE_LIBCURL
10833 /* Do request and check for success */
10834 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10835 BAD_CAST "GetMessageAuthor",
10836 message_id, &response, NULL, NULL, &code, &status_message);
10837 if (err) goto leave;
10839 /* Extract data */
10840 xpath_ctx = xmlXPathNewContext(response);
10841 if (!xpath_ctx) {
10842 err = IE_ERROR;
10843 goto leave;
10845 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10846 err = IE_ERROR;
10847 goto leave;
10849 result = xmlXPathEvalExpression(
10850 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10851 if (!result) {
10852 err = IE_ERROR;
10853 goto leave;
10855 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10856 isds_log_message(context,
10857 _("Missing GetMessageAuthorResponse element"));
10858 err = IE_ISDS;
10859 goto leave;
10861 if (result->nodesetval->nodeNr > 1) {
10862 isds_log_message(context,
10863 _("Multiple GetMessageAuthorResponse element"));
10864 err = IE_ISDS;
10865 goto leave;
10867 xpath_ctx->node = result->nodesetval->nodeTab[0];
10868 xmlXPathFreeObject(result); result = NULL;
10870 /* Fill output arguments in */
10871 EXTRACT_STRING("isds:userType", type_string);
10872 if (NULL != type_string) {
10873 if (NULL != sender_type) {
10874 *sender_type = calloc(1, sizeof(**sender_type));
10875 if (NULL == *sender_type) {
10876 err = IE_NOMEM;
10877 goto leave;
10880 err = string2isds_sender_type((xmlChar *)type_string,
10881 *sender_type);
10882 if (err) {
10883 zfree(*sender_type);
10884 if (err == IE_ENUM) {
10885 err = IE_SUCCESS;
10886 char *type_string_locale = _isds_utf82locale(type_string);
10887 isds_log(ILF_ISDS, ILL_WARNING,
10888 _("Unknown isds:userType value: %s"),
10889 type_string_locale);
10890 free(type_string_locale);
10895 if (NULL != sender_name)
10896 EXTRACT_STRING("isds:authorName", *sender_name);
10898 leave:
10899 if (err) {
10900 if (NULL != sender_type) zfree(*sender_type);
10901 zfree(type_string);
10902 if (NULL != sender_name) zfree(*sender_name);
10904 if (NULL != raw_sender_type) *raw_sender_type = type_string;
10906 xmlXPathFreeObject(result);
10907 xmlXPathFreeContext(xpath_ctx);
10909 free(code);
10910 free(status_message);
10911 xmlFreeDoc(response);
10913 if (!err)
10914 isds_log(ILF_ISDS, ILL_DEBUG,
10915 _("GetMessageAuthor request processed by server "
10916 "successfully.\n"));
10917 #else /* not HAVE_LIBCURL */
10918 err = IE_NOTSUP;
10919 #endif
10920 return err;
10924 /* Retrieve hash of message identified by ID stored in ISDS.
10925 * @context is session context
10926 * @message_id is message identifier
10927 * @hash is automatically reallocated message hash downloaded from ISDS.
10928 * Message must exist in system and must not be deleted. */
10929 isds_error isds_download_message_hash(struct isds_ctx *context,
10930 const char *message_id, struct isds_hash **hash) {
10932 isds_error err = IE_SUCCESS;
10933 #if HAVE_LIBCURL
10934 xmlDocPtr response = NULL;
10935 xmlChar *code = NULL, *status_message = NULL;
10936 xmlXPathContextPtr xpath_ctx = NULL;
10937 xmlXPathObjectPtr result = NULL;
10938 #endif
10940 if (!context) return IE_INVALID_CONTEXT;
10941 zfree(context->long_message);
10943 isds_hash_free(hash);
10945 #if HAVE_LIBCURL
10946 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10947 BAD_CAST "VerifyMessage", message_id,
10948 &response, NULL, NULL, &code, &status_message);
10949 if (err) goto leave;
10952 /* Extract data */
10953 xpath_ctx = xmlXPathNewContext(response);
10954 if (!xpath_ctx) {
10955 err = IE_ERROR;
10956 goto leave;
10958 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10959 err = IE_ERROR;
10960 goto leave;
10962 result = xmlXPathEvalExpression(
10963 BAD_CAST "/isds:VerifyMessageResponse",
10964 xpath_ctx);
10965 if (!result) {
10966 err = IE_ERROR;
10967 goto leave;
10969 /* Empty response */
10970 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10971 char *message_id_locale = _isds_utf82locale((char*) message_id);
10972 isds_printf_message(context,
10973 _("Server did not return any response for ID `%s' "
10974 "on VerifyMessage request"), message_id_locale);
10975 free(message_id_locale);
10976 err = IE_ISDS;
10977 goto leave;
10979 /* More responses */
10980 if (result->nodesetval->nodeNr > 1) {
10981 char *message_id_locale = _isds_utf82locale((char*) message_id);
10982 isds_printf_message(context,
10983 _("Server did return more responses for ID `%s' "
10984 "on VerifyMessage request"), message_id_locale);
10985 free(message_id_locale);
10986 err = IE_ISDS;
10987 goto leave;
10989 /* One response */
10990 xpath_ctx->node = result->nodesetval->nodeTab[0];
10992 /* Extract the hash */
10993 err = find_and_extract_DmHash(context, hash, xpath_ctx);
10995 leave:
10996 if (err) {
10997 isds_hash_free(hash);
11000 xmlXPathFreeObject(result);
11001 xmlXPathFreeContext(xpath_ctx);
11003 free(code);
11004 free(status_message);
11005 xmlFreeDoc(response);
11007 if (!err)
11008 isds_log(ILF_ISDS, ILL_DEBUG,
11009 _("VerifyMessage request processed by server "
11010 "successfully.\n")
11012 #else /* not HAVE_LIBCURL */
11013 err = IE_NOTSUP;
11014 #endif
11015 return err;
11019 /* Erase message specified by @message_id from long term storage. Other
11020 * message cannot be erased on user request.
11021 * @context is session context
11022 * @message_id is message identifier.
11023 * @incoming is true for incoming message, false for outgoing message.
11024 * @return
11025 * IE_SUCCESS if message has ben removed
11026 * IE_INVAL if message does not exist in long term storage or message
11027 * belongs to different box
11028 * TODO: IE_NOEPRM if user has no permission to erase a message */
11029 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11030 const char *message_id, _Bool incoming) {
11031 isds_error err = IE_SUCCESS;
11032 #if HAVE_LIBCURL
11033 xmlNodePtr request = NULL, node;
11034 xmlNsPtr isds_ns = NULL;
11035 xmlDocPtr response = NULL;
11036 xmlChar *code = NULL, *status_message = NULL;
11037 #endif
11039 if (!context) return IE_INVALID_CONTEXT;
11040 zfree(context->long_message);
11041 if (NULL == message_id) return IE_INVAL;
11043 /* Check if connection is established
11044 * TODO: This check should be done downstairs. */
11045 if (!context->curl) return IE_CONNECTION_CLOSED;
11047 #if HAVE_LIBCURL
11048 /* Build request */
11049 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11050 if (!request) {
11051 isds_log_message(context,
11052 _("Could build EraseMessage request"));
11053 return IE_ERROR;
11055 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11056 if(!isds_ns) {
11057 isds_log_message(context, _("Could not create ISDS name space"));
11058 xmlFreeNode(request);
11059 return IE_ERROR;
11061 xmlSetNs(request, isds_ns);
11063 err = validate_message_id_length(context, (xmlChar *) message_id);
11064 if (err) goto leave;
11065 INSERT_STRING(request, "dmID", message_id);
11067 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11070 /* Send request */
11071 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11072 "message ID %s to ISDS\n"), message_id);
11073 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11074 xmlFreeNode(request); request = NULL;
11076 if (err) {
11077 isds_log(ILF_ISDS, ILL_DEBUG,
11078 _("Processing ISDS response on EraseMessage request "
11079 "failed\n"));
11080 goto leave;
11083 /* Check for response status */
11084 err = isds_response_status(context, SERVICE_DM_INFO, response,
11085 &code, &status_message, NULL);
11086 if (err) {
11087 isds_log(ILF_ISDS, ILL_DEBUG,
11088 _("ISDS response on EraseMessage request is missing "
11089 "status\n"));
11090 goto leave;
11093 /* Check server status code */
11094 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11095 isds_log_message(context, _("Message to erase belongs to other box"));
11096 err = IE_INVAL;
11097 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11098 isds_log_message(context, _("Message to erase is not saved in "
11099 "long term storage or the direction does not match"));
11100 err = IE_INVAL;
11101 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11102 char *code_locale = _isds_utf82locale((char*) code);
11103 char *message_locale = _isds_utf82locale((char*) status_message);
11104 isds_log(ILF_ISDS, ILL_DEBUG,
11105 _("Server refused EraseMessage request "
11106 "(code=%s, message=%s)\n"),
11107 code_locale, message_locale);
11108 isds_log_message(context, message_locale);
11109 free(code_locale);
11110 free(message_locale);
11111 err = IE_ISDS;
11112 goto leave;
11115 leave:
11116 free(code);
11117 free(status_message);
11118 xmlFreeDoc(response);
11119 xmlFreeNode(request);
11121 if (!err)
11122 isds_log(ILF_ISDS, ILL_DEBUG,
11123 _("EraseMessage request processed by server "
11124 "successfully.\n")
11126 #else /* not HAVE_LIBCURL */
11127 err = IE_NOTSUP;
11128 #endif
11129 return err;
11133 /* Mark message as read. This is a transactional commit function to acknowledge
11134 * to ISDS the message has been downloaded and processed by client properly.
11135 * @context is session context
11136 * @message_id is message identifier. */
11137 isds_error isds_mark_message_read(struct isds_ctx *context,
11138 const char *message_id) {
11140 isds_error err = IE_SUCCESS;
11141 #if HAVE_LIBCURL
11142 xmlDocPtr response = NULL;
11143 xmlChar *code = NULL, *status_message = NULL;
11144 #endif
11146 if (!context) return IE_INVALID_CONTEXT;
11147 zfree(context->long_message);
11149 #if HAVE_LIBCURL
11150 /* Do request and check for success */
11151 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11152 BAD_CAST "MarkMessageAsDownloaded", message_id,
11153 &response, NULL, NULL, &code, &status_message);
11155 free(code);
11156 free(status_message);
11157 xmlFreeDoc(response);
11159 if (!err)
11160 isds_log(ILF_ISDS, ILL_DEBUG,
11161 _("MarkMessageAsDownloaded request processed by server "
11162 "successfully.\n")
11164 #else /* not HAVE_LIBCURL */
11165 err = IE_NOTSUP;
11166 #endif
11167 return err;
11171 /* Mark message as received by recipient. This is applicable only to
11172 * commercial message. Use envelope->dmType message member to distinguish
11173 * commercial message from government message. Government message is
11174 * received automatically (by law), commercial message on recipient request.
11175 * @context is session context
11176 * @message_id is message identifier. */
11177 isds_error isds_mark_message_received(struct isds_ctx *context,
11178 const char *message_id) {
11180 isds_error err = IE_SUCCESS;
11181 #if HAVE_LIBCURL
11182 xmlDocPtr response = NULL;
11183 xmlChar *code = NULL, *status_message = NULL;
11184 #endif
11186 if (!context) return IE_INVALID_CONTEXT;
11187 zfree(context->long_message);
11189 #if HAVE_LIBCURL
11190 /* Do request and check for success */
11191 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11192 BAD_CAST "ConfirmDelivery", message_id,
11193 &response, NULL, NULL, &code, &status_message);
11195 free(code);
11196 free(status_message);
11197 xmlFreeDoc(response);
11199 if (!err)
11200 isds_log(ILF_ISDS, ILL_DEBUG,
11201 _("ConfirmDelivery request processed by server "
11202 "successfully.\n")
11204 #else /* not HAVE_LIBCURL */
11205 err = IE_NOTSUP;
11206 #endif
11207 return err;
11211 /* Send document for authorized conversion into Czech POINT system.
11212 * This is public anonymous service, no log-in necessary. Special context is
11213 * used to reuse keep-a-live HTTPS connection.
11214 * @context is Czech POINT session context. DO NOT use context connected to
11215 * ISDS server. Use new context or context used by this function previously.
11216 * @document is document to convert. Only data, data_length, dmFileDescr and
11217 * is_xml members are significant. Be ware that not all document formats can be
11218 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11219 * @id is reallocated identifier assigned by Czech POINT system to
11220 * your document on submit. Use is to tell it to Czech POINT officer.
11221 * @date is reallocated document submit date (submitted documents
11222 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11223 * value. */
11224 isds_error czp_convert_document(struct isds_ctx *context,
11225 const struct isds_document *document,
11226 char **id, struct tm **date) {
11227 isds_error err = IE_SUCCESS;
11228 #if HAVE_LIBCURL
11229 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11230 xmlNodePtr request = NULL, node;
11231 xmlDocPtr response = NULL;
11233 xmlXPathContextPtr xpath_ctx = NULL;
11234 xmlXPathObjectPtr result = NULL;
11235 long int status = -1;
11236 long int *status_ptr = &status;
11237 char *string = NULL;
11238 #endif
11241 if (!context) return IE_INVALID_CONTEXT;
11242 zfree(context->long_message);
11243 if (!document || !id || !date) return IE_INVAL;
11245 if (document->is_xml) {
11246 isds_log_message(context,
11247 _("XML documents cannot be submitted to conversion"));
11248 return IE_NOTSUP;
11251 /* Free output arguments */
11252 zfree(*id);
11253 zfree(*date);
11255 #if HAVE_LIBCURL
11256 /* Store configuration */
11257 context->type = CTX_TYPE_CZP;
11258 free(context->url);
11259 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11260 if (!(context->url))
11261 return IE_NOMEM;
11263 /* Prepare CURL handle if not yet connected */
11264 if (!context->curl) {
11265 context->curl = curl_easy_init();
11266 if (!(context->curl))
11267 return IE_ERROR;
11270 /* Build conversion request */
11271 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11272 if (!request) {
11273 isds_log_message(context,
11274 _("Could not build Czech POINT conversion request"));
11275 return IE_ERROR;
11277 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11278 if(!deposit_ns) {
11279 isds_log_message(context,
11280 _("Could not create Czech POINT deposit name space"));
11281 xmlFreeNode(request);
11282 return IE_ERROR;
11284 xmlSetNs(request, deposit_ns);
11286 /* Insert children. They are in empty namespace! */
11287 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11288 if(!empty_ns) {
11289 isds_log_message(context, _("Could not create empty name space"));
11290 err = IE_ERROR;
11291 goto leave;
11293 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11294 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11295 document->dmFileDescr);
11297 /* Document encoded in Base64 */
11298 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11299 document->data, document->data_length);
11300 if (err) goto leave;
11302 isds_log(ILF_ISDS, ILL_DEBUG,
11303 _("Submitting document for conversion into Czech POINT deposit"));
11305 /* Send conversion request */
11306 err = _czp_czpdeposit(context, request, &response);
11307 xmlFreeNode(request); request = NULL;
11309 if (err) {
11310 czp_do_close_connection(context);
11311 goto leave;
11315 /* Extract response */
11316 xpath_ctx = xmlXPathNewContext(response);
11317 if (!xpath_ctx) {
11318 err = IE_ERROR;
11319 goto leave;
11321 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11322 err = IE_ERROR;
11323 goto leave;
11325 result = xmlXPathEvalExpression(
11326 BAD_CAST "/deposit:saveDocumentResponse/return",
11327 xpath_ctx);
11328 if (!result) {
11329 err = IE_ERROR;
11330 goto leave;
11332 /* Empty response */
11333 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11334 isds_printf_message(context,
11335 _("Missing `return' element in Czech POINT deposit response"));
11336 err = IE_ISDS;
11337 goto leave;
11339 /* More responses */
11340 if (result->nodesetval->nodeNr > 1) {
11341 isds_printf_message(context,
11342 _("Multiple `return' element in Czech POINT deposit response"));
11343 err = IE_ISDS;
11344 goto leave;
11346 /* One response */
11347 xpath_ctx->node = result->nodesetval->nodeTab[0];
11349 /* Get status */
11350 EXTRACT_LONGINT("status", status_ptr, 1);
11351 if (status) {
11352 EXTRACT_STRING("statusMsg", string);
11353 char *string_locale = _isds_utf82locale(string);
11354 isds_printf_message(context,
11355 _("Czech POINT deposit refused document for conversion "
11356 "(code=%ld, message=%s)"),
11357 status, string_locale);
11358 free(string_locale);
11359 err = IE_ISDS;
11360 goto leave;
11363 /* Get document ID */
11364 EXTRACT_STRING("documentID", *id);
11366 /* Get submit date */
11367 EXTRACT_STRING("dateInserted", string);
11368 if (string) {
11369 *date = calloc(1, sizeof(**date));
11370 if (!*date) {
11371 err = IE_NOMEM;
11372 goto leave;
11374 err = _isds_datestring2tm((xmlChar *)string, *date);
11375 if (err) {
11376 if (err == IE_NOTSUP) {
11377 err = IE_ISDS;
11378 char *string_locale = _isds_utf82locale(string);
11379 isds_printf_message(context,
11380 _("Invalid dateInserted value: %s"), string_locale);
11381 free(string_locale);
11383 goto leave;
11387 leave:
11388 free(string);
11389 xmlXPathFreeObject(result);
11390 xmlXPathFreeContext(xpath_ctx);
11392 xmlFreeDoc(response);
11393 xmlFreeNode(request);
11395 if (!err) {
11396 char *id_locale = _isds_utf82locale((char *) *id);
11397 isds_log(ILF_ISDS, ILL_DEBUG,
11398 _("Document %s has been submitted for conversion "
11399 "to server successfully\n"), id_locale);
11400 free(id_locale);
11402 #else /* not HAVE_LIBCURL */
11403 err = IE_NOTSUP;
11404 #endif
11405 return err;
11409 /* Close possibly opened connection to Czech POINT document deposit.
11410 * @context is Czech POINT session context. */
11411 isds_error czp_close_connection(struct isds_ctx *context) {
11412 if (!context) return IE_INVALID_CONTEXT;
11413 zfree(context->long_message);
11414 #if HAVE_LIBCURL
11415 return czp_do_close_connection(context);
11416 #else
11417 return IE_NOTSUP;
11418 #endif
11422 /* Send request for new box creation in testing ISDS instance.
11423 * It's not possible to request for a production box currently, as it
11424 * communicates via e-mail.
11425 * XXX: This function does not work either. Server complains about invalid
11426 * e-mail address.
11427 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11428 * this function
11429 * @context is special session context for box creation request. DO NOT use
11430 * standard context as it could reveal your password. Use fresh new context or
11431 * context previously used by this function.
11432 * @box is box description to create including single primary user (in case of
11433 * FO box type). It outputs box ID assigned by ISDS in dbID element.
11434 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11435 * box, or contact address of PFO box owner). The email member is mandatory as
11436 * it will be used to deliver credentials.
11437 * @former_names is former name of box owner. Pass NULL if you don't care.
11438 * @approval is optional external approval of box manipulation
11439 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11440 * NULL, if you don't care.*/
11441 isds_error isds_request_new_testing_box(struct isds_ctx *context,
11442 struct isds_DbOwnerInfo *box, const struct isds_list *users,
11443 const char *former_names, const struct isds_approval *approval,
11444 char **refnumber) {
11445 isds_error err = IE_SUCCESS;
11446 #if HAVE_LIBCURL
11447 xmlNodePtr request = NULL;
11448 xmlDocPtr response = NULL;
11449 xmlXPathContextPtr xpath_ctx = NULL;
11450 xmlXPathObjectPtr result = NULL;
11451 #endif
11454 if (!context) return IE_INVALID_CONTEXT;
11455 zfree(context->long_message);
11456 if (!box) return IE_INVAL;
11458 #if HAVE_LIBCURL
11459 if (!box->email || box->email[0] == '\0') {
11460 isds_log_message(context, _("E-mail field is mandatory"));
11461 return IE_INVAL;
11464 /* Scratch box ID */
11465 zfree(box->dbID);
11467 /* Store configuration */
11468 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
11469 free(context->url);
11470 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
11471 if (!(context->url))
11472 return IE_NOMEM;
11474 /* Prepare CURL handle if not yet connected */
11475 if (!context->curl) {
11476 context->curl = curl_easy_init();
11477 if (!(context->curl))
11478 return IE_ERROR;
11481 /* Build CreateDataBox request */
11482 err = build_CreateDBInput_request(context,
11483 &request, BAD_CAST "CreateDataBox",
11484 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
11485 if (err) goto leave;
11487 /* Send it to server and process response */
11488 err = send_destroy_request_check_response(context,
11489 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
11490 &response, (xmlChar **) refnumber, NULL);
11491 if (err) goto leave;
11493 /* Extract box ID */
11494 xpath_ctx = xmlXPathNewContext(response);
11495 if (!xpath_ctx) {
11496 err = IE_ERROR;
11497 goto leave;
11499 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11500 err = IE_ERROR;
11501 goto leave;
11503 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
11505 leave:
11506 xmlXPathFreeObject(result);
11507 xmlXPathFreeContext(xpath_ctx);
11508 xmlFreeDoc(response);
11509 xmlFreeNode(request);
11511 if (!err) {
11512 isds_log(ILF_ISDS, ILL_DEBUG,
11513 _("CreateDataBox request processed by server successfully.\n"));
11515 #else /* not HAVE_LIBCURL */
11516 err = IE_NOTSUP;
11517 #endif
11519 return err;
11523 /* Submit CMS signed message to ISDS to verify its originality. This is
11524 * stronger form of isds_verify_message_hash() because ISDS does more checks
11525 * than simple one (potentialy old weak) hash comparison.
11526 * @context is session context
11527 * @message is memory with raw CMS signed message bit stream
11528 * @length is @message size in bytes
11529 * @return
11530 * IE_SUCCESS if message originates in ISDS
11531 * IE_NOTEQUAL if message is unknown to ISDS
11532 * other code for other errors */
11533 isds_error isds_authenticate_message(struct isds_ctx *context,
11534 const void *message, size_t length) {
11535 isds_error err = IE_SUCCESS;
11536 #if HAVE_LIBCURL
11537 xmlNsPtr isds_ns = NULL;
11538 xmlNodePtr request = NULL;
11539 xmlDocPtr response = NULL;
11540 xmlXPathContextPtr xpath_ctx = NULL;
11541 xmlXPathObjectPtr result = NULL;
11542 _Bool *authentic = NULL;
11543 #endif
11545 if (!context) return IE_INVALID_CONTEXT;
11546 zfree(context->long_message);
11547 if (!message || length == 0) return IE_INVAL;
11549 #if HAVE_LIBCURL
11550 /* Check if connection is established
11551 * TODO: This check should be done downstairs. */
11552 if (!context->curl) return IE_CONNECTION_CLOSED;
11555 /* Build AuthenticateMessage request */
11556 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11557 if (!request) {
11558 isds_log_message(context,
11559 _("Could not build AuthenticateMessage request"));
11560 return IE_ERROR;
11562 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11563 if(!isds_ns) {
11564 isds_log_message(context, _("Could not create ISDS name space"));
11565 xmlFreeNode(request);
11566 return IE_ERROR;
11568 xmlSetNs(request, isds_ns);
11570 /* Insert Base64 encoded message */
11571 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11572 message, length);
11573 if (err) goto leave;
11575 /* Send request to server and process response */
11576 err = send_destroy_request_check_response(context,
11577 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11578 &response, NULL, NULL);
11579 if (err) goto leave;
11582 /* ISDS has decided */
11583 xpath_ctx = xmlXPathNewContext(response);
11584 if (!xpath_ctx) {
11585 err = IE_ERROR;
11586 goto leave;
11588 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11589 err = IE_ERROR;
11590 goto leave;
11593 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11595 if (!authentic) {
11596 isds_log_message(context,
11597 _("Server did not return any response on "
11598 "AuthenticateMessage request"));
11599 err = IE_ISDS;
11600 goto leave;
11602 if (*authentic) {
11603 isds_log(ILF_ISDS, ILL_DEBUG,
11604 _("ISDS authenticated the message successfully\n"));
11605 } else {
11606 isds_log_message(context, _("ISDS does not know the message"));
11607 err = IE_NOTEQUAL;
11611 leave:
11612 free(authentic);
11613 xmlXPathFreeObject(result);
11614 xmlXPathFreeContext(xpath_ctx);
11616 xmlFreeDoc(response);
11617 xmlFreeNode(request);
11618 #else /* not HAVE_LIBCURL */
11619 err = IE_NOTSUP;
11620 #endif
11622 return err;
11626 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11627 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11628 * be re-signed.
11629 * @context is session context
11630 * @input_data is memory with raw CMS signed message or delivery info bit
11631 * stream to re-sign
11632 * @input_length is @input_data size in bytes
11633 * @output_data is pointer to auto-allocated memory where to store re-signed
11634 * input data blob. Caller must free it.
11635 * @output_data is pointer where to store @output_data size in bytes
11636 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11637 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11638 * @return
11639 * IE_SUCCESS if CMS blob has been re-signed successfully
11640 * other code for other errors */
11641 isds_error isds_resign_message(struct isds_ctx *context,
11642 const void *input_data, size_t input_length,
11643 void **output_data, size_t *output_length, struct tm **valid_to) {
11644 isds_error err = IE_SUCCESS;
11645 #if HAVE_LIBCURL
11646 xmlNsPtr isds_ns = NULL;
11647 xmlNodePtr request = NULL;
11648 xmlDocPtr response = NULL;
11649 xmlXPathContextPtr xpath_ctx = NULL;
11650 xmlXPathObjectPtr result = NULL;
11651 char *string = NULL;
11652 const xmlChar *codes[] = {
11653 BAD_CAST "2200",
11654 BAD_CAST "2201",
11655 BAD_CAST "2204",
11656 BAD_CAST "2207",
11657 NULL
11659 const char *meanings[] = {
11660 "Message is bad",
11661 "Message is not original",
11662 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11663 "Time stamp could not been generated in time"
11665 const isds_error errors[] = {
11666 IE_INVAL,
11667 IE_NOTUNIQ,
11668 IE_INVAL,
11669 IE_ISDS,
11671 struct code_map_isds_error map = {
11672 .codes = codes,
11673 .meanings = meanings,
11674 .errors = errors
11676 #endif
11678 if (NULL != output_data) *output_data = NULL;
11679 if (NULL != output_length) *output_length = 0;
11680 if (NULL != valid_to) *valid_to = NULL;
11682 if (NULL == context) return IE_INVALID_CONTEXT;
11683 zfree(context->long_message);
11684 if (NULL == input_data || 0 == input_length) {
11685 isds_log_message(context, _("Empty CMS blob on input"));
11686 return IE_INVAL;
11688 if (NULL == output_data || NULL == output_length) {
11689 isds_log_message(context,
11690 _("NULL pointer provided for output CMS blob"));
11691 return IE_INVAL;
11694 #if HAVE_LIBCURL
11695 /* Check if connection is established
11696 * TODO: This check should be done downstairs. */
11697 if (!context->curl) return IE_CONNECTION_CLOSED;
11700 /* Build Re-signISDSDocument request */
11701 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11702 if (!request) {
11703 isds_log_message(context,
11704 _("Could not build Re-signISDSDocument request"));
11705 return IE_ERROR;
11707 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11708 if(!isds_ns) {
11709 isds_log_message(context, _("Could not create ISDS name space"));
11710 xmlFreeNode(request);
11711 return IE_ERROR;
11713 xmlSetNs(request, isds_ns);
11715 /* Insert Base64 encoded CMS blob */
11716 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11717 input_data, input_length);
11718 if (err) goto leave;
11720 /* Send request to server and process response */
11721 err = send_destroy_request_check_response(context,
11722 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11723 &response, NULL, &map);
11724 if (err) goto leave;
11727 /* Extract re-signed data */
11728 xpath_ctx = xmlXPathNewContext(response);
11729 if (!xpath_ctx) {
11730 err = IE_ERROR;
11731 goto leave;
11733 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11734 err = IE_ERROR;
11735 goto leave;
11737 result = xmlXPathEvalExpression(
11738 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11739 if (!result) {
11740 err = IE_ERROR;
11741 goto leave;
11743 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11744 isds_log_message(context,
11745 _("Missing Re-signISDSDocumentResponse element"));
11746 err = IE_ISDS;
11747 goto leave;
11749 if (result->nodesetval->nodeNr > 1) {
11750 isds_log_message(context,
11751 _("Multiple Re-signISDSDocumentResponse element"));
11752 err = IE_ISDS;
11753 goto leave;
11755 xpath_ctx->node = result->nodesetval->nodeTab[0];
11756 xmlXPathFreeObject(result); result = NULL;
11758 EXTRACT_STRING("isds:dmResultDoc", string);
11759 /* Decode non-empty data */
11760 if (NULL != string && string[0] != '\0') {
11761 *output_length = _isds_b64decode(string, output_data);
11762 if (*output_length == (size_t) -1) {
11763 isds_log_message(context,
11764 _("Error while Base64-decoding re-signed data"));
11765 err = IE_ERROR;
11766 goto leave;
11768 } else {
11769 isds_log_message(context, _("Server did not send re-signed data"));
11770 err = IE_ISDS;
11771 goto leave;
11773 zfree(string);
11775 if (NULL != valid_to) {
11776 /* Get time stamp expiration date */
11777 EXTRACT_STRING("isds:dmValidTo", string);
11778 if (NULL != string) {
11779 *valid_to = calloc(1, sizeof(**valid_to));
11780 if (!*valid_to) {
11781 err = IE_NOMEM;
11782 goto leave;
11784 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11785 if (err) {
11786 if (err == IE_NOTSUP) {
11787 err = IE_ISDS;
11788 char *string_locale = _isds_utf82locale(string);
11789 isds_printf_message(context,
11790 _("Invalid dmValidTo value: %s"), string_locale);
11791 free(string_locale);
11793 goto leave;
11798 leave:
11799 free(string);
11801 xmlXPathFreeObject(result);
11802 xmlXPathFreeContext(xpath_ctx);
11804 xmlFreeDoc(response);
11805 xmlFreeNode(request);
11806 #else /* not HAVE_LIBCURL */
11807 err = IE_NOTSUP;
11808 #endif
11810 return err;
11813 #undef INSERT_ELEMENT
11814 #undef CHECK_FOR_STRING_LENGTH
11815 #undef INSERT_STRING_ATTRIBUTE
11816 #undef INSERT_ULONGINTNOPTR
11817 #undef INSERT_ULONGINT
11818 #undef INSERT_LONGINT
11819 #undef INSERT_BOOLEAN
11820 #undef INSERT_SCALAR_BOOLEAN
11821 #undef INSERT_STRING
11822 #undef INSERT_STRING_WITH_NS
11823 #undef EXTRACT_STRING_ATTRIBUTE
11824 #undef EXTRACT_ULONGINT
11825 #undef EXTRACT_LONGINT
11826 #undef EXTRACT_BOOLEAN
11827 #undef EXTRACT_STRING
11830 /* Compute hash of message from raw representation and store it into envelope.
11831 * Original hash structure will be destroyed in envelope.
11832 * @context is session context
11833 * @message is message carrying raw XML message blob
11834 * @algorithm is desired hash algorithm to use */
11835 isds_error isds_compute_message_hash(struct isds_ctx *context,
11836 struct isds_message *message, const isds_hash_algorithm algorithm) {
11837 isds_error err = IE_SUCCESS;
11838 const char *nsuri;
11839 void *xml_stream = NULL;
11840 size_t xml_stream_length;
11841 size_t phys_start, phys_end;
11842 char *phys_path = NULL;
11843 struct isds_hash *new_hash = NULL;
11846 if (!context) return IE_INVALID_CONTEXT;
11847 zfree(context->long_message);
11848 if (!message) return IE_INVAL;
11850 if (!message->raw) {
11851 isds_log_message(context,
11852 _("Message does not carry raw representation"));
11853 return IE_INVAL;
11856 switch (message->raw_type) {
11857 case RAWTYPE_INCOMING_MESSAGE:
11858 nsuri = ISDS_NS;
11859 xml_stream = message->raw;
11860 xml_stream_length = message->raw_length;
11861 break;
11863 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11864 nsuri = SISDS_INCOMING_NS;
11865 xml_stream = message->raw;
11866 xml_stream_length = message->raw_length;
11867 break;
11869 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11870 nsuri = SISDS_INCOMING_NS;
11871 err = _isds_extract_cms_data(context,
11872 message->raw, message->raw_length,
11873 &xml_stream, &xml_stream_length);
11874 if (err) goto leave;
11875 break;
11877 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11878 nsuri = SISDS_OUTGOING_NS;
11879 xml_stream = message->raw;
11880 xml_stream_length = message->raw_length;
11881 break;
11883 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11884 nsuri = SISDS_OUTGOING_NS;
11885 err = _isds_extract_cms_data(context,
11886 message->raw, message->raw_length,
11887 &xml_stream, &xml_stream_length);
11888 if (err) goto leave;
11889 break;
11891 default:
11892 isds_log_message(context, _("Bad raw representation type"));
11893 return IE_INVAL;
11894 break;
11898 /* XXX: Hash is computed from original string representing isds:dmDm
11899 * subtree. That means no encoding, white space, xmlns attributes changes.
11900 * In other words, input for hash can be invalid XML stream. */
11901 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
11902 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11903 PHYSXML_ELEMENT_SEPARATOR,
11904 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
11905 PHYSXML_ELEMENT_SEPARATOR
11906 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
11907 err = IE_NOMEM;
11908 goto leave;
11910 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11911 phys_path, &phys_start, &phys_end);
11912 zfree(phys_path);
11913 if (err) {
11914 isds_log_message(context,
11915 _("Substring with isds:dmDM element could not be located "
11916 "in raw message"));
11917 goto leave;
11921 /* Compute hash */
11922 new_hash = calloc(1, sizeof(*new_hash));
11923 if (!new_hash) {
11924 err = IE_NOMEM;
11925 goto leave;
11927 new_hash->algorithm = algorithm;
11928 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
11929 new_hash);
11930 if (err) {
11931 isds_log_message(context, _("Could not compute message hash"));
11932 goto leave;
11935 /* Save computed hash */
11936 if (!message->envelope) {
11937 message->envelope = calloc(1, sizeof(*message->envelope));
11938 if (!message->envelope) {
11939 err = IE_NOMEM;
11940 goto leave;
11943 isds_hash_free(&message->envelope->hash);
11944 message->envelope->hash = new_hash;
11946 leave:
11947 if (err) {
11948 isds_hash_free(&new_hash);
11951 free(phys_path);
11952 if (xml_stream != message->raw) free(xml_stream);
11953 return err;
11957 /* Compare two hashes.
11958 * @h1 is first hash
11959 * @h2 is another hash
11960 * @return
11961 * IE_SUCCESS if hashes equal
11962 * IE_NOTUNIQ if hashes are comparable, but they don't equal
11963 * IE_ENUM if not comparable, but both structures defined
11964 * IE_INVAL if some of the structures are undefined (NULL)
11965 * IE_ERROR if internal error occurs */
11966 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
11967 if (h1 == NULL || h2 == NULL) return IE_INVAL;
11968 if (h1->algorithm != h2->algorithm) return IE_ENUM;
11969 if (h1->length != h2->length) return IE_ERROR;
11970 if (h1->length > 0 && !h1->value) return IE_ERROR;
11971 if (h2->length > 0 && !h2->value) return IE_ERROR;
11973 for (int i = 0; i < h1->length; i++) {
11974 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
11975 return IE_NOTEQUAL;
11977 return IE_SUCCESS;
11981 /* Check message has gone through ISDS by comparing message hash stored in
11982 * ISDS and locally computed hash. You must provide message with valid raw
11983 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
11984 * This is convenient wrapper for isds_download_message_hash(),
11985 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
11986 * @context is session context
11987 * @message is message with valid raw and envelope member; envelope->hash
11988 * member will be changed during function run. Use envelope on heap only.
11989 * @return
11990 * IE_SUCCESS if message originates in ISDS
11991 * IE_NOTEQUAL if message is unknown to ISDS
11992 * other code for other errors */
11993 isds_error isds_verify_message_hash(struct isds_ctx *context,
11994 struct isds_message *message) {
11995 isds_error err = IE_SUCCESS;
11996 struct isds_hash *downloaded_hash = NULL;
11998 if (!context) return IE_INVALID_CONTEXT;
11999 zfree(context->long_message);
12000 if (!message) return IE_INVAL;
12002 if (!message->envelope) {
12003 isds_log_message(context,
12004 _("Given message structure is missing envelope"));
12005 return IE_INVAL;
12007 if (!message->raw) {
12008 isds_log_message(context,
12009 _("Given message structure is missing raw representation"));
12010 return IE_INVAL;
12013 err = isds_download_message_hash(context, message->envelope->dmID,
12014 &downloaded_hash);
12015 if (err) goto leave;
12017 err = isds_compute_message_hash(context, message,
12018 downloaded_hash->algorithm);
12019 if (err) goto leave;
12021 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12023 leave:
12024 isds_hash_free(&downloaded_hash);
12025 return err;
12029 /* Search for document by document ID in list of documents. IDs are compared
12030 * as UTF-8 string.
12031 * @documents is list of isds_documents
12032 * @id is document identifier
12033 * @return first matching document or NULL. */
12034 const struct isds_document *isds_find_document_by_id(
12035 const struct isds_list *documents, const char *id) {
12036 const struct isds_list *item;
12037 const struct isds_document *document;
12039 for (item = documents; item; item = item->next) {
12040 document = (struct isds_document *) item->data;
12041 if (!document) continue;
12043 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12044 return document;
12047 return NULL;
12051 /* Normalize @mime_type to be proper MIME type.
12052 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12053 * guess regular MIME type (e.g. "application/pdf").
12054 * @mime_type is UTF-8 encoded MIME type to fix
12055 * @return original @mime_type if no better interpretation exists, or
12056 * constant static UTF-8 encoded string with proper MIME type. */
12057 const char *isds_normalize_mime_type(const char *mime_type) {
12058 if (!mime_type) return NULL;
12060 for (int offset = 0;
12061 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12062 offset += 2) {
12063 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12064 extension_map_mime[offset]))
12065 return (const char *) extension_map_mime[offset + 1];
12068 return mime_type;
12072 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12073 struct isds_message **message);
12074 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12075 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12076 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12077 struct isds_address **address);
12079 int isds_message_free(struct isds_message **message);
12080 int isds_address_free(struct isds_address **address);
12084 /* Makes known all relevant namespaces to given XPath context
12085 * @xpath_ctx is XPath context
12086 * @message_ns selects proper message name space. Unsigned and signed
12087 * messages and delivery info's differ in prefix and URI. */
12088 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12089 const message_ns_type message_ns) {
12090 const xmlChar *message_namespace = NULL;
12092 if (!xpath_ctx) return IE_ERROR;
12094 switch(message_ns) {
12095 case MESSAGE_NS_1:
12096 message_namespace = BAD_CAST ISDS1_NS; break;
12097 case MESSAGE_NS_UNSIGNED:
12098 message_namespace = BAD_CAST ISDS_NS; break;
12099 case MESSAGE_NS_SIGNED_INCOMING:
12100 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12101 case MESSAGE_NS_SIGNED_OUTGOING:
12102 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12103 case MESSAGE_NS_SIGNED_DELIVERY:
12104 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12105 default:
12106 return IE_ENUM;
12109 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12110 return IE_ERROR;
12111 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12112 return IE_ERROR;
12113 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12114 return IE_ERROR;
12115 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12116 return IE_ERROR;
12117 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12118 return IE_ERROR;
12119 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12120 return IE_ERROR;
12121 return IE_SUCCESS;