Correct isds_find_box_by_fulltext() prototype
[libisds.git] / src / isds.c
blobbb1431c8bbb718adf9cf6f4c7e670f8cb604b9d7
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;
7691 /* Extract counters */
7692 if (NULL != total_matching_boxes) {
7693 EXTRACT_ULONGINT("isds:totalCount", total_matching_boxes, 1);
7695 if (NULL != current_page_size) {
7696 EXTRACT_ULONGINT("isds:currentCount", current_page_size, 1);
7698 if (NULL != current_page_beginning) {
7699 EXTRACT_ULONGINT("isds:position", current_page_beginning, 1);
7701 if (NULL != last_page) {
7702 EXTRACT_BOOLEAN("isds:lastPage", last_page);
7705 /* Extract boxes if they present */
7706 result = xmlXPathEvalExpression(BAD_CAST
7707 "/isds:ISDSSearch2/isds:dbResults/isds:dbResult", xpath_ctx);
7708 if (NULL == result) {
7709 err = IE_ERROR;
7710 goto leave;
7712 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7713 struct isds_list *item, *prev_item = NULL;
7714 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7715 item = calloc(1, sizeof(*item));
7716 if (!item) {
7717 err = IE_NOMEM;
7718 goto leave;
7721 item->destructor = (void (*)(void **))isds_fulltext_result_free;
7722 if (i == 0) *boxes = item;
7723 else prev_item->next = item;
7724 prev_item = item;
7726 xpath_ctx->node = result->nodesetval->nodeTab[i];
7727 err = extract_dbResult(context,
7728 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
7729 (NULL == track_matches) ? 0 : *track_matches);
7730 if (err) goto leave;
7734 leave:
7735 if (err) {
7736 isds_list_free(boxes);
7739 free(string);
7740 xmlFreeNode(request);
7741 xmlXPathFreeObject(result);
7742 xmlXPathFreeContext(xpath_ctx);
7743 xmlFreeDoc(response);
7745 if (!err)
7746 isds_log(ILF_ISDS, ILL_DEBUG,
7747 _("ISDSSearch2 request processed by server successfully.\n"));
7748 #else /* not HAVE_LIBCURL */
7749 err = IE_NOTSUP;
7750 #endif
7752 return err;
7756 /* Get status of a box.
7757 * @context is ISDS session context.
7758 * @box_id is UTF-8 encoded box identifier as zero terminated string
7759 * @box_status is return value of box status.
7760 * @return:
7761 * IE_SUCCESS if box has been found and its status retrieved
7762 * IE_NOEXIST if box is not known to ISDS server
7763 * or other appropriate error.
7764 * You can use isds_DbState to enumerate box status. However out of enum
7765 * range value can be returned too. This is feature because ISDS
7766 * specification leaves the set of values open.
7767 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7768 * the box has been deleted, but ISDS still lists its former existence. */
7769 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7770 long int *box_status) {
7771 isds_error err = IE_SUCCESS;
7772 #if HAVE_LIBCURL
7773 xmlNsPtr isds_ns = NULL;
7774 xmlNodePtr request = NULL, db_id;
7775 xmlDocPtr response = NULL;
7776 xmlXPathContextPtr xpath_ctx = NULL;
7777 xmlXPathObjectPtr result = NULL;
7778 xmlChar *string = NULL;
7780 const xmlChar *codes[] = {
7781 BAD_CAST "5001",
7782 BAD_CAST "1007",
7783 BAD_CAST "2011",
7784 NULL
7786 const char *meanings[] = {
7787 "The box does not exist",
7788 "Box ID is malformed",
7789 "Box ID malformed",
7791 const isds_error errors[] = {
7792 IE_NOEXIST,
7793 IE_INVAL,
7794 IE_INVAL,
7796 struct code_map_isds_error map = {
7797 .codes = codes,
7798 .meanings = meanings,
7799 .errors = errors
7801 #endif
7803 if (!context) return IE_INVALID_CONTEXT;
7804 zfree(context->long_message);
7805 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7807 #if HAVE_LIBCURL
7808 /* Check if connection is established
7809 * TODO: This check should be done downstairs. */
7810 if (!context->curl) return IE_CONNECTION_CLOSED;
7813 /* Build CheckDataBox request */
7814 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7815 if (!request) {
7816 isds_log_message(context,
7817 _("Could build CheckDataBox request"));
7818 return IE_ERROR;
7820 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7821 if(!isds_ns) {
7822 isds_log_message(context, _("Could not create ISDS name space"));
7823 xmlFreeNode(request);
7824 return IE_ERROR;
7826 xmlSetNs(request, isds_ns);
7827 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7828 if (!db_id) {
7829 isds_log_message(context, _("Could not add dbID child to "
7830 "CheckDataBox element"));
7831 xmlFreeNode(request);
7832 return IE_ERROR;
7836 /* Send request and check response*/
7837 err = send_destroy_request_check_response(context,
7838 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7839 &request, &response, NULL, &map);
7840 if (err) goto leave;
7843 /* Extract data */
7844 xpath_ctx = xmlXPathNewContext(response);
7845 if (!xpath_ctx) {
7846 err = IE_ERROR;
7847 goto leave;
7849 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7850 err = IE_ERROR;
7851 goto leave;
7853 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7854 xpath_ctx);
7855 if (!result) {
7856 err = IE_ERROR;
7857 goto leave;
7859 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7860 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7861 err = IE_ISDS;
7862 goto leave;
7864 if (result->nodesetval->nodeNr > 1) {
7865 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7866 err = IE_ISDS;
7867 goto leave;
7869 xpath_ctx->node = result->nodesetval->nodeTab[0];
7870 xmlXPathFreeObject(result); result = NULL;
7872 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7875 leave:
7876 free(string);
7877 xmlXPathFreeObject(result);
7878 xmlXPathFreeContext(xpath_ctx);
7880 xmlFreeDoc(response);
7882 if (!err)
7883 isds_log(ILF_ISDS, ILL_DEBUG,
7884 _("CheckDataBox request processed by server successfully.\n"));
7885 #else /* not HAVE_LIBCURL */
7886 err = IE_NOTSUP;
7887 #endif
7889 return err;
7893 /* Get list of permissions to send commercial messages.
7894 * @context is ISDS session context.
7895 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7896 * @permissions is a reallocated list of permissions (struct
7897 * isds_commercial_permission*) to send commercial messages from @box_id. The
7898 * order of permissions is significant as the server applies the permissions
7899 * and associated pre-paid credits in the order. Empty list means no
7900 * permission.
7901 * @return:
7902 * IE_SUCCESS if the list has been obtained correctly,
7903 * or other appropriate error. */
7904 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7905 const char *box_id, struct isds_list **permissions) {
7906 isds_error err = IE_SUCCESS;
7907 #if HAVE_LIBCURL
7908 xmlDocPtr response = NULL;
7909 xmlXPathContextPtr xpath_ctx = NULL;
7910 xmlXPathObjectPtr result = NULL;
7911 #endif
7913 if (!context) return IE_INVALID_CONTEXT;
7914 zfree(context->long_message);
7915 if (NULL == permissions) return IE_INVAL;
7916 isds_list_free(permissions);
7917 if (NULL == box_id) return IE_INVAL;
7919 #if HAVE_LIBCURL
7920 /* Check if connection is established */
7921 if (!context->curl) return IE_CONNECTION_CLOSED;
7923 /* Do request and check for success */
7924 err = build_send_dbid_request_check_response(context,
7925 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7926 BAD_CAST box_id, NULL, &response, NULL);
7927 if (!err) {
7928 isds_log(ILF_ISDS, ILL_DEBUG,
7929 _("PDZInfo request processed by server successfully.\n"));
7932 /* Extract data */
7933 /* Prepare structure */
7934 xpath_ctx = xmlXPathNewContext(response);
7935 if (!xpath_ctx) {
7936 err = IE_ERROR;
7937 goto leave;
7939 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7940 err = IE_ERROR;
7941 goto leave;
7944 /* Set context node */
7945 result = xmlXPathEvalExpression(BAD_CAST
7946 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7947 xpath_ctx);
7948 if (!result) {
7949 err = IE_ERROR;
7950 goto leave;
7952 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7953 struct isds_list *prev_item = NULL;
7955 /* Iterate over all permission records */
7956 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7957 struct isds_list *item;
7959 /* Prepare structure */
7960 item = calloc(1, sizeof(*item));
7961 if (!item) {
7962 err = IE_NOMEM;
7963 goto leave;
7965 item->destructor = (void(*)(void**))isds_commercial_permission_free;
7966 if (i == 0) *permissions = item;
7967 else prev_item->next = item;
7968 prev_item = item;
7970 /* Extract it */
7971 xpath_ctx->node = result->nodesetval->nodeTab[i];
7972 err = extract_DbPDZRecord(context,
7973 (struct isds_commercial_permission **) (&item->data),
7974 xpath_ctx);
7975 if (err) goto leave;
7979 leave:
7980 if (err) {
7981 isds_list_free(permissions);
7984 xmlXPathFreeObject(result);
7985 xmlXPathFreeContext(xpath_ctx);
7986 xmlFreeDoc(response);
7988 #else /* not HAVE_LIBCURL */
7989 err = IE_NOTSUP;
7990 #endif
7992 return err;
7996 /* Get details about credit for sending pre-paid commercial messages.
7997 * @context is ISDS session context.
7998 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
7999 * @from_date is first day of credit history to return in @history. Only
8000 * tm_year, tm_mon and tm_mday carry sane value.
8001 * @to_date is last day of credit history to return in @history. Only
8002 * tm_year, tm_mon and tm_mday carry sane value.
8003 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8004 * if you don't care. This and all other credit values are integers in
8005 * hundredths of Czech Crowns.
8006 * @email outputs notification e-mail address where notifications about credit
8007 * are sent. This is automatically reallocated string. Pass NULL if you don't
8008 * care. It can return NULL if no address is defined.
8009 * @history outputs auto-reallocated list of pointers to struct
8010 * isds_credit_event. Events in closed interval @from_time to @to_time are
8011 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8012 * are sorted by time.
8013 * @return:
8014 * IE_SUCCESS if the credit details have been obtained correctly,
8015 * or other appropriate error. Please note that server allows to retrieve
8016 * only limited history of events. */
8017 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8018 const char *box_id,
8019 const struct tm *from_date, const struct tm *to_date,
8020 long int *credit, char **email, struct isds_list **history) {
8021 isds_error err = IE_SUCCESS;
8022 #if HAVE_LIBCURL
8023 char *box_id_locale = NULL;
8024 xmlNodePtr request = NULL, node;
8025 xmlNsPtr isds_ns = NULL;
8026 xmlChar *string = NULL;
8028 xmlDocPtr response = NULL;
8029 xmlXPathContextPtr xpath_ctx = NULL;
8030 xmlXPathObjectPtr result = NULL;
8032 const xmlChar *codes[] = {
8033 BAD_CAST "1004",
8034 BAD_CAST "2011",
8035 BAD_CAST "1093",
8036 BAD_CAST "1137",
8037 BAD_CAST "1058",
8038 NULL
8040 const char *meanings[] = {
8041 "Insufficient priviledges for the box",
8042 "The box does not exist",
8043 "Date is too long (history is not available after 15 months)",
8044 "Interval is too long (limit is 3 months)",
8045 "Invalid date"
8047 const isds_error errors[] = {
8048 IE_ISDS,
8049 IE_NOEXIST,
8050 IE_DATE,
8051 IE_DATE,
8052 IE_DATE,
8054 struct code_map_isds_error map = {
8055 .codes = codes,
8056 .meanings = meanings,
8057 .errors = errors
8059 #endif
8061 if (!context) return IE_INVALID_CONTEXT;
8062 zfree(context->long_message);
8064 /* Free output argument */
8065 if (NULL != credit) *credit = 0;
8066 if (NULL != email) zfree(*email);
8067 isds_list_free(history);
8069 if (NULL == box_id) return IE_INVAL;
8071 #if HAVE_LIBCURL
8072 /* Check if connection is established */
8073 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8075 box_id_locale = _isds_utf82locale((char*)box_id);
8076 if (NULL == box_id_locale) {
8077 err = IE_NOMEM;
8078 goto leave;
8081 /* Build request */
8082 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8083 if (NULL == request) {
8084 isds_printf_message(context,
8085 _("Could not build DataBoxCreditInfo request for %s box"),
8086 box_id_locale);
8087 err = IE_ERROR;
8088 goto leave;
8090 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8091 if(!isds_ns) {
8092 isds_log_message(context, _("Could not create ISDS name space"));
8093 err = IE_ERROR;
8094 goto leave;
8096 xmlSetNs(request, isds_ns);
8098 /* Add mandatory XSD:tIdDbInput child */
8099 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8100 /* Add mandatory dates elements with optional values */
8101 if (from_date) {
8102 err = tm2datestring(from_date, &string);
8103 if (err) {
8104 isds_log_message(context,
8105 _("Could not convert `from_date' argument to ISO date "
8106 "string"));
8107 goto leave;
8109 INSERT_STRING(request, "ciFromDate", string);
8110 zfree(string);
8111 } else {
8112 INSERT_STRING(request, "ciFromDate", NULL);
8114 if (to_date) {
8115 err = tm2datestring(to_date, &string);
8116 if (err) {
8117 isds_log_message(context,
8118 _("Could not convert `to_date' argument to ISO date "
8119 "string"));
8120 goto leave;
8122 INSERT_STRING(request, "ciTodate", string);
8123 zfree(string);
8124 } else {
8125 INSERT_STRING(request, "ciTodate", NULL);
8128 /* Send request and check response*/
8129 err = send_destroy_request_check_response(context,
8130 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8131 &request, &response, NULL, &map);
8132 if (err) goto leave;
8135 /* Extract data */
8136 /* Set context to the root */
8137 xpath_ctx = xmlXPathNewContext(response);
8138 if (!xpath_ctx) {
8139 err = IE_ERROR;
8140 goto leave;
8142 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8143 err = IE_ERROR;
8144 goto leave;
8146 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8147 xpath_ctx);
8148 if (!result) {
8149 err = IE_ERROR;
8150 goto leave;
8152 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8153 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8154 err = IE_ISDS;
8155 goto leave;
8157 if (result->nodesetval->nodeNr > 1) {
8158 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8159 err = IE_ISDS;
8160 goto leave;
8162 xpath_ctx->node = result->nodesetval->nodeTab[0];
8163 xmlXPathFreeObject(result); result = NULL;
8165 /* Extract common data */
8166 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8167 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8169 /* Extract records */
8170 if (NULL == history) goto leave;
8171 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8172 xpath_ctx);
8173 if (!result) {
8174 err = IE_ERROR;
8175 goto leave;
8177 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8178 struct isds_list *prev_item = NULL;
8180 /* Iterate over all records */
8181 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
8182 struct isds_list *item;
8184 /* Prepare structure */
8185 item = calloc(1, sizeof(*item));
8186 if (!item) {
8187 err = IE_NOMEM;
8188 goto leave;
8190 item->destructor = (void(*)(void**))isds_credit_event_free;
8191 if (i == 0) *history = item;
8192 else prev_item->next = item;
8193 prev_item = item;
8195 /* Extract it */
8196 xpath_ctx->node = result->nodesetval->nodeTab[i];
8197 err = extract_CiRecord(context,
8198 (struct isds_credit_event **) (&item->data),
8199 xpath_ctx);
8200 if (err) goto leave;
8204 leave:
8205 if (!err) {
8206 isds_log(ILF_ISDS, ILL_DEBUG,
8207 _("DataBoxCreditInfo request processed by server successfully.\n"));
8209 if (err) {
8210 isds_list_free(history);
8211 if (NULL != email) zfree(*email)
8214 free(box_id_locale);
8215 xmlXPathFreeObject(result);
8216 xmlXPathFreeContext(xpath_ctx);
8217 xmlFreeDoc(response);
8219 #else /* not HAVE_LIBCURL */
8220 err = IE_NOTSUP;
8221 #endif
8223 return err;
8227 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8228 * code, destroy response and log success.
8229 * @context is ISDS session context.
8230 * @service_name is name of SERVICE_DB_MANIPULATION service
8231 * @box_id is UTF-8 encoded box identifier as zero terminated string
8232 * @approval is optional external approval of box manipulation
8233 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8234 * NULL, if you don't care. */
8235 static isds_error build_send_manipulationdbid_request_check_drop_response(
8236 struct isds_ctx *context, const xmlChar *service_name,
8237 const xmlChar *box_id, const struct isds_approval *approval,
8238 xmlChar **refnumber) {
8239 isds_error err = IE_SUCCESS;
8240 #if HAVE_LIBCURL
8241 xmlDocPtr response = NULL;
8242 #endif
8244 if (!context) return IE_INVALID_CONTEXT;
8245 zfree(context->long_message);
8246 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8248 #if HAVE_LIBCURL
8249 /* Check if connection is established */
8250 if (!context->curl) return IE_CONNECTION_CLOSED;
8252 /* Do request and check for success */
8253 err = build_send_dbid_request_check_response(context,
8254 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8255 &response, refnumber);
8256 xmlFreeDoc(response);
8258 if (!err) {
8259 char *service_name_locale = _isds_utf82locale((char *) service_name);
8260 isds_log(ILF_ISDS, ILL_DEBUG,
8261 _("%s request processed by server successfully.\n"),
8262 service_name_locale);
8263 free(service_name_locale);
8265 #else /* not HAVE_LIBCURL */
8266 err = IE_NOTSUP;
8267 #endif
8269 return err;
8273 /* Switch box into state where box can receive commercial messages (off by
8274 * default)
8275 * @context is ISDS session context.
8276 * @box_id is UTF-8 encoded box identifier as zero terminated string
8277 * @allow is true for enable, false for disable commercial messages income
8278 * @approval is optional external approval of box manipulation
8279 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8280 * NULL, if you don't care. */
8281 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8282 const char *box_id, const _Bool allow,
8283 const struct isds_approval *approval, char **refnumber) {
8284 return build_send_manipulationdbid_request_check_drop_response(context,
8285 (allow) ? BAD_CAST "SetOpenAddressing" :
8286 BAD_CAST "ClearOpenAddressing",
8287 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8291 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8292 * message acceptance). This is just a box permission. Sender must apply
8293 * such role by sending each message.
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 OVM role permission
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_effective_ovm(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 "SetEffectiveOVM" :
8305 BAD_CAST "ClearEffectiveOVM",
8306 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8310 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8311 * code, destroy response and log success.
8312 * @context is ISDS session context.
8313 * @service_name is name of SERVICE_DB_MANIPULATION service
8314 * @owner is structure describing box
8315 * @approval is optional external approval of box manipulation
8316 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8317 * NULL, if you don't care. */
8318 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8319 struct isds_ctx *context, const xmlChar *service_name,
8320 const struct isds_DbOwnerInfo *owner,
8321 const struct isds_approval *approval, xmlChar **refnumber) {
8322 isds_error err = IE_SUCCESS;
8323 #if HAVE_LIBCURL
8324 char *service_name_locale = NULL;
8325 xmlNodePtr request = NULL, db_owner_info;
8326 xmlNsPtr isds_ns = NULL;
8327 #endif
8330 if (!context) return IE_INVALID_CONTEXT;
8331 zfree(context->long_message);
8332 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8334 #if HAVE_LIBCURL
8335 service_name_locale = _isds_utf82locale((char*)service_name);
8336 if (!service_name_locale) {
8337 err = IE_NOMEM;
8338 goto leave;
8341 /* Build request */
8342 request = xmlNewNode(NULL, service_name);
8343 if (!request) {
8344 isds_printf_message(context,
8345 _("Could not build %s request"), service_name_locale);
8346 err = IE_ERROR;
8347 goto leave;
8349 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8350 if(!isds_ns) {
8351 isds_log_message(context, _("Could not create ISDS name space"));
8352 err = IE_ERROR;
8353 goto leave;
8355 xmlSetNs(request, isds_ns);
8358 /* Add XSD:tOwnerInfoInput child*/
8359 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8360 err = insert_DbOwnerInfo(context, owner, db_owner_info);
8361 if (err) goto leave;
8363 /* Add XSD:gExtApproval*/
8364 err = insert_GExtApproval(context, approval, request);
8365 if (err) goto leave;
8367 /* Send it to server and process response */
8368 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8369 service_name, &request, refnumber);
8371 leave:
8372 xmlFreeNode(request);
8373 free(service_name_locale);
8374 #else /* not HAVE_LIBCURL */
8375 err = IE_NOTSUP;
8376 #endif
8378 return err;
8382 /* Switch box accessibility state on request of box owner.
8383 * Despite the name, owner must do the request off-line. This function is
8384 * designed for such off-line meeting points (e.g. Czech POINT).
8385 * @context is ISDS session context.
8386 * @box identifies box to switch accessibility state.
8387 * @allow is true for making accessible, false to disallow access.
8388 * @approval is optional external approval of box manipulation
8389 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8390 * NULL, if you don't care. */
8391 isds_error isds_switch_box_accessibility_on_owner_request(
8392 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8393 const _Bool allow, const struct isds_approval *approval,
8394 char **refnumber) {
8395 return build_send_manipulationdbowner_request_check_drop_response(context,
8396 (allow) ? BAD_CAST "EnableOwnDataBox" :
8397 BAD_CAST "DisableOwnDataBox",
8398 box, approval, (xmlChar **) refnumber);
8402 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8403 * date.
8404 * @context is ISDS session context.
8405 * @box identifies box to switch accessibility state.
8406 * @since is date since accessibility has been denied. This can be past too.
8407 * Only tm_year, tm_mon and tm_mday carry sane value.
8408 * @approval is optional external approval of box manipulation
8409 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8410 * NULL, if you don't care. */
8411 isds_error isds_disable_box_accessibility_externaly(
8412 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8413 const struct tm *since, const struct isds_approval *approval,
8414 char **refnumber) {
8415 isds_error err = IE_SUCCESS;
8416 #if HAVE_LIBCURL
8417 char *service_name_locale = NULL;
8418 xmlNodePtr request = NULL, node;
8419 xmlNsPtr isds_ns = NULL;
8420 xmlChar *string = NULL;
8421 #endif
8424 if (!context) return IE_INVALID_CONTEXT;
8425 zfree(context->long_message);
8426 if (!box || !since) return IE_INVAL;
8428 #if HAVE_LIBCURL
8429 /* Build request */
8430 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
8431 if (!request) {
8432 isds_printf_message(context,
8433 _("Could not build %s request"), "DisableDataBoxExternally");
8434 err = IE_ERROR;
8435 goto leave;
8437 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8438 if(!isds_ns) {
8439 isds_log_message(context, _("Could not create ISDS name space"));
8440 err = IE_ERROR;
8441 goto leave;
8443 xmlSetNs(request, isds_ns);
8446 /* Add @box identification */
8447 INSERT_ELEMENT(node, request, "dbOwnerInfo");
8448 err = insert_DbOwnerInfo(context, box, node);
8449 if (err) goto leave;
8451 /* Add @since date */
8452 err = tm2datestring(since, &string);
8453 if(err) {
8454 isds_log_message(context,
8455 _("Could not convert `since' argument to ISO date string"));
8456 goto leave;
8458 INSERT_STRING(request, "dbOwnerDisableDate", string);
8459 zfree(string);
8461 /* Add @approval */
8462 err = insert_GExtApproval(context, approval, request);
8463 if (err) goto leave;
8465 /* Send it to server and process response */
8466 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8467 BAD_CAST "DisableDataBoxExternally", &request,
8468 (xmlChar **) refnumber);
8470 leave:
8471 free(string);
8472 xmlFreeNode(request);
8473 free(service_name_locale);
8474 #else /* not HAVE_LIBCURL */
8475 err = IE_NOTSUP;
8476 #endif
8478 return err;
8482 #if HAVE_LIBCURL
8483 /* Insert struct isds_message data (envelope (recipient data optional) and
8484 * documents into XML tree
8485 * @context is session context
8486 * @outgoing_message is libisds structure with message data
8487 * @create_message is XML CreateMessage or CreateMultipleMessage element
8488 * @process_recipient true for recipient data serialization, false for no
8489 * serialization */
8490 static isds_error insert_envelope_files(struct isds_ctx *context,
8491 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8492 const _Bool process_recipient) {
8494 isds_error err = IE_SUCCESS;
8495 xmlNodePtr envelope, dm_files, node;
8496 xmlChar *string = NULL;
8498 if (!context) return IE_INVALID_CONTEXT;
8499 if (!outgoing_message || !create_message) return IE_INVAL;
8502 /* Build envelope */
8503 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8504 if (!envelope) {
8505 isds_printf_message(context, _("Could not add dmEnvelope child to "
8506 "%s element"), create_message->name);
8507 return IE_ERROR;
8510 if (!outgoing_message->envelope) {
8511 isds_log_message(context, _("Outgoing message is missing envelope"));
8512 err = IE_INVAL;
8513 goto leave;
8516 /* Insert optional message type */
8517 err = insert_message_type(context, outgoing_message->envelope->dmType,
8518 envelope);
8519 if (err) goto leave;
8521 INSERT_STRING(envelope, "dmSenderOrgUnit",
8522 outgoing_message->envelope->dmSenderOrgUnit);
8523 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8524 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8526 if (process_recipient) {
8527 if (!outgoing_message->envelope->dbIDRecipient) {
8528 isds_log_message(context,
8529 _("Outgoing message is missing recipient box identifier"));
8530 err = IE_INVAL;
8531 goto leave;
8533 INSERT_STRING(envelope, "dbIDRecipient",
8534 outgoing_message->envelope->dbIDRecipient);
8536 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8537 outgoing_message->envelope->dmRecipientOrgUnit);
8538 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8539 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8540 INSERT_STRING(envelope, "dmToHands",
8541 outgoing_message->envelope->dmToHands);
8544 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8545 "dmAnnotation");
8546 INSERT_STRING(envelope, "dmAnnotation",
8547 outgoing_message->envelope->dmAnnotation);
8549 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8550 0, 50, "dmRecipientRefNumber");
8551 INSERT_STRING(envelope, "dmRecipientRefNumber",
8552 outgoing_message->envelope->dmRecipientRefNumber);
8554 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8555 0, 50, "dmSenderRefNumber");
8556 INSERT_STRING(envelope, "dmSenderRefNumber",
8557 outgoing_message->envelope->dmSenderRefNumber);
8559 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8560 0, 50, "dmRecipientIdent");
8561 INSERT_STRING(envelope, "dmRecipientIdent",
8562 outgoing_message->envelope->dmRecipientIdent);
8564 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8565 0, 50, "dmSenderIdent");
8566 INSERT_STRING(envelope, "dmSenderIdent",
8567 outgoing_message->envelope->dmSenderIdent);
8569 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8570 outgoing_message->envelope->dmLegalTitleLaw, string);
8571 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8572 outgoing_message->envelope->dmLegalTitleYear, string);
8573 INSERT_STRING(envelope, "dmLegalTitleSect",
8574 outgoing_message->envelope->dmLegalTitleSect);
8575 INSERT_STRING(envelope, "dmLegalTitlePar",
8576 outgoing_message->envelope->dmLegalTitlePar);
8577 INSERT_STRING(envelope, "dmLegalTitlePoint",
8578 outgoing_message->envelope->dmLegalTitlePoint);
8580 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8581 outgoing_message->envelope->dmPersonalDelivery);
8582 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8583 outgoing_message->envelope->dmAllowSubstDelivery);
8585 /* ???: Should we require value for dbEffectiveOVM sender?
8586 * ISDS has default as true */
8587 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8588 INSERT_BOOLEAN(envelope, "dmOVM",
8589 outgoing_message->envelope->dmPublishOwnID);
8592 /* Append dmFiles */
8593 if (!outgoing_message->documents) {
8594 isds_log_message(context,
8595 _("Outgoing message is missing list of documents"));
8596 err = IE_INVAL;
8597 goto leave;
8599 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8600 if (!dm_files) {
8601 isds_printf_message(context, _("Could not add dmFiles child to "
8602 "%s element"), create_message->name);
8603 err = IE_ERROR;
8604 goto leave;
8607 /* Check for document hierarchy */
8608 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8609 if (err) goto leave;
8611 /* Process each document */
8612 for (struct isds_list *item =
8613 (struct isds_list *) outgoing_message->documents;
8614 item; item = item->next) {
8615 if (!item->data) {
8616 isds_log_message(context,
8617 _("List of documents contains empty item"));
8618 err = IE_INVAL;
8619 goto leave;
8621 /* FIXME: Check for dmFileMetaType and for document references.
8622 * Only first document can be of MAIN type */
8623 err = insert_document(context, (struct isds_document*) item->data,
8624 dm_files);
8626 if (err) goto leave;
8629 leave:
8630 free(string);
8631 return err;
8633 #endif /* HAVE_LIBCURL */
8636 /* Send a message via ISDS to a recipient
8637 * @context is session context
8638 * @outgoing_message is message to send; Some members are mandatory (like
8639 * dbIDRecipient), some are optional and some are irrelevant (especially data
8640 * about sender). Included pointer to isds_list documents must contain at
8641 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8642 * members will be filled with valid data from ISDS. Exact list of write
8643 * members is subject to change. Currently dmID is changed.
8644 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8645 isds_error isds_send_message(struct isds_ctx *context,
8646 struct isds_message *outgoing_message) {
8648 isds_error err = IE_SUCCESS;
8649 #if HAVE_LIBCURL
8650 xmlNsPtr isds_ns = NULL;
8651 xmlNodePtr request = NULL;
8652 xmlDocPtr response = NULL;
8653 xmlChar *code = NULL, *message = NULL;
8654 xmlXPathContextPtr xpath_ctx = NULL;
8655 xmlXPathObjectPtr result = NULL;
8656 /*_Bool message_is_complete = 0;*/
8657 #endif
8659 if (!context) return IE_INVALID_CONTEXT;
8660 zfree(context->long_message);
8661 if (!outgoing_message) return IE_INVAL;
8663 #if HAVE_LIBCURL
8664 /* Check if connection is established
8665 * TODO: This check should be done downstairs. */
8666 if (!context->curl) return IE_CONNECTION_CLOSED;
8669 /* Build CreateMessage request */
8670 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8671 if (!request) {
8672 isds_log_message(context,
8673 _("Could not build CreateMessage request"));
8674 return IE_ERROR;
8676 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8677 if(!isds_ns) {
8678 isds_log_message(context, _("Could not create ISDS name space"));
8679 xmlFreeNode(request);
8680 return IE_ERROR;
8682 xmlSetNs(request, isds_ns);
8684 /* Append envelope and files */
8685 err = insert_envelope_files(context, outgoing_message, request, 1);
8686 if (err) goto leave;
8689 /* Signal we can serialize message since now */
8690 /*message_is_complete = 1;*/
8693 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8695 /* Sent request */
8696 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8698 /* Don't' destroy request, we want to provide it to application later */
8700 if (err) {
8701 isds_log(ILF_ISDS, ILL_DEBUG,
8702 _("Processing ISDS response on CreateMessage "
8703 "request failed\n"));
8704 goto leave;
8707 /* Check for response status */
8708 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8709 &code, &message, NULL);
8710 if (err) {
8711 isds_log(ILF_ISDS, ILL_DEBUG,
8712 _("ISDS response on CreateMessage request "
8713 "is missing status\n"));
8714 goto leave;
8717 /* Request processed, but refused by server or server failed */
8718 if (xmlStrcmp(code, BAD_CAST "0000")) {
8719 char *box_id_locale =
8720 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8721 char *code_locale = _isds_utf82locale((char*)code);
8722 char *message_locale = _isds_utf82locale((char*)message);
8723 isds_log(ILF_ISDS, ILL_DEBUG,
8724 _("Server did not accept message for %s on CreateMessage "
8725 "request (code=%s, message=%s)\n"),
8726 box_id_locale, code_locale, message_locale);
8727 isds_log_message(context, message_locale);
8728 free(box_id_locale);
8729 free(code_locale);
8730 free(message_locale);
8731 err = IE_ISDS;
8732 goto leave;
8736 /* Extract data */
8737 xpath_ctx = xmlXPathNewContext(response);
8738 if (!xpath_ctx) {
8739 err = IE_ERROR;
8740 goto leave;
8742 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8743 err = IE_ERROR;
8744 goto leave;
8746 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8747 xpath_ctx);
8748 if (!result) {
8749 err = IE_ERROR;
8750 goto leave;
8752 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8753 isds_log_message(context, _("Missing CreateMessageResponse element"));
8754 err = IE_ISDS;
8755 goto leave;
8757 if (result->nodesetval->nodeNr > 1) {
8758 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8759 err = IE_ISDS;
8760 goto leave;
8762 xpath_ctx->node = result->nodesetval->nodeTab[0];
8763 xmlXPathFreeObject(result); result = NULL;
8765 if (outgoing_message->envelope->dmID) {
8766 free(outgoing_message->envelope->dmID);
8767 outgoing_message->envelope->dmID = NULL;
8769 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8770 if (!outgoing_message->envelope->dmID) {
8771 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8772 "but did not return assigned message ID\n"));
8775 leave:
8776 /* TODO: Serialize message into structure member raw */
8777 /* XXX: Each web service transport message in different format.
8778 * Therefore it's not possible to save them directly.
8779 * To save them, one must figure out common format.
8780 * We can leave it on application, or we can implement the ESS format. */
8781 /*if (message_is_complete) {
8782 if (outgoing_message->envelope->dmID) {
8784 /* Add assigned message ID as first child*/
8785 /*xmlNodePtr dmid_text = xmlNewText(
8786 (xmlChar *) outgoing_message->envelope->dmID);
8787 if (!dmid_text) goto serialization_failed;
8789 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8790 BAD_CAST "dmID");
8791 if (!dmid_element) {
8792 xmlFreeNode(dmid_text);
8793 goto serialization_failed;
8796 xmlNodePtr dmid_element_with_text =
8797 xmlAddChild(dmid_element, dmid_text);
8798 if (!dmid_element_with_text) {
8799 xmlFreeNode(dmid_element);
8800 xmlFreeNode(dmid_text);
8801 goto serialization_failed;
8804 node = xmlAddPrevSibling(envelope->childern,
8805 dmid_element_with_text);
8806 if (!node) {
8807 xmlFreeNodeList(dmid_element_with_text);
8808 goto serialization_failed;
8812 /* Serialize message with ID into raw */
8813 /*buffer = serialize_element(envelope)*/
8814 /* }
8816 serialization_failed:
8820 /* Clean up */
8821 xmlXPathFreeObject(result);
8822 xmlXPathFreeContext(xpath_ctx);
8824 free(code);
8825 free(message);
8826 xmlFreeDoc(response);
8827 xmlFreeNode(request);
8829 if (!err)
8830 isds_log(ILF_ISDS, ILL_DEBUG,
8831 _("CreateMessage request processed by server "
8832 "successfully.\n"));
8833 #else /* not HAVE_LIBCURL */
8834 err = IE_NOTSUP;
8835 #endif
8837 return err;
8841 /* Send a message via ISDS to a multiple recipients
8842 * @context is session context
8843 * @outgoing_message is message to send; Some members are mandatory,
8844 * some are optional and some are irrelevant (especially data
8845 * about sender). Data about recipient will be substituted by ISDS from
8846 * @copies. Included pointer to isds_list documents must
8847 * contain at least one document of FILEMETATYPE_MAIN.
8848 * @copies is list of isds_message_copy structures addressing all desired
8849 * recipients. This is read-write structure, some members will be filled with
8850 * valid data from ISDS (message IDs, error codes, error descriptions).
8851 * @return
8852 * ISDS_SUCCESS if all messages have been sent
8853 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8854 * succeeded messages can be identified by copies->data->error),
8855 * or other error code if something other goes wrong. */
8856 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8857 const struct isds_message *outgoing_message,
8858 struct isds_list *copies) {
8860 isds_error err = IE_SUCCESS;
8861 #if HAVE_LIBCURL
8862 isds_error append_err;
8863 xmlNsPtr isds_ns = NULL;
8864 xmlNodePtr request = NULL, recipients, recipient, node;
8865 struct isds_list *item;
8866 struct isds_message_copy *copy;
8867 xmlDocPtr response = NULL;
8868 xmlChar *code = NULL, *message = NULL;
8869 xmlXPathContextPtr xpath_ctx = NULL;
8870 xmlXPathObjectPtr result = NULL;
8871 xmlChar *string = NULL;
8872 int i;
8873 #endif
8875 if (!context) return IE_INVALID_CONTEXT;
8876 zfree(context->long_message);
8877 if (!outgoing_message || !copies) return IE_INVAL;
8879 #if HAVE_LIBCURL
8880 /* Check if connection is established
8881 * TODO: This check should be done downstairs. */
8882 if (!context->curl) return IE_CONNECTION_CLOSED;
8885 /* Build CreateMultipleMessage request */
8886 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
8887 if (!request) {
8888 isds_log_message(context,
8889 _("Could not build CreateMultipleMessage request"));
8890 return IE_ERROR;
8892 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8893 if(!isds_ns) {
8894 isds_log_message(context, _("Could not create ISDS name space"));
8895 xmlFreeNode(request);
8896 return IE_ERROR;
8898 xmlSetNs(request, isds_ns);
8901 /* Build recipients */
8902 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8903 if (!recipients) {
8904 isds_log_message(context, _("Could not add dmRecipients child to "
8905 "CreateMultipleMessage element"));
8906 xmlFreeNode(request);
8907 return IE_ERROR;
8910 /* Insert each recipient */
8911 for (item = copies; item; item = item->next) {
8912 copy = (struct isds_message_copy *) item->data;
8913 if (!copy) {
8914 isds_log_message(context,
8915 _("`copies' list item contains empty data"));
8916 err = IE_INVAL;
8917 goto leave;
8920 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
8921 if (!recipient) {
8922 isds_log_message(context, _("Could not add dmRecipient child to "
8923 "dmRecipients element"));
8924 err = IE_ERROR;
8925 goto leave;
8928 if (!copy->dbIDRecipient) {
8929 isds_log_message(context,
8930 _("Message copy is missing recipient box identifier"));
8931 err = IE_INVAL;
8932 goto leave;
8934 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
8935 INSERT_STRING(recipient, "dmRecipientOrgUnit",
8936 copy->dmRecipientOrgUnit);
8937 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
8938 copy->dmRecipientOrgUnitNum, string);
8939 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
8942 /* Append envelope and files */
8943 err = insert_envelope_files(context, outgoing_message, request, 0);
8944 if (err) goto leave;
8947 isds_log(ILF_ISDS, ILL_DEBUG,
8948 _("Sending CreateMultipleMessage request to ISDS\n"));
8950 /* Sent request */
8951 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8952 if (err) {
8953 isds_log(ILF_ISDS, ILL_DEBUG,
8954 _("Processing ISDS response on CreateMultipleMessage "
8955 "request failed\n"));
8956 goto leave;
8959 /* Check for response status */
8960 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8961 &code, &message, NULL);
8962 if (err) {
8963 isds_log(ILF_ISDS, ILL_DEBUG,
8964 _("ISDS response on CreateMultipleMessage request "
8965 "is missing status\n"));
8966 goto leave;
8969 /* Request processed, but some copies failed */
8970 if (!xmlStrcmp(code, BAD_CAST "0004")) {
8971 char *box_id_locale =
8972 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8973 char *code_locale = _isds_utf82locale((char*)code);
8974 char *message_locale = _isds_utf82locale((char*)message);
8975 isds_log(ILF_ISDS, ILL_DEBUG,
8976 _("Server did accept message for multiple recipients "
8977 "on CreateMultipleMessage request but delivery to "
8978 "some of them failed (code=%s, message=%s)\n"),
8979 box_id_locale, code_locale, message_locale);
8980 isds_log_message(context, message_locale);
8981 free(box_id_locale);
8982 free(code_locale);
8983 free(message_locale);
8984 err = IE_PARTIAL_SUCCESS;
8987 /* Request refused by server as whole */
8988 else if (xmlStrcmp(code, BAD_CAST "0000")) {
8989 char *box_id_locale =
8990 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8991 char *code_locale = _isds_utf82locale((char*)code);
8992 char *message_locale = _isds_utf82locale((char*)message);
8993 isds_log(ILF_ISDS, ILL_DEBUG,
8994 _("Server did not accept message for multiple recipients "
8995 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
8996 box_id_locale, code_locale, message_locale);
8997 isds_log_message(context, message_locale);
8998 free(box_id_locale);
8999 free(code_locale);
9000 free(message_locale);
9001 err = IE_ISDS;
9002 goto leave;
9006 /* Extract data */
9007 xpath_ctx = xmlXPathNewContext(response);
9008 if (!xpath_ctx) {
9009 err = IE_ERROR;
9010 goto leave;
9012 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9013 err = IE_ERROR;
9014 goto leave;
9016 result = xmlXPathEvalExpression(
9017 BAD_CAST "/isds:CreateMultipleMessageResponse"
9018 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9019 xpath_ctx);
9020 if (!result) {
9021 err = IE_ERROR;
9022 goto leave;
9024 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9025 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9026 err = IE_ISDS;
9027 goto leave;
9030 /* Extract message ID and delivery status for each copy */
9031 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9032 item = item->next, i++) {
9033 copy = (struct isds_message_copy *) item->data;
9034 xpath_ctx->node = result->nodesetval->nodeTab[i];
9036 append_err = append_TMStatus(context, copy, xpath_ctx);
9037 if (append_err) {
9038 err = append_err;
9039 goto leave;
9042 if (item || i < result->nodesetval->nodeNr) {
9043 isds_printf_message(context, _("ISDS returned unexpected number of "
9044 "message copy delivery states: %d"),
9045 result->nodesetval->nodeNr);
9046 err = IE_ISDS;
9047 goto leave;
9051 leave:
9052 /* Clean up */
9053 free(string);
9054 xmlXPathFreeObject(result);
9055 xmlXPathFreeContext(xpath_ctx);
9057 free(code);
9058 free(message);
9059 xmlFreeDoc(response);
9060 xmlFreeNode(request);
9062 if (!err)
9063 isds_log(ILF_ISDS, ILL_DEBUG,
9064 _("CreateMultipleMessageResponse request processed by server "
9065 "successfully.\n"));
9066 #else /* not HAVE_LIBCURL */
9067 err = IE_NOTSUP;
9068 #endif
9070 return err;
9074 /* Get list of messages. This is common core for getting sent or received
9075 * messages.
9076 * Any criterion argument can be NULL, if you don't care about it.
9077 * @context is session context. Must not be NULL.
9078 * @outgoing_direction is true if you want list of outgoing messages,
9079 * it's false if you want incoming messages.
9080 * @from_time is minimal time and date of message sending inclusive.
9081 * @to_time is maximal time and date of message sending inclusive
9082 * @organization_unit_number is number of sender/recipient respectively.
9083 * @status_filter is bit field of isds_message_status values. Use special
9084 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9085 * all values, you can use bit-wise arithmetic if you want.)
9086 * @offset is index of first message we are interested in. First message is 1.
9087 * Set to 0 (or 1) if you don't care.
9088 * @number is maximal length of list you want to get as input value, outputs
9089 * number of messages matching these criteria. Can be NULL if you don't care
9090 * (applies to output value either).
9091 * @messages is automatically reallocated list of isds_message's. Be ware that
9092 * it returns only brief overview (envelope and some other fields) about each
9093 * message, not the complete message. FIXME: Specify exact fields.
9094 * The list is sorted by delivery time in ascending order.
9095 * Use NULL if you don't care about don't need the data (useful if you want to
9096 * know only the @number). If you provide &NULL, list will be allocated on
9097 * heap, if you provide pointer to non-NULL, list will be freed automatically
9098 * at first. Also in case of error the list will be NULLed.
9099 * @return IE_SUCCESS or appropriate error code. */
9100 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9101 _Bool outgoing_direction,
9102 const struct timeval *from_time, const struct timeval *to_time,
9103 const long int *organization_unit_number,
9104 const unsigned int status_filter,
9105 const unsigned long int offset, unsigned long int *number,
9106 struct isds_list **messages) {
9108 isds_error err = IE_SUCCESS;
9109 #if HAVE_LIBCURL
9110 xmlNsPtr isds_ns = NULL;
9111 xmlNodePtr request = NULL, node;
9112 xmlDocPtr response = NULL;
9113 xmlChar *code = NULL, *message = NULL;
9114 xmlXPathContextPtr xpath_ctx = NULL;
9115 xmlXPathObjectPtr result = NULL;
9116 xmlChar *string = NULL;
9117 long unsigned int count = 0;
9118 #endif
9120 if (!context) return IE_INVALID_CONTEXT;
9121 zfree(context->long_message);
9123 /* Free former message list if any */
9124 if (messages) isds_list_free(messages);
9126 #if HAVE_LIBCURL
9127 /* Check if connection is established
9128 * TODO: This check should be done downstairs. */
9129 if (!context->curl) return IE_CONNECTION_CLOSED;
9131 /* Build GetListOf*Messages request */
9132 request = xmlNewNode(NULL,
9133 (outgoing_direction) ?
9134 BAD_CAST "GetListOfSentMessages" :
9135 BAD_CAST "GetListOfReceivedMessages"
9137 if (!request) {
9138 isds_log_message(context,
9139 (outgoing_direction) ?
9140 _("Could not build GetListOfSentMessages request") :
9141 _("Could not build GetListOfReceivedMessages request")
9143 return IE_ERROR;
9145 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9146 if(!isds_ns) {
9147 isds_log_message(context, _("Could not create ISDS name space"));
9148 xmlFreeNode(request);
9149 return IE_ERROR;
9151 xmlSetNs(request, isds_ns);
9154 if (from_time) {
9155 err = timeval2timestring(from_time, &string);
9156 if (err) goto leave;
9158 INSERT_STRING(request, "dmFromTime", string);
9159 free(string); string = NULL;
9161 if (to_time) {
9162 err = timeval2timestring(to_time, &string);
9163 if (err) goto leave;
9165 INSERT_STRING(request, "dmToTime", string);
9166 free(string); string = NULL;
9168 if (outgoing_direction) {
9169 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9170 organization_unit_number, string);
9171 } else {
9172 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9173 organization_unit_number, string);
9176 if (status_filter > MESSAGESTATE_ANY) {
9177 isds_printf_message(context,
9178 _("Invalid message state filter value: %ld"), status_filter);
9179 err = IE_INVAL;
9180 goto leave;
9182 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9184 if (offset > 0 ) {
9185 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9186 } else {
9187 INSERT_STRING(request, "dmOffset", "1");
9190 /* number 0 means no limit */
9191 if (number && *number == 0) {
9192 INSERT_STRING(request, "dmLimit", NULL);
9193 } else {
9194 INSERT_ULONGINT(request, "dmLimit", number, string);
9198 isds_log(ILF_ISDS, ILL_DEBUG,
9199 (outgoing_direction) ?
9200 _("Sending GetListOfSentMessages request to ISDS\n") :
9201 _("Sending GetListOfReceivedMessages request to ISDS\n")
9204 /* Sent request */
9205 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9206 xmlFreeNode(request); request = NULL;
9208 if (err) {
9209 isds_log(ILF_ISDS, ILL_DEBUG,
9210 (outgoing_direction) ?
9211 _("Processing ISDS response on GetListOfSentMessages "
9212 "request failed\n") :
9213 _("Processing ISDS response on GetListOfReceivedMessages "
9214 "request failed\n")
9216 goto leave;
9219 /* Check for response status */
9220 err = isds_response_status(context, SERVICE_DM_INFO, response,
9221 &code, &message, NULL);
9222 if (err) {
9223 isds_log(ILF_ISDS, ILL_DEBUG,
9224 (outgoing_direction) ?
9225 _("ISDS response on GetListOfSentMessages request "
9226 "is missing status\n") :
9227 _("ISDS response on GetListOfReceivedMessages request "
9228 "is missing status\n")
9230 goto leave;
9233 /* Request processed, but nothing found */
9234 if (xmlStrcmp(code, BAD_CAST "0000")) {
9235 char *code_locale = _isds_utf82locale((char*)code);
9236 char *message_locale = _isds_utf82locale((char*)message);
9237 isds_log(ILF_ISDS, ILL_DEBUG,
9238 (outgoing_direction) ?
9239 _("Server refused GetListOfSentMessages request "
9240 "(code=%s, message=%s)\n") :
9241 _("Server refused GetListOfReceivedMessages request "
9242 "(code=%s, message=%s)\n"),
9243 code_locale, message_locale);
9244 isds_log_message(context, message_locale);
9245 free(code_locale);
9246 free(message_locale);
9247 err = IE_ISDS;
9248 goto leave;
9252 /* Extract data */
9253 xpath_ctx = xmlXPathNewContext(response);
9254 if (!xpath_ctx) {
9255 err = IE_ERROR;
9256 goto leave;
9258 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9259 err = IE_ERROR;
9260 goto leave;
9262 result = xmlXPathEvalExpression(
9263 (outgoing_direction) ?
9264 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9265 "isds:dmRecords/isds:dmRecord" :
9266 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9267 "isds:dmRecords/isds:dmRecord",
9268 xpath_ctx);
9269 if (!result) {
9270 err = IE_ERROR;
9271 goto leave;
9274 /* Fill output arguments in */
9275 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9276 struct isds_envelope *envelope;
9277 struct isds_list *item = NULL, *last_item = NULL;
9279 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9280 /* Create new message */
9281 item = calloc(1, sizeof(*item));
9282 if (!item) {
9283 err = IE_NOMEM;
9284 goto leave;
9286 item->destructor = (void(*)(void**)) &isds_message_free;
9287 item->data = calloc(1, sizeof(struct isds_message));
9288 if (!item->data) {
9289 isds_list_free(&item);
9290 err = IE_NOMEM;
9291 goto leave;
9294 /* Extract envelope data */
9295 xpath_ctx->node = result->nodesetval->nodeTab[count];
9296 envelope = NULL;
9297 err = extract_DmRecord(context, &envelope, xpath_ctx);
9298 if (err) {
9299 isds_list_free(&item);
9300 goto leave;
9303 /* Attach extracted envelope */
9304 ((struct isds_message *) item->data)->envelope = envelope;
9306 /* Append new message into the list */
9307 if (!*messages) {
9308 *messages = last_item = item;
9309 } else {
9310 last_item->next = item;
9311 last_item = item;
9315 if (number) *number = count;
9317 leave:
9318 if (err) {
9319 isds_list_free(messages);
9322 free(string);
9323 xmlXPathFreeObject(result);
9324 xmlXPathFreeContext(xpath_ctx);
9326 free(code);
9327 free(message);
9328 xmlFreeDoc(response);
9329 xmlFreeNode(request);
9331 if (!err)
9332 isds_log(ILF_ISDS, ILL_DEBUG,
9333 (outgoing_direction) ?
9334 _("GetListOfSentMessages request processed by server "
9335 "successfully.\n") :
9336 _("GetListOfReceivedMessages request processed by server "
9337 "successfully.\n")
9339 #else /* not HAVE_LIBCURL */
9340 err = IE_NOTSUP;
9341 #endif
9342 return err;
9346 /* Get list of outgoing (already sent) messages.
9347 * Any criterion argument can be NULL, if you don't care about it.
9348 * @context is session context. Must not be NULL.
9349 * @from_time is minimal time and date of message sending inclusive.
9350 * @to_time is maximal time and date of message sending inclusive
9351 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9352 * @status_filter is bit field of isds_message_status values. Use special
9353 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9354 * all values, you can use bit-wise arithmetic if you want.)
9355 * @offset is index of first message we are interested in. First message is 1.
9356 * Set to 0 (or 1) if you don't care.
9357 * @number is maximal length of list you want to get as input value, outputs
9358 * number of messages matching these criteria. Can be NULL if you don't care
9359 * (applies to output value either).
9360 * @messages is automatically reallocated list of isds_message's. Be ware that
9361 * it returns only brief overview (envelope and some other fields) about each
9362 * message, not the complete message. FIXME: Specify exact fields.
9363 * The list is sorted by delivery time in ascending order.
9364 * Use NULL if you don't care about the meta data (useful if you want to know
9365 * only the @number). If you provide &NULL, list will be allocated on heap,
9366 * if you provide pointer to non-NULL, list will be freed automatically at
9367 * first. Also in case of error the list will be NULLed.
9368 * @return IE_SUCCESS or appropriate error code. */
9369 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9370 const struct timeval *from_time, const struct timeval *to_time,
9371 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9372 const unsigned long int offset, unsigned long int *number,
9373 struct isds_list **messages) {
9375 return isds_get_list_of_messages(
9376 context, 1,
9377 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9378 offset, number,
9379 messages);
9383 /* Get list of incoming (addressed to you) messages.
9384 * Any criterion argument can be NULL, if you don't care about it.
9385 * @context is session context. Must not be NULL.
9386 * @from_time is minimal time and date of message sending inclusive.
9387 * @to_time is maximal time and date of message sending inclusive
9388 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9389 * @status_filter is bit field of isds_message_status values. Use special
9390 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9391 * all values, you can use bit-wise arithmetic if you want.)
9392 * @offset is index of first message we are interested in. First message is 1.
9393 * Set to 0 (or 1) if you don't care.
9394 * @number is maximal length of list you want to get as input value, outputs
9395 * number of messages matching these criteria. Can be NULL if you don't care
9396 * (applies to output value either).
9397 * @messages is automatically reallocated list of isds_message's. Be ware that
9398 * it returns only brief overview (envelope and some other fields) about each
9399 * message, not the complete message. FIXME: Specify exact fields.
9400 * Use NULL if you don't care about the meta data (useful if you want to know
9401 * only the @number). If you provide &NULL, list will be allocated on heap,
9402 * if you provide pointer to non-NULL, list will be freed automatically at
9403 * first. Also in case of error the list will be NULLed.
9404 * @return IE_SUCCESS or appropriate error code. */
9405 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9406 const struct timeval *from_time, const struct timeval *to_time,
9407 const long int *dmRecipientOrgUnitNum,
9408 const unsigned int status_filter,
9409 const unsigned long int offset, unsigned long int *number,
9410 struct isds_list **messages) {
9412 return isds_get_list_of_messages(
9413 context, 0,
9414 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9415 offset, number,
9416 messages);
9420 /* Get list of sent message state changes.
9421 * Any criterion argument can be NULL, if you don't care about it.
9422 * @context is session context. Must not be NULL.
9423 * @from_time is minimal time and date of status changes inclusive
9424 * @to_time is maximal time and date of status changes inclusive
9425 * @changed_states is automatically reallocated list of
9426 * isds_message_status_change's. If you provide &NULL, list will be allocated
9427 * on heap, if you provide pointer to non-NULL, list will be freed
9428 * automatically at first. Also in case of error the list will be NULLed.
9429 * XXX: The list item ordering is not specified.
9430 * XXX: Server provides only `recent' changes.
9431 * @return IE_SUCCESS or appropriate error code. */
9432 isds_error isds_get_list_of_sent_message_state_changes(
9433 struct isds_ctx *context,
9434 const struct timeval *from_time, const struct timeval *to_time,
9435 struct isds_list **changed_states) {
9437 isds_error err = IE_SUCCESS;
9438 #if HAVE_LIBCURL
9439 xmlNsPtr isds_ns = NULL;
9440 xmlNodePtr request = NULL, node;
9441 xmlDocPtr response = NULL;
9442 xmlXPathContextPtr xpath_ctx = NULL;
9443 xmlXPathObjectPtr result = NULL;
9444 xmlChar *string = NULL;
9445 long unsigned int count = 0;
9446 #endif
9448 if (!context) return IE_INVALID_CONTEXT;
9449 zfree(context->long_message);
9451 /* Free former message list if any */
9452 isds_list_free(changed_states);
9454 #if HAVE_LIBCURL
9455 /* Check if connection is established
9456 * TODO: This check should be done downstairs. */
9457 if (!context->curl) return IE_CONNECTION_CLOSED;
9459 /* Build GetMessageStateChanges request */
9460 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
9461 if (!request) {
9462 isds_log_message(context,
9463 _("Could not build GetMessageStateChanges request"));
9464 return IE_ERROR;
9466 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9467 if(!isds_ns) {
9468 isds_log_message(context, _("Could not create ISDS name space"));
9469 xmlFreeNode(request);
9470 return IE_ERROR;
9472 xmlSetNs(request, isds_ns);
9475 if (from_time) {
9476 err = timeval2timestring(from_time, &string);
9477 if (err) goto leave;
9479 INSERT_STRING(request, "dmFromTime", string);
9480 zfree(string);
9482 if (to_time) {
9483 err = timeval2timestring(to_time, &string);
9484 if (err) goto leave;
9486 INSERT_STRING(request, "dmToTime", string);
9487 zfree(string);
9490 /* Sent request */
9491 err = send_destroy_request_check_response(context,
9492 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9493 &response, NULL, NULL);
9494 if (err) goto leave;
9497 /* Extract data */
9498 xpath_ctx = xmlXPathNewContext(response);
9499 if (!xpath_ctx) {
9500 err = IE_ERROR;
9501 goto leave;
9503 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9504 err = IE_ERROR;
9505 goto leave;
9507 result = xmlXPathEvalExpression(
9508 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9509 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9510 if (!result) {
9511 err = IE_ERROR;
9512 goto leave;
9515 /* Fill output arguments in */
9516 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9517 struct isds_list *item = NULL, *last_item = NULL;
9519 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9520 /* Create new status change */
9521 item = calloc(1, sizeof(*item));
9522 if (!item) {
9523 err = IE_NOMEM;
9524 goto leave;
9526 item->destructor =
9527 (void(*)(void**)) &isds_message_status_change_free;
9529 /* Extract message status change */
9530 xpath_ctx->node = result->nodesetval->nodeTab[count];
9531 err = extract_StateChangesRecord(context,
9532 (struct isds_message_status_change **) &item->data,
9533 xpath_ctx);
9534 if (err) {
9535 isds_list_free(&item);
9536 goto leave;
9539 /* Append new message status change into the list */
9540 if (!*changed_states) {
9541 *changed_states = last_item = item;
9542 } else {
9543 last_item->next = item;
9544 last_item = item;
9549 leave:
9550 if (err) {
9551 isds_list_free(changed_states);
9554 free(string);
9555 xmlXPathFreeObject(result);
9556 xmlXPathFreeContext(xpath_ctx);
9557 xmlFreeDoc(response);
9558 xmlFreeNode(request);
9560 if (!err)
9561 isds_log(ILF_ISDS, ILL_DEBUG,
9562 _("GetMessageStateChanges request processed by server "
9563 "successfully.\n"));
9564 #else /* not HAVE_LIBCURL */
9565 err = IE_NOTSUP;
9566 #endif
9567 return err;
9571 #if HAVE_LIBCURL
9572 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9573 * code
9574 * @context is session context
9575 * @service is ISDS WS service handler
9576 * @service_name is name of SERVICE_DM_OPERATIONS
9577 * @message_id is message ID to send as service argument to ISDS
9578 * @response is reallocated server SOAP body response as XML document
9579 * @raw_response is reallocated bit stream with response body. Use
9580 * NULL if you don't care
9581 * @raw_response_length is size of @raw_response in bytes
9582 * @code is reallocated ISDS status code
9583 * @status_message is reallocated ISDS status message
9584 * @return error coded from lower layer, context message will be set up
9585 * appropriately. */
9586 static isds_error build_send_check_message_request(struct isds_ctx *context,
9587 const isds_service service, const xmlChar *service_name,
9588 const char *message_id,
9589 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9590 xmlChar **code, xmlChar **status_message) {
9592 isds_error err = IE_SUCCESS;
9593 char *service_name_locale = NULL, *message_id_locale = NULL;
9594 xmlNodePtr request = NULL, node;
9595 xmlNsPtr isds_ns = NULL;
9597 if (!context) return IE_INVALID_CONTEXT;
9598 if (!service_name || !message_id) return IE_INVAL;
9599 if (!response || !code || !status_message) return IE_INVAL;
9600 if (!raw_response_length && raw_response) return IE_INVAL;
9602 /* Free output argument */
9603 xmlFreeDoc(*response); *response = NULL;
9604 if (raw_response) zfree(*raw_response);
9605 zfree(*code);
9606 zfree(*status_message);
9609 /* Check if connection is established
9610 * TODO: This check should be done downstairs. */
9611 if (!context->curl) return IE_CONNECTION_CLOSED;
9613 service_name_locale = _isds_utf82locale((char*)service_name);
9614 message_id_locale = _isds_utf82locale(message_id);
9615 if (!service_name_locale || !message_id_locale) {
9616 err = IE_NOMEM;
9617 goto leave;
9620 /* Build request */
9621 request = xmlNewNode(NULL, service_name);
9622 if (!request) {
9623 isds_printf_message(context,
9624 _("Could not build %s request for %s message ID"),
9625 service_name_locale, message_id_locale);
9626 err = IE_ERROR;
9627 goto leave;
9629 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9630 if(!isds_ns) {
9631 isds_log_message(context, _("Could not create ISDS name space"));
9632 err = IE_ERROR;
9633 goto leave;
9635 xmlSetNs(request, isds_ns);
9638 /* Add requested ID */
9639 err = validate_message_id_length(context, (xmlChar *) message_id);
9640 if (err) goto leave;
9641 INSERT_STRING(request, "dmID", message_id);
9644 isds_log(ILF_ISDS, ILL_DEBUG,
9645 _("Sending %s request for %s message ID to ISDS\n"),
9646 service_name_locale, message_id_locale);
9648 /* Send request */
9649 err = _isds(context, service, request, response,
9650 raw_response, raw_response_length);
9651 xmlFreeNode(request); request = NULL;
9653 if (err) {
9654 isds_log(ILF_ISDS, ILL_DEBUG,
9655 _("Processing ISDS response on %s request failed\n"),
9656 service_name_locale);
9657 goto leave;
9660 /* Check for response status */
9661 err = isds_response_status(context, service, *response,
9662 code, status_message, NULL);
9663 if (err) {
9664 isds_log(ILF_ISDS, ILL_DEBUG,
9665 _("ISDS response on %s request is missing status\n"),
9666 service_name_locale);
9667 goto leave;
9670 /* Request processed, but nothing found */
9671 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9672 char *code_locale = _isds_utf82locale((char*) *code);
9673 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9674 isds_log(ILF_ISDS, ILL_DEBUG,
9675 _("Server refused %s request for %s message ID "
9676 "(code=%s, message=%s)\n"),
9677 service_name_locale, message_id_locale,
9678 code_locale, status_message_locale);
9679 isds_log_message(context, status_message_locale);
9680 free(code_locale);
9681 free(status_message_locale);
9682 err = IE_ISDS;
9683 goto leave;
9686 leave:
9687 free(message_id_locale);
9688 free(service_name_locale);
9689 xmlFreeNode(request);
9690 return err;
9694 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9695 * signed data and free ISDS response.
9696 * @context is session context
9697 * @message_id is UTF-8 encoded message ID for logging purpose
9698 * @response is parsed XML document. It will be freed and NULLed in the middle
9699 * of function run to save memory. This is not guaranteed in case of error.
9700 * @request_name is name of ISDS request used to construct response root
9701 * element name and for logging purpose.
9702 * @raw is reallocated output buffer with DER encoded CMS data
9703 * @raw_length is size of @raw buffer in bytes
9704 * @returns standard error codes, in case of error, @raw will be freed and
9705 * NULLed, @response sometimes. */
9706 static isds_error find_extract_signed_data_free_response(
9707 struct isds_ctx *context, const xmlChar *message_id,
9708 xmlDocPtr *response, const xmlChar *request_name,
9709 void **raw, size_t *raw_length) {
9711 isds_error err = IE_SUCCESS;
9712 char *xpath_expression = NULL;
9713 xmlXPathContextPtr xpath_ctx = NULL;
9714 xmlXPathObjectPtr result = NULL;
9715 char *encoded_structure = NULL;
9717 if (!context) return IE_INVALID_CONTEXT;
9718 if (!raw) return IE_INVAL;
9719 zfree(*raw);
9720 if (!message_id || !response || !*response || !request_name || !raw_length)
9721 return IE_INVAL;
9723 /* Build XPath expression */
9724 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9725 "Response/isds:dmSignature");
9726 if (!xpath_expression) return IE_NOMEM;
9728 /* Extract data */
9729 xpath_ctx = xmlXPathNewContext(*response);
9730 if (!xpath_ctx) {
9731 err = IE_ERROR;
9732 goto leave;
9734 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9735 err = IE_ERROR;
9736 goto leave;
9738 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9739 if (!result) {
9740 err = IE_ERROR;
9741 goto leave;
9743 /* Empty response */
9744 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9745 char *message_id_locale = _isds_utf82locale((char*) message_id);
9746 isds_printf_message(context,
9747 _("Server did not return any signed data for message ID `%s' "
9748 "on %s request"),
9749 message_id_locale, request_name);
9750 free(message_id_locale);
9751 err = IE_ISDS;
9752 goto leave;
9754 /* More responses */
9755 if (result->nodesetval->nodeNr > 1) {
9756 char *message_id_locale = _isds_utf82locale((char*) message_id);
9757 isds_printf_message(context,
9758 _("Server did return more signed data for message ID `%s' "
9759 "on %s request"),
9760 message_id_locale, request_name);
9761 free(message_id_locale);
9762 err = IE_ISDS;
9763 goto leave;
9765 /* One response */
9766 xpath_ctx->node = result->nodesetval->nodeTab[0];
9768 /* Extract PKCS#7 structure */
9769 EXTRACT_STRING(".", encoded_structure);
9770 if (!encoded_structure) {
9771 isds_log_message(context, _("dmSignature element is empty"));
9774 /* Here we have delivery info as standalone CMS in encoded_structure.
9775 * We don't need any other data, free them: */
9776 xmlXPathFreeObject(result); result = NULL;
9777 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9778 xmlFreeDoc(*response); *response = NULL;
9781 /* Decode PKCS#7 to DER format */
9782 *raw_length = _isds_b64decode(encoded_structure, raw);
9783 if (*raw_length == (size_t) -1) {
9784 isds_log_message(context,
9785 _("Error while Base64-decoding PKCS#7 structure"));
9786 err = IE_ERROR;
9787 goto leave;
9790 leave:
9791 if (err) {
9792 zfree(*raw);
9793 raw_length = 0;
9796 free(encoded_structure);
9797 xmlXPathFreeObject(result);
9798 xmlXPathFreeContext(xpath_ctx);
9799 free(xpath_expression);
9801 return err;
9803 #endif /* HAVE_LIBCURL */
9806 /* Download incoming message envelope identified by ID.
9807 * @context is session context
9808 * @message_id is message identifier (you can get them from
9809 * isds_get_list_of_received_messages())
9810 * @message is automatically reallocated message retrieved from ISDS.
9811 * It will miss documents per se. Use isds_get_received_message(), if you are
9812 * interested in documents (content) too.
9813 * Returned hash and timestamp require documents to be verifiable. */
9814 isds_error isds_get_received_envelope(struct isds_ctx *context,
9815 const char *message_id, struct isds_message **message) {
9817 isds_error err = IE_SUCCESS;
9818 #if HAVE_LIBCURL
9819 xmlDocPtr response = NULL;
9820 xmlChar *code = NULL, *status_message = NULL;
9821 xmlXPathContextPtr xpath_ctx = NULL;
9822 xmlXPathObjectPtr result = NULL;
9823 #endif
9825 if (!context) return IE_INVALID_CONTEXT;
9826 zfree(context->long_message);
9828 /* Free former message if any */
9829 if (!message) return IE_INVAL;
9830 isds_message_free(message);
9832 #if HAVE_LIBCURL
9833 /* Do request and check for success */
9834 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9835 BAD_CAST "MessageEnvelopeDownload", message_id,
9836 &response, NULL, NULL, &code, &status_message);
9837 if (err) goto leave;
9839 /* Extract data */
9840 xpath_ctx = xmlXPathNewContext(response);
9841 if (!xpath_ctx) {
9842 err = IE_ERROR;
9843 goto leave;
9845 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9846 err = IE_ERROR;
9847 goto leave;
9849 result = xmlXPathEvalExpression(
9850 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9851 "isds:dmReturnedMessageEnvelope",
9852 xpath_ctx);
9853 if (!result) {
9854 err = IE_ERROR;
9855 goto leave;
9857 /* Empty response */
9858 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9859 char *message_id_locale = _isds_utf82locale((char*) message_id);
9860 isds_printf_message(context,
9861 _("Server did not return any envelope for ID `%s' "
9862 "on MessageEnvelopeDownload request"), message_id_locale);
9863 free(message_id_locale);
9864 err = IE_ISDS;
9865 goto leave;
9867 /* More envelops */
9868 if (result->nodesetval->nodeNr > 1) {
9869 char *message_id_locale = _isds_utf82locale((char*) message_id);
9870 isds_printf_message(context,
9871 _("Server did return more envelopes for ID `%s' "
9872 "on MessageEnvelopeDownload request"), message_id_locale);
9873 free(message_id_locale);
9874 err = IE_ISDS;
9875 goto leave;
9877 /* One message */
9878 xpath_ctx->node = result->nodesetval->nodeTab[0];
9880 /* Extract the envelope (= message without documents, hence 0) */
9881 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9882 if (err) goto leave;
9884 /* Save XML blob */
9885 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9886 &(*message)->raw_length);
9888 leave:
9889 if (err) {
9890 isds_message_free(message);
9893 xmlXPathFreeObject(result);
9894 xmlXPathFreeContext(xpath_ctx);
9896 free(code);
9897 free(status_message);
9898 if (!*message || !(*message)->xml) {
9899 xmlFreeDoc(response);
9902 if (!err)
9903 isds_log(ILF_ISDS, ILL_DEBUG,
9904 _("MessageEnvelopeDownload request processed by server "
9905 "successfully.\n")
9907 #else /* not HAVE_LIBCURL */
9908 err = IE_NOTSUP;
9909 #endif
9910 return err;
9914 /* Load delivery info of any format from buffer.
9915 * @context is session context
9916 * @raw_type advertises format of @buffer content. Only delivery info types
9917 * are accepted.
9918 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9919 * retrieve such data from message->raw after calling
9920 * isds_get_signed_delivery_info().
9921 * @length is length of buffer in bytes.
9922 * @message is automatically reallocated message parsed from @buffer.
9923 * @strategy selects how buffer will be attached into raw isds_message member.
9924 * */
9925 isds_error isds_load_delivery_info(struct isds_ctx *context,
9926 const isds_raw_type raw_type,
9927 const void *buffer, const size_t length,
9928 struct isds_message **message, const isds_buffer_strategy strategy) {
9930 isds_error err = IE_SUCCESS;
9931 message_ns_type message_ns;
9932 xmlDocPtr message_doc = NULL;
9933 xmlXPathContextPtr xpath_ctx = NULL;
9934 xmlXPathObjectPtr result = NULL;
9935 void *xml_stream = NULL;
9936 size_t xml_stream_length = 0;
9938 if (!context) return IE_INVALID_CONTEXT;
9939 zfree(context->long_message);
9940 if (!message) return IE_INVAL;
9941 isds_message_free(message);
9942 if (!buffer) return IE_INVAL;
9945 /* Select buffer format and extract XML from CMS*/
9946 switch (raw_type) {
9947 case RAWTYPE_DELIVERYINFO:
9948 message_ns = MESSAGE_NS_UNSIGNED;
9949 xml_stream = (void *) buffer;
9950 xml_stream_length = length;
9951 break;
9953 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
9954 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9955 xml_stream = (void *) buffer;
9956 xml_stream_length = length;
9957 break;
9959 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
9960 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9961 err = _isds_extract_cms_data(context, buffer, length,
9962 &xml_stream, &xml_stream_length);
9963 if (err) goto leave;
9964 break;
9966 default:
9967 isds_log_message(context, _("Bad raw delivery representation type"));
9968 return IE_INVAL;
9969 break;
9972 isds_log(ILF_ISDS, ILL_DEBUG,
9973 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
9974 xml_stream_length, xml_stream);
9976 /* Convert delivery info XML stream into XPath context */
9977 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9978 if (!message_doc) {
9979 err = IE_XML;
9980 goto leave;
9982 xpath_ctx = xmlXPathNewContext(message_doc);
9983 if (!xpath_ctx) {
9984 err = IE_ERROR;
9985 goto leave;
9987 /* XXX: Name spaces mangled for signed delivery info:
9988 * http://isds.czechpoint.cz/v20/delivery:
9990 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
9991 * <q:dmDelivery>
9992 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9993 * <p:dmID>170272</p:dmID>
9994 * ...
9995 * </p:dmDm>
9996 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9997 * ...
9998 * </q:dmEvents>...</q:dmEvents>
9999 * </q:dmDelivery>
10000 * </q:GetDeliveryInfoResponse>
10001 * */
10002 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10003 err = IE_ERROR;
10004 goto leave;
10006 result = xmlXPathEvalExpression(
10007 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10008 xpath_ctx);
10009 if (!result) {
10010 err = IE_ERROR;
10011 goto leave;
10013 /* Empty delivery info */
10014 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10015 isds_printf_message(context,
10016 _("XML document is not sisds:dmDelivery document"));
10017 err = IE_ISDS;
10018 goto leave;
10020 /* More delivery info's */
10021 if (result->nodesetval->nodeNr > 1) {
10022 isds_printf_message(context,
10023 _("XML document has more sisds:dmDelivery elements"));
10024 err = IE_ISDS;
10025 goto leave;
10027 /* One delivery info */
10028 xpath_ctx->node = result->nodesetval->nodeTab[0];
10030 /* Extract the envelope (= message without documents, hence 0).
10031 * XXX: extract_TReturnedMessage() can obtain attachments size,
10032 * but delivery info carries none. It's coded as option elements,
10033 * so it should work. */
10034 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10035 if (err) goto leave;
10037 /* Extract events */
10038 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10039 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10040 if (err) { err = IE_ERROR; goto leave; }
10041 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10042 if (err) goto leave;
10044 /* Append raw CMS structure into message */
10045 (*message)->raw_type = raw_type;
10046 switch (strategy) {
10047 case BUFFER_DONT_STORE:
10048 break;
10049 case BUFFER_COPY:
10050 (*message)->raw = malloc(length);
10051 if (!(*message)->raw) {
10052 err = IE_NOMEM;
10053 goto leave;
10055 memcpy((*message)->raw, buffer, length);
10056 (*message)->raw_length = length;
10057 break;
10058 case BUFFER_MOVE:
10059 (*message)->raw = (void *) buffer;
10060 (*message)->raw_length = length;
10061 break;
10062 default:
10063 err = IE_ENUM;
10064 goto leave;
10067 leave:
10068 if (err) {
10069 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10070 isds_message_free(message);
10073 xmlXPathFreeObject(result);
10074 xmlXPathFreeContext(xpath_ctx);
10075 if (!*message || !(*message)->xml) {
10076 xmlFreeDoc(message_doc);
10078 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10080 if (!err)
10081 isds_log(ILF_ISDS, ILL_DEBUG,
10082 _("Delivery info loaded successfully.\n"));
10083 return err;
10087 /* Download signed delivery info-sheet of given message identified by ID.
10088 * @context is session context
10089 * @message_id is message identifier (you can get them from
10090 * isds_get_list_of_{sent,received}_messages())
10091 * @message is automatically reallocated message retrieved from ISDS.
10092 * It will miss documents per se. Use isds_get_signed_received_message(),
10093 * if you are interested in documents (content). OTOH, only this function
10094 * can get list events message has gone through. */
10095 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10096 const char *message_id, struct isds_message **message) {
10098 isds_error err = IE_SUCCESS;
10099 #if HAVE_LIBCURL
10100 xmlDocPtr response = NULL;
10101 xmlChar *code = NULL, *status_message = NULL;
10102 void *raw = NULL;
10103 size_t raw_length = 0;
10104 #endif
10106 if (!context) return IE_INVALID_CONTEXT;
10107 zfree(context->long_message);
10109 /* Free former message if any */
10110 if (!message) return IE_INVAL;
10111 isds_message_free(message);
10113 #if HAVE_LIBCURL
10114 /* Do request and check for success */
10115 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10116 BAD_CAST "GetSignedDeliveryInfo", message_id,
10117 &response, NULL, NULL, &code, &status_message);
10118 if (err) goto leave;
10120 /* Find signed delivery info, extract it into raw and maybe free
10121 * response */
10122 err = find_extract_signed_data_free_response(context,
10123 (xmlChar *)message_id, &response,
10124 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10125 if (err) goto leave;
10127 /* Parse delivery info */
10128 err = isds_load_delivery_info(context,
10129 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10130 message, BUFFER_MOVE);
10131 if (err) goto leave;
10133 raw = NULL;
10135 leave:
10136 if (err) {
10137 isds_message_free(message);
10140 free(raw);
10141 free(code);
10142 free(status_message);
10143 xmlFreeDoc(response);
10145 if (!err)
10146 isds_log(ILF_ISDS, ILL_DEBUG,
10147 _("GetSignedDeliveryInfo request processed by server "
10148 "successfully.\n")
10150 #else /* not HAVE_LIBCURL */
10151 err = IE_NOTSUP;
10152 #endif
10153 return err;
10157 /* Download delivery info-sheet of given message identified by ID.
10158 * @context is session context
10159 * @message_id is message identifier (you can get them from
10160 * isds_get_list_of_{sent,received}_messages())
10161 * @message is automatically reallocated message retrieved from ISDS.
10162 * It will miss documents per se. Use isds_get_received_message(), if you are
10163 * interested in documents (content). OTOH, only this function can get list
10164 * of events message has gone through. */
10165 isds_error isds_get_delivery_info(struct isds_ctx *context,
10166 const char *message_id, struct isds_message **message) {
10168 isds_error err = IE_SUCCESS;
10169 #if HAVE_LIBCURL
10170 xmlDocPtr response = NULL;
10171 xmlChar *code = NULL, *status_message = NULL;
10172 xmlNodePtr delivery_node = NULL;
10173 void *raw = NULL;
10174 size_t raw_length = 0;
10175 #endif
10177 if (!context) return IE_INVALID_CONTEXT;
10178 zfree(context->long_message);
10180 /* Free former message if any */
10181 if (!message) return IE_INVAL;
10182 isds_message_free(message);
10184 #if HAVE_LIBCURL
10185 /* Do request and check for success */
10186 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10187 BAD_CAST "GetDeliveryInfo", message_id,
10188 &response, NULL, NULL, &code, &status_message);
10189 if (err) goto leave;
10192 /* Serialize delivery info */
10193 delivery_node = xmlDocGetRootElement(response);
10194 if (!delivery_node) {
10195 char *message_id_locale = _isds_utf82locale((char*) message_id);
10196 isds_printf_message(context,
10197 _("Server did not return any delivery info for ID `%s' "
10198 "on GetDeliveryInfo request"), message_id_locale);
10199 free(message_id_locale);
10200 err = IE_ISDS;
10201 goto leave;
10203 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10204 if (err) goto leave;
10206 /* Parse delivery info */
10207 /* TODO: Here we parse the response second time. We could single delivery
10208 * parser from isds_load_delivery_info() to make things faster. */
10209 err = isds_load_delivery_info(context,
10210 RAWTYPE_DELIVERYINFO, raw, raw_length,
10211 message, BUFFER_MOVE);
10212 if (err) goto leave;
10214 raw = NULL;
10217 leave:
10218 if (err) {
10219 isds_message_free(message);
10222 free(raw);
10223 free(code);
10224 free(status_message);
10225 xmlFreeDoc(response);
10227 if (!err)
10228 isds_log(ILF_ISDS, ILL_DEBUG,
10229 _("GetDeliveryInfo request processed by server "
10230 "successfully.\n")
10232 #else /* not HAVE_LIBCURL */
10233 err = IE_NOTSUP;
10234 #endif
10235 return err;
10239 /* Download incoming message identified by ID.
10240 * @context is session context
10241 * @message_id is message identifier (you can get them from
10242 * isds_get_list_of_received_messages())
10243 * @message is automatically reallocated message retrieved from ISDS */
10244 isds_error isds_get_received_message(struct isds_ctx *context,
10245 const char *message_id, struct isds_message **message) {
10247 isds_error err = IE_SUCCESS;
10248 #if HAVE_LIBCURL
10249 xmlDocPtr response = NULL;
10250 void *xml_stream = NULL;
10251 size_t xml_stream_length;
10252 xmlChar *code = NULL, *status_message = NULL;
10253 xmlXPathContextPtr xpath_ctx = NULL;
10254 xmlXPathObjectPtr result = NULL;
10255 char *phys_path = NULL;
10256 size_t phys_start, phys_end;
10257 #endif
10259 if (!context) return IE_INVALID_CONTEXT;
10260 zfree(context->long_message);
10262 /* Free former message if any */
10263 if (NULL == message) return IE_INVAL;
10264 if (message) isds_message_free(message);
10266 #if HAVE_LIBCURL
10267 /* Do request and check for success */
10268 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10269 BAD_CAST "MessageDownload", message_id,
10270 &response, &xml_stream, &xml_stream_length,
10271 &code, &status_message);
10272 if (err) goto leave;
10274 /* Extract data */
10275 xpath_ctx = xmlXPathNewContext(response);
10276 if (!xpath_ctx) {
10277 err = IE_ERROR;
10278 goto leave;
10280 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10281 err = IE_ERROR;
10282 goto leave;
10284 result = xmlXPathEvalExpression(
10285 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10286 xpath_ctx);
10287 if (!result) {
10288 err = IE_ERROR;
10289 goto leave;
10291 /* Empty response */
10292 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10293 char *message_id_locale = _isds_utf82locale((char*) message_id);
10294 isds_printf_message(context,
10295 _("Server did not return any message for ID `%s' "
10296 "on MessageDownload request"), message_id_locale);
10297 free(message_id_locale);
10298 err = IE_ISDS;
10299 goto leave;
10301 /* More messages */
10302 if (result->nodesetval->nodeNr > 1) {
10303 char *message_id_locale = _isds_utf82locale((char*) message_id);
10304 isds_printf_message(context,
10305 _("Server did return more messages for ID `%s' "
10306 "on MessageDownload request"), message_id_locale);
10307 free(message_id_locale);
10308 err = IE_ISDS;
10309 goto leave;
10311 /* One message */
10312 xpath_ctx->node = result->nodesetval->nodeTab[0];
10314 /* Extract the message */
10315 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10316 if (err) goto leave;
10318 /* Locate raw XML blob */
10319 phys_path = strdup(
10320 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10321 PHYSXML_ELEMENT_SEPARATOR
10322 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10323 PHYSXML_ELEMENT_SEPARATOR
10324 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10326 if (!phys_path) {
10327 err = IE_NOMEM;
10328 goto leave;
10330 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10331 phys_path, &phys_start, &phys_end);
10332 zfree(phys_path);
10333 if (err) {
10334 isds_log_message(context,
10335 _("Substring with isds:MessageDownloadResponse element "
10336 "could not be located in raw SOAP message"));
10337 goto leave;
10339 /* Save XML blob */
10340 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10341 &(*message)->raw_length);*/
10342 /* TODO: Store name space declarations from ancestors */
10343 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10344 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10345 (*message)->raw_length = phys_end - phys_start + 1;
10346 (*message)->raw = malloc((*message)->raw_length);
10347 if (!(*message)->raw) {
10348 err = IE_NOMEM;
10349 goto leave;
10351 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10354 leave:
10355 if (err) {
10356 isds_message_free(message);
10359 free(phys_path);
10361 xmlXPathFreeObject(result);
10362 xmlXPathFreeContext(xpath_ctx);
10364 free(code);
10365 free(status_message);
10366 free(xml_stream);
10367 if (!*message || !(*message)->xml) {
10368 xmlFreeDoc(response);
10371 if (!err)
10372 isds_log(ILF_ISDS, ILL_DEBUG,
10373 _("MessageDownload request processed by server "
10374 "successfully.\n")
10376 #else /* not HAVE_LIBCURL */
10377 err = IE_NOTSUP;
10378 #endif
10379 return err;
10383 /* Load message of any type from buffer.
10384 * @context is session context
10385 * @raw_type defines content type of @buffer. Only message types are allowed.
10386 * @buffer is message raw representation. Format (CMS, plain signed,
10387 * message direction) is defined in @raw_type. You can retrieve such data
10388 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10389 * @length is length of buffer in bytes.
10390 * @message is automatically reallocated message parsed from @buffer.
10391 * @strategy selects how buffer will be attached into raw isds_message member.
10392 * */
10393 isds_error isds_load_message(struct isds_ctx *context,
10394 const isds_raw_type raw_type, const void *buffer, const size_t length,
10395 struct isds_message **message, const isds_buffer_strategy strategy) {
10397 isds_error err = IE_SUCCESS;
10398 void *xml_stream = NULL;
10399 size_t xml_stream_length = 0;
10400 message_ns_type message_ns;
10401 xmlDocPtr message_doc = NULL;
10402 xmlXPathContextPtr xpath_ctx = NULL;
10403 xmlXPathObjectPtr result = NULL;
10405 if (!context) return IE_INVALID_CONTEXT;
10406 zfree(context->long_message);
10407 if (!message) return IE_INVAL;
10408 isds_message_free(message);
10409 if (!buffer) return IE_INVAL;
10412 /* Select buffer format and extract XML from CMS*/
10413 switch (raw_type) {
10414 case RAWTYPE_INCOMING_MESSAGE:
10415 message_ns = MESSAGE_NS_UNSIGNED;
10416 xml_stream = (void *) buffer;
10417 xml_stream_length = length;
10418 break;
10420 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10421 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10422 xml_stream = (void *) buffer;
10423 xml_stream_length = length;
10424 break;
10426 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10427 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10428 err = _isds_extract_cms_data(context, buffer, length,
10429 &xml_stream, &xml_stream_length);
10430 if (err) goto leave;
10431 break;
10433 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10434 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10435 xml_stream = (void *) buffer;
10436 xml_stream_length = length;
10437 break;
10439 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10440 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10441 err = _isds_extract_cms_data(context, buffer, length,
10442 &xml_stream, &xml_stream_length);
10443 if (err) goto leave;
10444 break;
10446 default:
10447 isds_log_message(context, _("Bad raw message representation type"));
10448 return IE_INVAL;
10449 break;
10452 isds_log(ILF_ISDS, ILL_DEBUG,
10453 _("Loading message:\n%.*s\nEnd of message\n"),
10454 xml_stream_length, xml_stream);
10456 /* Convert messages XML stream into XPath context */
10457 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10458 if (!message_doc) {
10459 err = IE_XML;
10460 goto leave;
10462 xpath_ctx = xmlXPathNewContext(message_doc);
10463 if (!xpath_ctx) {
10464 err = IE_ERROR;
10465 goto leave;
10467 /* XXX: Standard name space for unsigned incoming direction:
10468 * http://isds.czechpoint.cz/v20/
10470 * XXX: Name spaces mangled for signed outgoing direction:
10471 * http://isds.czechpoint.cz/v20/SentMessage:
10473 * <q:MessageDownloadResponse
10474 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10475 * <q:dmReturnedMessage>
10476 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10477 * <p:dmID>151916</p:dmID>
10478 * ...
10479 * </p:dmDm>
10480 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10481 * ...
10482 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10483 * </q:dmReturnedMessage>
10484 * </q:MessageDownloadResponse>
10486 * XXX: Name spaces mangled for signed incoming direction:
10487 * http://isds.czechpoint.cz/v20/message:
10489 * <q:MessageDownloadResponse
10490 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10491 * <q:dmReturnedMessage>
10492 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10493 * <p:dmID>151916</p:dmID>
10494 * ...
10495 * </p:dmDm>
10496 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10497 * ...
10498 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10499 * </q:dmReturnedMessage>
10500 * </q:MessageDownloadResponse>
10502 * Stupidity of ISDS developers is unlimited */
10503 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10504 err = IE_ERROR;
10505 goto leave;
10507 result = xmlXPathEvalExpression(
10508 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10509 xpath_ctx);
10510 if (!result) {
10511 err = IE_ERROR;
10512 goto leave;
10514 /* Empty message */
10515 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10516 isds_printf_message(context,
10517 _("XML document does not contain "
10518 "sisds:dmReturnedMessage element"));
10519 err = IE_ISDS;
10520 goto leave;
10522 /* More messages */
10523 if (result->nodesetval->nodeNr > 1) {
10524 isds_printf_message(context,
10525 _("XML document has more sisds:dmReturnedMessage elements"));
10526 err = IE_ISDS;
10527 goto leave;
10529 /* One message */
10530 xpath_ctx->node = result->nodesetval->nodeTab[0];
10532 /* Extract the message */
10533 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10534 if (err) goto leave;
10536 /* Append raw buffer into message */
10537 (*message)->raw_type = raw_type;
10538 switch (strategy) {
10539 case BUFFER_DONT_STORE:
10540 break;
10541 case BUFFER_COPY:
10542 (*message)->raw = malloc(length);
10543 if (!(*message)->raw) {
10544 err = IE_NOMEM;
10545 goto leave;
10547 memcpy((*message)->raw, buffer, length);
10548 (*message)->raw_length = length;
10549 break;
10550 case BUFFER_MOVE:
10551 (*message)->raw = (void *) buffer;
10552 (*message)->raw_length = length;
10553 break;
10554 default:
10555 err = IE_ENUM;
10556 goto leave;
10560 leave:
10561 if (err) {
10562 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10563 isds_message_free(message);
10566 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10567 xmlXPathFreeObject(result);
10568 xmlXPathFreeContext(xpath_ctx);
10569 if (!*message || !(*message)->xml) {
10570 xmlFreeDoc(message_doc);
10573 if (!err)
10574 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10575 return err;
10579 /* Determine type of raw message or delivery info according some heuristics.
10580 * It does not validate the raw blob.
10581 * @context is session context
10582 * @raw_type returns content type of @buffer. Valid only if exit code of this
10583 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10584 * reallocated memory.
10585 * @buffer is message raw representation.
10586 * @length is length of buffer in bytes. */
10587 isds_error isds_guess_raw_type(struct isds_ctx *context,
10588 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10589 isds_error err;
10590 void *xml_stream = NULL;
10591 size_t xml_stream_length = 0;
10592 xmlDocPtr document = NULL;
10593 xmlNodePtr root = NULL;
10595 if (!context) return IE_INVALID_CONTEXT;
10596 zfree(context->long_message);
10597 if (length == 0 || !buffer) return IE_INVAL;
10598 if (!raw_type) return IE_INVAL;
10600 /* Try CMS */
10601 err = _isds_extract_cms_data(context, buffer, length,
10602 &xml_stream, &xml_stream_length);
10603 if (err) {
10604 xml_stream = (void *) buffer;
10605 xml_stream_length = (size_t) length;
10606 err = IE_SUCCESS;
10609 /* Try XML */
10610 document = xmlParseMemory(xml_stream, xml_stream_length);
10611 if (!document) {
10612 isds_printf_message(context,
10613 _("Could not parse data as XML document"));
10614 err = IE_NOTSUP;
10615 goto leave;
10618 /* Get root element */
10619 root = xmlDocGetRootElement(document);
10620 if (!root) {
10621 isds_printf_message(context,
10622 _("XML document is missing root element"));
10623 err = IE_XML;
10624 goto leave;
10627 if (!root->ns || !root->ns->href) {
10628 isds_printf_message(context,
10629 _("Root element does not belong to any name space"));
10630 err = IE_NOTSUP;
10631 goto leave;
10634 /* Test name space */
10635 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10636 if (xml_stream == buffer)
10637 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10638 else
10639 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10640 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10641 if (xml_stream == buffer)
10642 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10643 else
10644 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10645 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10646 if (xml_stream == buffer)
10647 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10648 else
10649 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10650 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10651 if (xml_stream != buffer) {
10652 isds_printf_message(context,
10653 _("Document in ISDS name space is encapsulated into CMS" ));
10654 err = IE_NOTSUP;
10655 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10656 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10657 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10658 *raw_type = RAWTYPE_DELIVERYINFO;
10659 else {
10660 isds_printf_message(context,
10661 _("Unknown root element in ISDS name space"));
10662 err = IE_NOTSUP;
10664 } else {
10665 isds_printf_message(context,
10666 _("Unknown name space"));
10667 err = IE_NOTSUP;
10670 leave:
10671 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10672 xmlFreeDoc(document);
10673 return err;
10677 /* Download signed incoming/outgoing message identified by ID.
10678 * @context is session context
10679 * @output is true for outgoing message, false for incoming message
10680 * @message_id is message identifier (you can get them from
10681 * isds_get_list_of_{sent,received}_messages())
10682 * @message is automatically reallocated message retrieved from ISDS. The raw
10683 * member will be filled with PKCS#7 structure in DER format. */
10684 static isds_error isds_get_signed_message(struct isds_ctx *context,
10685 const _Bool outgoing, const char *message_id,
10686 struct isds_message **message) {
10688 isds_error err = IE_SUCCESS;
10689 #if HAVE_LIBCURL
10690 xmlDocPtr response = NULL;
10691 xmlChar *code = NULL, *status_message = NULL;
10692 xmlXPathContextPtr xpath_ctx = NULL;
10693 xmlXPathObjectPtr result = NULL;
10694 char *encoded_structure = NULL;
10695 void *raw = NULL;
10696 size_t raw_length = 0;
10697 #endif
10699 if (!context) return IE_INVALID_CONTEXT;
10700 zfree(context->long_message);
10701 if (!message) return IE_INVAL;
10702 isds_message_free(message);
10704 #if HAVE_LIBCURL
10705 /* Do request and check for success */
10706 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10707 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10708 BAD_CAST "SignedMessageDownload",
10709 message_id, &response, NULL, NULL, &code, &status_message);
10710 if (err) goto leave;
10712 /* Find signed message, extract it into raw and maybe free
10713 * response */
10714 err = find_extract_signed_data_free_response(context,
10715 (xmlChar *)message_id, &response,
10716 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10717 BAD_CAST "SignedMessageDownload",
10718 &raw, &raw_length);
10719 if (err) goto leave;
10721 /* Parse message */
10722 err = isds_load_message(context,
10723 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10724 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10725 raw, raw_length, message, BUFFER_MOVE);
10726 if (err) goto leave;
10728 raw = NULL;
10730 leave:
10731 if (err) {
10732 isds_message_free(message);
10735 free(encoded_structure);
10736 xmlXPathFreeObject(result);
10737 xmlXPathFreeContext(xpath_ctx);
10738 free(raw);
10740 free(code);
10741 free(status_message);
10742 xmlFreeDoc(response);
10744 if (!err)
10745 isds_log(ILF_ISDS, ILL_DEBUG,
10746 (outgoing) ?
10747 _("SignedSentMessageDownload request processed by server "
10748 "successfully.\n") :
10749 _("SignedMessageDownload request processed by server "
10750 "successfully.\n")
10752 #else /* not HAVE_LIBCURL */
10753 err = IE_NOTSUP;
10754 #endif
10755 return err;
10759 /* Download signed incoming message identified by ID.
10760 * @context is session context
10761 * @message_id is message identifier (you can get them from
10762 * isds_get_list_of_received_messages())
10763 * @message is automatically reallocated message retrieved from ISDS. The raw
10764 * member will be filled with PKCS#7 structure in DER format. */
10765 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10766 const char *message_id, struct isds_message **message) {
10767 return isds_get_signed_message(context, 0, message_id, message);
10771 /* Download signed outgoing message identified by ID.
10772 * @context is session context
10773 * @message_id is message identifier (you can get them from
10774 * isds_get_list_of_sent_messages())
10775 * @message is automatically reallocated message retrieved from ISDS. The raw
10776 * member will be filled with PKCS#7 structure in DER format. */
10777 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10778 const char *message_id, struct isds_message **message) {
10779 return isds_get_signed_message(context, 1, message_id, message);
10783 /* Get type and name of user who sent a message identified by ID.
10784 * @context is session context
10785 * @message_id is message identifier
10786 * @sender_type is pointer to automatically allocated type of sender detected
10787 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10788 * library or to the server, NULL will be returned. Pass NULL if you don't
10789 * care about it.
10790 * @raw_sender_type is automatically reallocated UTF-8 string describing
10791 * sender type or NULL if not known to server. Pass NULL if you don't care.
10792 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10793 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10794 isds_error isds_get_message_sender(struct isds_ctx *context,
10795 const char *message_id, isds_sender_type **sender_type,
10796 char **raw_sender_type, char **sender_name) {
10797 isds_error err = IE_SUCCESS;
10798 #if HAVE_LIBCURL
10799 xmlDocPtr response = NULL;
10800 xmlChar *code = NULL, *status_message = NULL;
10801 xmlXPathContextPtr xpath_ctx = NULL;
10802 xmlXPathObjectPtr result = NULL;
10803 char *type_string = NULL;
10804 #endif
10806 if (!context) return IE_INVALID_CONTEXT;
10807 zfree(context->long_message);
10808 if (sender_type) zfree(*sender_type);
10809 if (raw_sender_type) zfree(*raw_sender_type);
10810 if (sender_name) zfree(*sender_name);
10811 if (!message_id) return IE_INVAL;
10813 #if HAVE_LIBCURL
10814 /* Do request and check for success */
10815 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10816 BAD_CAST "GetMessageAuthor",
10817 message_id, &response, NULL, NULL, &code, &status_message);
10818 if (err) goto leave;
10820 /* Extract data */
10821 xpath_ctx = xmlXPathNewContext(response);
10822 if (!xpath_ctx) {
10823 err = IE_ERROR;
10824 goto leave;
10826 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10827 err = IE_ERROR;
10828 goto leave;
10830 result = xmlXPathEvalExpression(
10831 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10832 if (!result) {
10833 err = IE_ERROR;
10834 goto leave;
10836 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10837 isds_log_message(context,
10838 _("Missing GetMessageAuthorResponse element"));
10839 err = IE_ISDS;
10840 goto leave;
10842 if (result->nodesetval->nodeNr > 1) {
10843 isds_log_message(context,
10844 _("Multiple GetMessageAuthorResponse element"));
10845 err = IE_ISDS;
10846 goto leave;
10848 xpath_ctx->node = result->nodesetval->nodeTab[0];
10849 xmlXPathFreeObject(result); result = NULL;
10851 /* Fill output arguments in */
10852 EXTRACT_STRING("isds:userType", type_string);
10853 if (NULL != type_string) {
10854 if (NULL != sender_type) {
10855 *sender_type = calloc(1, sizeof(**sender_type));
10856 if (NULL == *sender_type) {
10857 err = IE_NOMEM;
10858 goto leave;
10861 err = string2isds_sender_type((xmlChar *)type_string,
10862 *sender_type);
10863 if (err) {
10864 zfree(*sender_type);
10865 if (err == IE_ENUM) {
10866 err = IE_SUCCESS;
10867 char *type_string_locale = _isds_utf82locale(type_string);
10868 isds_log(ILF_ISDS, ILL_WARNING,
10869 _("Unknown isds:userType value: %s"),
10870 type_string_locale);
10871 free(type_string_locale);
10876 if (NULL != sender_name)
10877 EXTRACT_STRING("isds:authorName", *sender_name);
10879 leave:
10880 if (err) {
10881 if (NULL != sender_type) zfree(*sender_type);
10882 zfree(type_string);
10883 if (NULL != sender_name) zfree(*sender_name);
10885 if (NULL != raw_sender_type) *raw_sender_type = type_string;
10887 xmlXPathFreeObject(result);
10888 xmlXPathFreeContext(xpath_ctx);
10890 free(code);
10891 free(status_message);
10892 xmlFreeDoc(response);
10894 if (!err)
10895 isds_log(ILF_ISDS, ILL_DEBUG,
10896 _("GetMessageAuthor request processed by server "
10897 "successfully.\n"));
10898 #else /* not HAVE_LIBCURL */
10899 err = IE_NOTSUP;
10900 #endif
10901 return err;
10905 /* Retrieve hash of message identified by ID stored in ISDS.
10906 * @context is session context
10907 * @message_id is message identifier
10908 * @hash is automatically reallocated message hash downloaded from ISDS.
10909 * Message must exist in system and must not be deleted. */
10910 isds_error isds_download_message_hash(struct isds_ctx *context,
10911 const char *message_id, struct isds_hash **hash) {
10913 isds_error err = IE_SUCCESS;
10914 #if HAVE_LIBCURL
10915 xmlDocPtr response = NULL;
10916 xmlChar *code = NULL, *status_message = NULL;
10917 xmlXPathContextPtr xpath_ctx = NULL;
10918 xmlXPathObjectPtr result = NULL;
10919 #endif
10921 if (!context) return IE_INVALID_CONTEXT;
10922 zfree(context->long_message);
10924 isds_hash_free(hash);
10926 #if HAVE_LIBCURL
10927 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10928 BAD_CAST "VerifyMessage", message_id,
10929 &response, NULL, NULL, &code, &status_message);
10930 if (err) goto leave;
10933 /* Extract data */
10934 xpath_ctx = xmlXPathNewContext(response);
10935 if (!xpath_ctx) {
10936 err = IE_ERROR;
10937 goto leave;
10939 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10940 err = IE_ERROR;
10941 goto leave;
10943 result = xmlXPathEvalExpression(
10944 BAD_CAST "/isds:VerifyMessageResponse",
10945 xpath_ctx);
10946 if (!result) {
10947 err = IE_ERROR;
10948 goto leave;
10950 /* Empty response */
10951 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10952 char *message_id_locale = _isds_utf82locale((char*) message_id);
10953 isds_printf_message(context,
10954 _("Server did not return any response for ID `%s' "
10955 "on VerifyMessage request"), message_id_locale);
10956 free(message_id_locale);
10957 err = IE_ISDS;
10958 goto leave;
10960 /* More responses */
10961 if (result->nodesetval->nodeNr > 1) {
10962 char *message_id_locale = _isds_utf82locale((char*) message_id);
10963 isds_printf_message(context,
10964 _("Server did return more responses for ID `%s' "
10965 "on VerifyMessage request"), message_id_locale);
10966 free(message_id_locale);
10967 err = IE_ISDS;
10968 goto leave;
10970 /* One response */
10971 xpath_ctx->node = result->nodesetval->nodeTab[0];
10973 /* Extract the hash */
10974 err = find_and_extract_DmHash(context, hash, xpath_ctx);
10976 leave:
10977 if (err) {
10978 isds_hash_free(hash);
10981 xmlXPathFreeObject(result);
10982 xmlXPathFreeContext(xpath_ctx);
10984 free(code);
10985 free(status_message);
10986 xmlFreeDoc(response);
10988 if (!err)
10989 isds_log(ILF_ISDS, ILL_DEBUG,
10990 _("VerifyMessage request processed by server "
10991 "successfully.\n")
10993 #else /* not HAVE_LIBCURL */
10994 err = IE_NOTSUP;
10995 #endif
10996 return err;
11000 /* Erase message specified by @message_id from long term storage. Other
11001 * message cannot be erased on user request.
11002 * @context is session context
11003 * @message_id is message identifier.
11004 * @incoming is true for incoming message, false for outgoing message.
11005 * @return
11006 * IE_SUCCESS if message has ben removed
11007 * IE_INVAL if message does not exist in long term storage or message
11008 * belongs to different box
11009 * TODO: IE_NOEPRM if user has no permission to erase a message */
11010 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11011 const char *message_id, _Bool incoming) {
11012 isds_error err = IE_SUCCESS;
11013 #if HAVE_LIBCURL
11014 xmlNodePtr request = NULL, node;
11015 xmlNsPtr isds_ns = NULL;
11016 xmlDocPtr response = NULL;
11017 xmlChar *code = NULL, *status_message = NULL;
11018 #endif
11020 if (!context) return IE_INVALID_CONTEXT;
11021 zfree(context->long_message);
11022 if (NULL == message_id) return IE_INVAL;
11024 /* Check if connection is established
11025 * TODO: This check should be done downstairs. */
11026 if (!context->curl) return IE_CONNECTION_CLOSED;
11028 #if HAVE_LIBCURL
11029 /* Build request */
11030 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11031 if (!request) {
11032 isds_log_message(context,
11033 _("Could build EraseMessage request"));
11034 return IE_ERROR;
11036 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11037 if(!isds_ns) {
11038 isds_log_message(context, _("Could not create ISDS name space"));
11039 xmlFreeNode(request);
11040 return IE_ERROR;
11042 xmlSetNs(request, isds_ns);
11044 err = validate_message_id_length(context, (xmlChar *) message_id);
11045 if (err) goto leave;
11046 INSERT_STRING(request, "dmID", message_id);
11048 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11051 /* Send request */
11052 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11053 "message ID %s to ISDS\n"), message_id);
11054 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11055 xmlFreeNode(request); request = NULL;
11057 if (err) {
11058 isds_log(ILF_ISDS, ILL_DEBUG,
11059 _("Processing ISDS response on EraseMessage request "
11060 "failed\n"));
11061 goto leave;
11064 /* Check for response status */
11065 err = isds_response_status(context, SERVICE_DM_INFO, response,
11066 &code, &status_message, NULL);
11067 if (err) {
11068 isds_log(ILF_ISDS, ILL_DEBUG,
11069 _("ISDS response on EraseMessage request is missing "
11070 "status\n"));
11071 goto leave;
11074 /* Check server status code */
11075 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11076 isds_log_message(context, _("Message to erase belongs to other box"));
11077 err = IE_INVAL;
11078 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11079 isds_log_message(context, _("Message to erase is not saved in "
11080 "long term storage or the direction does not match"));
11081 err = IE_INVAL;
11082 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11083 char *code_locale = _isds_utf82locale((char*) code);
11084 char *message_locale = _isds_utf82locale((char*) status_message);
11085 isds_log(ILF_ISDS, ILL_DEBUG,
11086 _("Server refused EraseMessage request "
11087 "(code=%s, message=%s)\n"),
11088 code_locale, message_locale);
11089 isds_log_message(context, message_locale);
11090 free(code_locale);
11091 free(message_locale);
11092 err = IE_ISDS;
11093 goto leave;
11096 leave:
11097 free(code);
11098 free(status_message);
11099 xmlFreeDoc(response);
11100 xmlFreeNode(request);
11102 if (!err)
11103 isds_log(ILF_ISDS, ILL_DEBUG,
11104 _("EraseMessage request processed by server "
11105 "successfully.\n")
11107 #else /* not HAVE_LIBCURL */
11108 err = IE_NOTSUP;
11109 #endif
11110 return err;
11114 /* Mark message as read. This is a transactional commit function to acknowledge
11115 * to ISDS the message has been downloaded and processed by client properly.
11116 * @context is session context
11117 * @message_id is message identifier. */
11118 isds_error isds_mark_message_read(struct isds_ctx *context,
11119 const char *message_id) {
11121 isds_error err = IE_SUCCESS;
11122 #if HAVE_LIBCURL
11123 xmlDocPtr response = NULL;
11124 xmlChar *code = NULL, *status_message = NULL;
11125 #endif
11127 if (!context) return IE_INVALID_CONTEXT;
11128 zfree(context->long_message);
11130 #if HAVE_LIBCURL
11131 /* Do request and check for success */
11132 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11133 BAD_CAST "MarkMessageAsDownloaded", message_id,
11134 &response, NULL, NULL, &code, &status_message);
11136 free(code);
11137 free(status_message);
11138 xmlFreeDoc(response);
11140 if (!err)
11141 isds_log(ILF_ISDS, ILL_DEBUG,
11142 _("MarkMessageAsDownloaded request processed by server "
11143 "successfully.\n")
11145 #else /* not HAVE_LIBCURL */
11146 err = IE_NOTSUP;
11147 #endif
11148 return err;
11152 /* Mark message as received by recipient. This is applicable only to
11153 * commercial message. Use envelope->dmType message member to distinguish
11154 * commercial message from government message. Government message is
11155 * received automatically (by law), commercial message on recipient request.
11156 * @context is session context
11157 * @message_id is message identifier. */
11158 isds_error isds_mark_message_received(struct isds_ctx *context,
11159 const char *message_id) {
11161 isds_error err = IE_SUCCESS;
11162 #if HAVE_LIBCURL
11163 xmlDocPtr response = NULL;
11164 xmlChar *code = NULL, *status_message = NULL;
11165 #endif
11167 if (!context) return IE_INVALID_CONTEXT;
11168 zfree(context->long_message);
11170 #if HAVE_LIBCURL
11171 /* Do request and check for success */
11172 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11173 BAD_CAST "ConfirmDelivery", message_id,
11174 &response, NULL, NULL, &code, &status_message);
11176 free(code);
11177 free(status_message);
11178 xmlFreeDoc(response);
11180 if (!err)
11181 isds_log(ILF_ISDS, ILL_DEBUG,
11182 _("ConfirmDelivery request processed by server "
11183 "successfully.\n")
11185 #else /* not HAVE_LIBCURL */
11186 err = IE_NOTSUP;
11187 #endif
11188 return err;
11192 /* Send document for authorized conversion into Czech POINT system.
11193 * This is public anonymous service, no log-in necessary. Special context is
11194 * used to reuse keep-a-live HTTPS connection.
11195 * @context is Czech POINT session context. DO NOT use context connected to
11196 * ISDS server. Use new context or context used by this function previously.
11197 * @document is document to convert. Only data, data_length, dmFileDescr and
11198 * is_xml members are significant. Be ware that not all document formats can be
11199 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11200 * @id is reallocated identifier assigned by Czech POINT system to
11201 * your document on submit. Use is to tell it to Czech POINT officer.
11202 * @date is reallocated document submit date (submitted documents
11203 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11204 * value. */
11205 isds_error czp_convert_document(struct isds_ctx *context,
11206 const struct isds_document *document,
11207 char **id, struct tm **date) {
11208 isds_error err = IE_SUCCESS;
11209 #if HAVE_LIBCURL
11210 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11211 xmlNodePtr request = NULL, node;
11212 xmlDocPtr response = NULL;
11214 xmlXPathContextPtr xpath_ctx = NULL;
11215 xmlXPathObjectPtr result = NULL;
11216 long int status = -1;
11217 long int *status_ptr = &status;
11218 char *string = NULL;
11219 #endif
11222 if (!context) return IE_INVALID_CONTEXT;
11223 zfree(context->long_message);
11224 if (!document || !id || !date) return IE_INVAL;
11226 if (document->is_xml) {
11227 isds_log_message(context,
11228 _("XML documents cannot be submitted to conversion"));
11229 return IE_NOTSUP;
11232 /* Free output arguments */
11233 zfree(*id);
11234 zfree(*date);
11236 #if HAVE_LIBCURL
11237 /* Store configuration */
11238 context->type = CTX_TYPE_CZP;
11239 free(context->url);
11240 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11241 if (!(context->url))
11242 return IE_NOMEM;
11244 /* Prepare CURL handle if not yet connected */
11245 if (!context->curl) {
11246 context->curl = curl_easy_init();
11247 if (!(context->curl))
11248 return IE_ERROR;
11251 /* Build conversion request */
11252 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11253 if (!request) {
11254 isds_log_message(context,
11255 _("Could not build Czech POINT conversion request"));
11256 return IE_ERROR;
11258 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11259 if(!deposit_ns) {
11260 isds_log_message(context,
11261 _("Could not create Czech POINT deposit name space"));
11262 xmlFreeNode(request);
11263 return IE_ERROR;
11265 xmlSetNs(request, deposit_ns);
11267 /* Insert children. They are in empty namespace! */
11268 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11269 if(!empty_ns) {
11270 isds_log_message(context, _("Could not create empty name space"));
11271 err = IE_ERROR;
11272 goto leave;
11274 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11275 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11276 document->dmFileDescr);
11278 /* Document encoded in Base64 */
11279 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11280 document->data, document->data_length);
11281 if (err) goto leave;
11283 isds_log(ILF_ISDS, ILL_DEBUG,
11284 _("Submitting document for conversion into Czech POINT deposit"));
11286 /* Send conversion request */
11287 err = _czp_czpdeposit(context, request, &response);
11288 xmlFreeNode(request); request = NULL;
11290 if (err) {
11291 czp_do_close_connection(context);
11292 goto leave;
11296 /* Extract response */
11297 xpath_ctx = xmlXPathNewContext(response);
11298 if (!xpath_ctx) {
11299 err = IE_ERROR;
11300 goto leave;
11302 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11303 err = IE_ERROR;
11304 goto leave;
11306 result = xmlXPathEvalExpression(
11307 BAD_CAST "/deposit:saveDocumentResponse/return",
11308 xpath_ctx);
11309 if (!result) {
11310 err = IE_ERROR;
11311 goto leave;
11313 /* Empty response */
11314 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11315 isds_printf_message(context,
11316 _("Missing `return' element in Czech POINT deposit response"));
11317 err = IE_ISDS;
11318 goto leave;
11320 /* More responses */
11321 if (result->nodesetval->nodeNr > 1) {
11322 isds_printf_message(context,
11323 _("Multiple `return' element in Czech POINT deposit response"));
11324 err = IE_ISDS;
11325 goto leave;
11327 /* One response */
11328 xpath_ctx->node = result->nodesetval->nodeTab[0];
11330 /* Get status */
11331 EXTRACT_LONGINT("status", status_ptr, 1);
11332 if (status) {
11333 EXTRACT_STRING("statusMsg", string);
11334 char *string_locale = _isds_utf82locale(string);
11335 isds_printf_message(context,
11336 _("Czech POINT deposit refused document for conversion "
11337 "(code=%ld, message=%s)"),
11338 status, string_locale);
11339 free(string_locale);
11340 err = IE_ISDS;
11341 goto leave;
11344 /* Get document ID */
11345 EXTRACT_STRING("documentID", *id);
11347 /* Get submit date */
11348 EXTRACT_STRING("dateInserted", string);
11349 if (string) {
11350 *date = calloc(1, sizeof(**date));
11351 if (!*date) {
11352 err = IE_NOMEM;
11353 goto leave;
11355 err = _isds_datestring2tm((xmlChar *)string, *date);
11356 if (err) {
11357 if (err == IE_NOTSUP) {
11358 err = IE_ISDS;
11359 char *string_locale = _isds_utf82locale(string);
11360 isds_printf_message(context,
11361 _("Invalid dateInserted value: %s"), string_locale);
11362 free(string_locale);
11364 goto leave;
11368 leave:
11369 free(string);
11370 xmlXPathFreeObject(result);
11371 xmlXPathFreeContext(xpath_ctx);
11373 xmlFreeDoc(response);
11374 xmlFreeNode(request);
11376 if (!err) {
11377 char *id_locale = _isds_utf82locale((char *) *id);
11378 isds_log(ILF_ISDS, ILL_DEBUG,
11379 _("Document %s has been submitted for conversion "
11380 "to server successfully\n"), id_locale);
11381 free(id_locale);
11383 #else /* not HAVE_LIBCURL */
11384 err = IE_NOTSUP;
11385 #endif
11386 return err;
11390 /* Close possibly opened connection to Czech POINT document deposit.
11391 * @context is Czech POINT session context. */
11392 isds_error czp_close_connection(struct isds_ctx *context) {
11393 if (!context) return IE_INVALID_CONTEXT;
11394 zfree(context->long_message);
11395 #if HAVE_LIBCURL
11396 return czp_do_close_connection(context);
11397 #else
11398 return IE_NOTSUP;
11399 #endif
11403 /* Send request for new box creation in testing ISDS instance.
11404 * It's not possible to request for a production box currently, as it
11405 * communicates via e-mail.
11406 * XXX: This function does not work either. Server complains about invalid
11407 * e-mail address.
11408 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11409 * this function
11410 * @context is special session context for box creation request. DO NOT use
11411 * standard context as it could reveal your password. Use fresh new context or
11412 * context previously used by this function.
11413 * @box is box description to create including single primary user (in case of
11414 * FO box type). It outputs box ID assigned by ISDS in dbID element.
11415 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11416 * box, or contact address of PFO box owner). The email member is mandatory as
11417 * it will be used to deliver credentials.
11418 * @former_names is former name of box owner. Pass NULL if you don't care.
11419 * @approval is optional external approval of box manipulation
11420 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11421 * NULL, if you don't care.*/
11422 isds_error isds_request_new_testing_box(struct isds_ctx *context,
11423 struct isds_DbOwnerInfo *box, const struct isds_list *users,
11424 const char *former_names, const struct isds_approval *approval,
11425 char **refnumber) {
11426 isds_error err = IE_SUCCESS;
11427 #if HAVE_LIBCURL
11428 xmlNodePtr request = NULL;
11429 xmlDocPtr response = NULL;
11430 xmlXPathContextPtr xpath_ctx = NULL;
11431 xmlXPathObjectPtr result = NULL;
11432 #endif
11435 if (!context) return IE_INVALID_CONTEXT;
11436 zfree(context->long_message);
11437 if (!box) return IE_INVAL;
11439 #if HAVE_LIBCURL
11440 if (!box->email || box->email[0] == '\0') {
11441 isds_log_message(context, _("E-mail field is mandatory"));
11442 return IE_INVAL;
11445 /* Scratch box ID */
11446 zfree(box->dbID);
11448 /* Store configuration */
11449 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
11450 free(context->url);
11451 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
11452 if (!(context->url))
11453 return IE_NOMEM;
11455 /* Prepare CURL handle if not yet connected */
11456 if (!context->curl) {
11457 context->curl = curl_easy_init();
11458 if (!(context->curl))
11459 return IE_ERROR;
11462 /* Build CreateDataBox request */
11463 err = build_CreateDBInput_request(context,
11464 &request, BAD_CAST "CreateDataBox",
11465 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
11466 if (err) goto leave;
11468 /* Send it to server and process response */
11469 err = send_destroy_request_check_response(context,
11470 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
11471 &response, (xmlChar **) refnumber, NULL);
11472 if (err) goto leave;
11474 /* Extract box ID */
11475 xpath_ctx = xmlXPathNewContext(response);
11476 if (!xpath_ctx) {
11477 err = IE_ERROR;
11478 goto leave;
11480 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11481 err = IE_ERROR;
11482 goto leave;
11484 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
11486 leave:
11487 xmlXPathFreeObject(result);
11488 xmlXPathFreeContext(xpath_ctx);
11489 xmlFreeDoc(response);
11490 xmlFreeNode(request);
11492 if (!err) {
11493 isds_log(ILF_ISDS, ILL_DEBUG,
11494 _("CreateDataBox request processed by server successfully.\n"));
11496 #else /* not HAVE_LIBCURL */
11497 err = IE_NOTSUP;
11498 #endif
11500 return err;
11504 /* Submit CMS signed message to ISDS to verify its originality. This is
11505 * stronger form of isds_verify_message_hash() because ISDS does more checks
11506 * than simple one (potentialy old weak) hash comparison.
11507 * @context is session context
11508 * @message is memory with raw CMS signed message bit stream
11509 * @length is @message size in bytes
11510 * @return
11511 * IE_SUCCESS if message originates in ISDS
11512 * IE_NOTEQUAL if message is unknown to ISDS
11513 * other code for other errors */
11514 isds_error isds_authenticate_message(struct isds_ctx *context,
11515 const void *message, size_t length) {
11516 isds_error err = IE_SUCCESS;
11517 #if HAVE_LIBCURL
11518 xmlNsPtr isds_ns = NULL;
11519 xmlNodePtr request = NULL;
11520 xmlDocPtr response = NULL;
11521 xmlXPathContextPtr xpath_ctx = NULL;
11522 xmlXPathObjectPtr result = NULL;
11523 _Bool *authentic = NULL;
11524 #endif
11526 if (!context) return IE_INVALID_CONTEXT;
11527 zfree(context->long_message);
11528 if (!message || length == 0) return IE_INVAL;
11530 #if HAVE_LIBCURL
11531 /* Check if connection is established
11532 * TODO: This check should be done downstairs. */
11533 if (!context->curl) return IE_CONNECTION_CLOSED;
11536 /* Build AuthenticateMessage request */
11537 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11538 if (!request) {
11539 isds_log_message(context,
11540 _("Could not build AuthenticateMessage request"));
11541 return IE_ERROR;
11543 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11544 if(!isds_ns) {
11545 isds_log_message(context, _("Could not create ISDS name space"));
11546 xmlFreeNode(request);
11547 return IE_ERROR;
11549 xmlSetNs(request, isds_ns);
11551 /* Insert Base64 encoded message */
11552 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11553 message, length);
11554 if (err) goto leave;
11556 /* Send request to server and process response */
11557 err = send_destroy_request_check_response(context,
11558 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11559 &response, NULL, NULL);
11560 if (err) goto leave;
11563 /* ISDS has decided */
11564 xpath_ctx = xmlXPathNewContext(response);
11565 if (!xpath_ctx) {
11566 err = IE_ERROR;
11567 goto leave;
11569 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11570 err = IE_ERROR;
11571 goto leave;
11574 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11576 if (!authentic) {
11577 isds_log_message(context,
11578 _("Server did not return any response on "
11579 "AuthenticateMessage request"));
11580 err = IE_ISDS;
11581 goto leave;
11583 if (*authentic) {
11584 isds_log(ILF_ISDS, ILL_DEBUG,
11585 _("ISDS authenticated the message successfully\n"));
11586 } else {
11587 isds_log_message(context, _("ISDS does not know the message"));
11588 err = IE_NOTEQUAL;
11592 leave:
11593 free(authentic);
11594 xmlXPathFreeObject(result);
11595 xmlXPathFreeContext(xpath_ctx);
11597 xmlFreeDoc(response);
11598 xmlFreeNode(request);
11599 #else /* not HAVE_LIBCURL */
11600 err = IE_NOTSUP;
11601 #endif
11603 return err;
11607 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11608 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11609 * be re-signed.
11610 * @context is session context
11611 * @input_data is memory with raw CMS signed message or delivery info bit
11612 * stream to re-sign
11613 * @input_length is @input_data size in bytes
11614 * @output_data is pointer to auto-allocated memory where to store re-signed
11615 * input data blob. Caller must free it.
11616 * @output_data is pointer where to store @output_data size in bytes
11617 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11618 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11619 * @return
11620 * IE_SUCCESS if CMS blob has been re-signed successfully
11621 * other code for other errors */
11622 isds_error isds_resign_message(struct isds_ctx *context,
11623 const void *input_data, size_t input_length,
11624 void **output_data, size_t *output_length, struct tm **valid_to) {
11625 isds_error err = IE_SUCCESS;
11626 #if HAVE_LIBCURL
11627 xmlNsPtr isds_ns = NULL;
11628 xmlNodePtr request = NULL;
11629 xmlDocPtr response = NULL;
11630 xmlXPathContextPtr xpath_ctx = NULL;
11631 xmlXPathObjectPtr result = NULL;
11632 char *string = NULL;
11633 const xmlChar *codes[] = {
11634 BAD_CAST "2200",
11635 BAD_CAST "2201",
11636 BAD_CAST "2204",
11637 BAD_CAST "2207",
11638 NULL
11640 const char *meanings[] = {
11641 "Message is bad",
11642 "Message is not original",
11643 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11644 "Time stamp could not been generated in time"
11646 const isds_error errors[] = {
11647 IE_INVAL,
11648 IE_NOTUNIQ,
11649 IE_INVAL,
11650 IE_ISDS,
11652 struct code_map_isds_error map = {
11653 .codes = codes,
11654 .meanings = meanings,
11655 .errors = errors
11657 #endif
11659 if (NULL != output_data) *output_data = NULL;
11660 if (NULL != output_length) *output_length = 0;
11661 if (NULL != valid_to) *valid_to = NULL;
11663 if (NULL == context) return IE_INVALID_CONTEXT;
11664 zfree(context->long_message);
11665 if (NULL == input_data || 0 == input_length) {
11666 isds_log_message(context, _("Empty CMS blob on input"));
11667 return IE_INVAL;
11669 if (NULL == output_data || NULL == output_length) {
11670 isds_log_message(context,
11671 _("NULL pointer provided for output CMS blob"));
11672 return IE_INVAL;
11675 #if HAVE_LIBCURL
11676 /* Check if connection is established
11677 * TODO: This check should be done downstairs. */
11678 if (!context->curl) return IE_CONNECTION_CLOSED;
11681 /* Build Re-signISDSDocument request */
11682 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11683 if (!request) {
11684 isds_log_message(context,
11685 _("Could not build Re-signISDSDocument request"));
11686 return IE_ERROR;
11688 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11689 if(!isds_ns) {
11690 isds_log_message(context, _("Could not create ISDS name space"));
11691 xmlFreeNode(request);
11692 return IE_ERROR;
11694 xmlSetNs(request, isds_ns);
11696 /* Insert Base64 encoded CMS blob */
11697 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11698 input_data, input_length);
11699 if (err) goto leave;
11701 /* Send request to server and process response */
11702 err = send_destroy_request_check_response(context,
11703 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11704 &response, NULL, &map);
11705 if (err) goto leave;
11708 /* Extract re-signed data */
11709 xpath_ctx = xmlXPathNewContext(response);
11710 if (!xpath_ctx) {
11711 err = IE_ERROR;
11712 goto leave;
11714 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11715 err = IE_ERROR;
11716 goto leave;
11718 result = xmlXPathEvalExpression(
11719 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11720 if (!result) {
11721 err = IE_ERROR;
11722 goto leave;
11724 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11725 isds_log_message(context,
11726 _("Missing Re-signISDSDocumentResponse element"));
11727 err = IE_ISDS;
11728 goto leave;
11730 if (result->nodesetval->nodeNr > 1) {
11731 isds_log_message(context,
11732 _("Multiple Re-signISDSDocumentResponse element"));
11733 err = IE_ISDS;
11734 goto leave;
11736 xpath_ctx->node = result->nodesetval->nodeTab[0];
11737 xmlXPathFreeObject(result); result = NULL;
11739 EXTRACT_STRING("isds:dmResultDoc", string);
11740 /* Decode non-empty data */
11741 if (NULL != string && string[0] != '\0') {
11742 *output_length = _isds_b64decode(string, output_data);
11743 if (*output_length == (size_t) -1) {
11744 isds_log_message(context,
11745 _("Error while Base64-decoding re-signed data"));
11746 err = IE_ERROR;
11747 goto leave;
11749 } else {
11750 isds_log_message(context, _("Server did not send re-signed data"));
11751 err = IE_ISDS;
11752 goto leave;
11754 zfree(string);
11756 if (NULL != valid_to) {
11757 /* Get time stamp expiration date */
11758 EXTRACT_STRING("isds:dmValidTo", string);
11759 if (NULL != string) {
11760 *valid_to = calloc(1, sizeof(**valid_to));
11761 if (!*valid_to) {
11762 err = IE_NOMEM;
11763 goto leave;
11765 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11766 if (err) {
11767 if (err == IE_NOTSUP) {
11768 err = IE_ISDS;
11769 char *string_locale = _isds_utf82locale(string);
11770 isds_printf_message(context,
11771 _("Invalid dmValidTo value: %s"), string_locale);
11772 free(string_locale);
11774 goto leave;
11779 leave:
11780 free(string);
11782 xmlXPathFreeObject(result);
11783 xmlXPathFreeContext(xpath_ctx);
11785 xmlFreeDoc(response);
11786 xmlFreeNode(request);
11787 #else /* not HAVE_LIBCURL */
11788 err = IE_NOTSUP;
11789 #endif
11791 return err;
11794 #undef INSERT_ELEMENT
11795 #undef CHECK_FOR_STRING_LENGTH
11796 #undef INSERT_STRING_ATTRIBUTE
11797 #undef INSERT_ULONGINTNOPTR
11798 #undef INSERT_ULONGINT
11799 #undef INSERT_LONGINT
11800 #undef INSERT_BOOLEAN
11801 #undef INSERT_SCALAR_BOOLEAN
11802 #undef INSERT_STRING
11803 #undef INSERT_STRING_WITH_NS
11804 #undef EXTRACT_STRING_ATTRIBUTE
11805 #undef EXTRACT_ULONGINT
11806 #undef EXTRACT_LONGINT
11807 #undef EXTRACT_BOOLEAN
11808 #undef EXTRACT_STRING
11811 /* Compute hash of message from raw representation and store it into envelope.
11812 * Original hash structure will be destroyed in envelope.
11813 * @context is session context
11814 * @message is message carrying raw XML message blob
11815 * @algorithm is desired hash algorithm to use */
11816 isds_error isds_compute_message_hash(struct isds_ctx *context,
11817 struct isds_message *message, const isds_hash_algorithm algorithm) {
11818 isds_error err = IE_SUCCESS;
11819 const char *nsuri;
11820 void *xml_stream = NULL;
11821 size_t xml_stream_length;
11822 size_t phys_start, phys_end;
11823 char *phys_path = NULL;
11824 struct isds_hash *new_hash = NULL;
11827 if (!context) return IE_INVALID_CONTEXT;
11828 zfree(context->long_message);
11829 if (!message) return IE_INVAL;
11831 if (!message->raw) {
11832 isds_log_message(context,
11833 _("Message does not carry raw representation"));
11834 return IE_INVAL;
11837 switch (message->raw_type) {
11838 case RAWTYPE_INCOMING_MESSAGE:
11839 nsuri = ISDS_NS;
11840 xml_stream = message->raw;
11841 xml_stream_length = message->raw_length;
11842 break;
11844 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11845 nsuri = SISDS_INCOMING_NS;
11846 xml_stream = message->raw;
11847 xml_stream_length = message->raw_length;
11848 break;
11850 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11851 nsuri = SISDS_INCOMING_NS;
11852 err = _isds_extract_cms_data(context,
11853 message->raw, message->raw_length,
11854 &xml_stream, &xml_stream_length);
11855 if (err) goto leave;
11856 break;
11858 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11859 nsuri = SISDS_OUTGOING_NS;
11860 xml_stream = message->raw;
11861 xml_stream_length = message->raw_length;
11862 break;
11864 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11865 nsuri = SISDS_OUTGOING_NS;
11866 err = _isds_extract_cms_data(context,
11867 message->raw, message->raw_length,
11868 &xml_stream, &xml_stream_length);
11869 if (err) goto leave;
11870 break;
11872 default:
11873 isds_log_message(context, _("Bad raw representation type"));
11874 return IE_INVAL;
11875 break;
11879 /* XXX: Hash is computed from original string representing isds:dmDm
11880 * subtree. That means no encoding, white space, xmlns attributes changes.
11881 * In other words, input for hash can be invalid XML stream. */
11882 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
11883 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11884 PHYSXML_ELEMENT_SEPARATOR,
11885 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
11886 PHYSXML_ELEMENT_SEPARATOR
11887 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
11888 err = IE_NOMEM;
11889 goto leave;
11891 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11892 phys_path, &phys_start, &phys_end);
11893 zfree(phys_path);
11894 if (err) {
11895 isds_log_message(context,
11896 _("Substring with isds:dmDM element could not be located "
11897 "in raw message"));
11898 goto leave;
11902 /* Compute hash */
11903 new_hash = calloc(1, sizeof(*new_hash));
11904 if (!new_hash) {
11905 err = IE_NOMEM;
11906 goto leave;
11908 new_hash->algorithm = algorithm;
11909 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
11910 new_hash);
11911 if (err) {
11912 isds_log_message(context, _("Could not compute message hash"));
11913 goto leave;
11916 /* Save computed hash */
11917 if (!message->envelope) {
11918 message->envelope = calloc(1, sizeof(*message->envelope));
11919 if (!message->envelope) {
11920 err = IE_NOMEM;
11921 goto leave;
11924 isds_hash_free(&message->envelope->hash);
11925 message->envelope->hash = new_hash;
11927 leave:
11928 if (err) {
11929 isds_hash_free(&new_hash);
11932 free(phys_path);
11933 if (xml_stream != message->raw) free(xml_stream);
11934 return err;
11938 /* Compare two hashes.
11939 * @h1 is first hash
11940 * @h2 is another hash
11941 * @return
11942 * IE_SUCCESS if hashes equal
11943 * IE_NOTUNIQ if hashes are comparable, but they don't equal
11944 * IE_ENUM if not comparable, but both structures defined
11945 * IE_INVAL if some of the structures are undefined (NULL)
11946 * IE_ERROR if internal error occurs */
11947 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
11948 if (h1 == NULL || h2 == NULL) return IE_INVAL;
11949 if (h1->algorithm != h2->algorithm) return IE_ENUM;
11950 if (h1->length != h2->length) return IE_ERROR;
11951 if (h1->length > 0 && !h1->value) return IE_ERROR;
11952 if (h2->length > 0 && !h2->value) return IE_ERROR;
11954 for (int i = 0; i < h1->length; i++) {
11955 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
11956 return IE_NOTEQUAL;
11958 return IE_SUCCESS;
11962 /* Check message has gone through ISDS by comparing message hash stored in
11963 * ISDS and locally computed hash. You must provide message with valid raw
11964 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
11965 * This is convenient wrapper for isds_download_message_hash(),
11966 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
11967 * @context is session context
11968 * @message is message with valid raw and envelope member; envelope->hash
11969 * member will be changed during function run. Use envelope on heap only.
11970 * @return
11971 * IE_SUCCESS if message originates in ISDS
11972 * IE_NOTEQUAL if message is unknown to ISDS
11973 * other code for other errors */
11974 isds_error isds_verify_message_hash(struct isds_ctx *context,
11975 struct isds_message *message) {
11976 isds_error err = IE_SUCCESS;
11977 struct isds_hash *downloaded_hash = NULL;
11979 if (!context) return IE_INVALID_CONTEXT;
11980 zfree(context->long_message);
11981 if (!message) return IE_INVAL;
11983 if (!message->envelope) {
11984 isds_log_message(context,
11985 _("Given message structure is missing envelope"));
11986 return IE_INVAL;
11988 if (!message->raw) {
11989 isds_log_message(context,
11990 _("Given message structure is missing raw representation"));
11991 return IE_INVAL;
11994 err = isds_download_message_hash(context, message->envelope->dmID,
11995 &downloaded_hash);
11996 if (err) goto leave;
11998 err = isds_compute_message_hash(context, message,
11999 downloaded_hash->algorithm);
12000 if (err) goto leave;
12002 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12004 leave:
12005 isds_hash_free(&downloaded_hash);
12006 return err;
12010 /* Search for document by document ID in list of documents. IDs are compared
12011 * as UTF-8 string.
12012 * @documents is list of isds_documents
12013 * @id is document identifier
12014 * @return first matching document or NULL. */
12015 const struct isds_document *isds_find_document_by_id(
12016 const struct isds_list *documents, const char *id) {
12017 const struct isds_list *item;
12018 const struct isds_document *document;
12020 for (item = documents; item; item = item->next) {
12021 document = (struct isds_document *) item->data;
12022 if (!document) continue;
12024 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12025 return document;
12028 return NULL;
12032 /* Normalize @mime_type to be proper MIME type.
12033 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12034 * guess regular MIME type (e.g. "application/pdf").
12035 * @mime_type is UTF-8 encoded MIME type to fix
12036 * @return original @mime_type if no better interpretation exists, or
12037 * constant static UTF-8 encoded string with proper MIME type. */
12038 const char *isds_normalize_mime_type(const char *mime_type) {
12039 if (!mime_type) return NULL;
12041 for (int offset = 0;
12042 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12043 offset += 2) {
12044 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12045 extension_map_mime[offset]))
12046 return (const char *) extension_map_mime[offset + 1];
12049 return mime_type;
12053 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12054 struct isds_message **message);
12055 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12056 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12057 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12058 struct isds_address **address);
12060 int isds_message_free(struct isds_message **message);
12061 int isds_address_free(struct isds_address **address);
12065 /* Makes known all relevant namespaces to given XPath context
12066 * @xpath_ctx is XPath context
12067 * @message_ns selects proper message name space. Unsigned and signed
12068 * messages and delivery info's differ in prefix and URI. */
12069 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12070 const message_ns_type message_ns) {
12071 const xmlChar *message_namespace = NULL;
12073 if (!xpath_ctx) return IE_ERROR;
12075 switch(message_ns) {
12076 case MESSAGE_NS_1:
12077 message_namespace = BAD_CAST ISDS1_NS; break;
12078 case MESSAGE_NS_UNSIGNED:
12079 message_namespace = BAD_CAST ISDS_NS; break;
12080 case MESSAGE_NS_SIGNED_INCOMING:
12081 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12082 case MESSAGE_NS_SIGNED_OUTGOING:
12083 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12084 case MESSAGE_NS_SIGNED_DELIVERY:
12085 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12086 default:
12087 return IE_ENUM;
12090 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12091 return IE_ERROR;
12092 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12093 return IE_ERROR;
12094 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12095 return IE_ERROR;
12096 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12097 return IE_ERROR;
12098 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12099 return IE_ERROR;
12100 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12101 return IE_ERROR;
12102 return IE_SUCCESS;