timeval.tv_sec does not have to be of type time_t
[libisds.git] / src / isds.c
blob05c837bc52cf252d91b2c5f87b610fe7551c987b
1 #include "isds_priv.h"
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include <stdint.h> /* For uint8_t and intmax_t */
8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
9 #include <inttypes.h> /* For PRIdMAX formatting macro */
10 #include "utils.h"
11 #if HAVE_LIBCURL
12 #include "soap.h"
13 #endif
14 #include "validator.h"
15 #include "crypto.h"
16 #include "physxml.h"
17 #include "system.h"
19 /* Global variables.
20 * Allocated in isds_init() and deallocated in isds_cleanup(). */
21 unsigned int log_facilities;
22 isds_log_level log_level;
23 isds_log_callback log_callback;
24 void *log_callback_data;
25 const char *version_gpgme = N_("n/a");
26 const char *version_gcrypt = N_("n/a");
27 const char *version_openssl = N_("n/a");
28 const char *version_expat = N_("n/a");
30 /* Locators */
31 /* Base URL of production ISDS instance */
32 const char isds_locator[] = "https://ws1.mojedatovaschranka.cz/";
33 const char isds_cert_locator[] = "https://ws1c.mojedatovaschranka.cz/";
34 const char isds_otp_locator[] = "https://www.mojedatovaschranka.cz/";
36 /* Base URL of production ISDS instance */
37 const char isds_testing_locator[] = "https://ws1.czebox.cz/";
38 const char isds_cert_testing_locator[] = "https://ws1c.czebox.cz/";
39 const char isds_otp_testing_locator[] = "https://www.czebox.cz/";
41 /* Extension to MIME type map */
42 static const xmlChar *extension_map_mime[] = {
43 BAD_CAST "cer", BAD_CAST "application/x-x509-ca-cert",
44 BAD_CAST "crt", BAD_CAST "application/x-x509-ca-cert",
45 BAD_CAST "der", BAD_CAST "application/x-x509-ca-cert",
46 BAD_CAST "doc", BAD_CAST "application/msword",
47 BAD_CAST "docx", BAD_CAST "application/vnd.openxmlformats-officedocument."
48 "wordprocessingml.document",
49 BAD_CAST "dbf", BAD_CAST "application/octet-stream",
50 BAD_CAST "prj", BAD_CAST "application/octet-stream",
51 BAD_CAST "qix", BAD_CAST "application/octet-stream",
52 BAD_CAST "sbn", BAD_CAST "application/octet-stream",
53 BAD_CAST "sbx", BAD_CAST "application/octet-stream",
54 BAD_CAST "shp", BAD_CAST "application/octet-stream",
55 BAD_CAST "shx", BAD_CAST "application/octet-stream",
56 BAD_CAST "dgn", BAD_CAST "application/octet-stream",
57 BAD_CAST "dwg", BAD_CAST "image/vnd.dwg",
58 BAD_CAST "edi", BAD_CAST "application/edifact",
59 BAD_CAST "fo", BAD_CAST "application/vnd.software602.filler.form+xml",
60 BAD_CAST "gfs", BAD_CAST "application/xml",
61 BAD_CAST "gml", BAD_CAST "application/xml",
62 BAD_CAST "gif", BAD_CAST "image/gif",
63 BAD_CAST "htm", BAD_CAST "text/html",
64 BAD_CAST "html", BAD_CAST "text/html",
65 BAD_CAST "isdoc", BAD_CAST "text/isdoc",
66 BAD_CAST "isdocx", BAD_CAST "text/isdocx",
67 BAD_CAST "jfif", BAD_CAST "image/jpeg",
68 BAD_CAST "jpg", BAD_CAST "image/jpeg",
69 BAD_CAST "jpeg", BAD_CAST "image/jpeg",
70 BAD_CAST "mpeg", BAD_CAST "video/mpeg",
71 BAD_CAST "mpeg1", BAD_CAST "video/mpeg",
72 BAD_CAST "mpeg2", BAD_CAST "video/mpeg",
73 BAD_CAST "mpg", BAD_CAST "video/mpeg",
74 BAD_CAST "mp2", BAD_CAST "audio/mpeg",
75 BAD_CAST "mp3", BAD_CAST "audio/mpeg",
76 BAD_CAST "odp", BAD_CAST "application/vnd.oasis.opendocument.presentation",
77 BAD_CAST "ods", BAD_CAST "application/vnd.oasis.opendocument.spreadsheet",
78 BAD_CAST "odt", BAD_CAST "application/vnd.oasis.opendocument.text",
79 BAD_CAST "pdf", BAD_CAST "application/pdf",
80 BAD_CAST "p7b", BAD_CAST "application/pkcs7-certificates",
81 BAD_CAST "p7c", BAD_CAST "application/pkcs7-mime",
82 BAD_CAST "p7m", BAD_CAST "application/pkcs7-mime",
83 BAD_CAST "p7f", BAD_CAST "application/pkcs7-signature",
84 BAD_CAST "p7s", BAD_CAST "application/pkcs7-signature",
85 BAD_CAST "pk7", BAD_CAST "application/pkcs7-mime",
86 BAD_CAST "png", BAD_CAST "image/png",
87 BAD_CAST "ppt", BAD_CAST "application/vnd.ms-powerpoint",
88 BAD_CAST "pptx", BAD_CAST "application/vnd.openxmlformats-officedocument."
89 "presentationml.presentation",
90 BAD_CAST "rtf", BAD_CAST "application/rtf",
91 BAD_CAST "tif", BAD_CAST "image/tiff",
92 BAD_CAST "tiff", BAD_CAST "image/tiff",
93 BAD_CAST "tsr", BAD_CAST "application/timestamp-reply",
94 BAD_CAST "tst", BAD_CAST "application/timestamp-reply",
95 BAD_CAST "txt", BAD_CAST "text/plain",
96 BAD_CAST "wav", BAD_CAST "audio/wav",
97 BAD_CAST "xls", BAD_CAST "application/vnd.ms-excel",
98 BAD_CAST "xlsx", BAD_CAST "application/vnd.openxmlformats-officedocument."
99 "spreadsheetml.sheet",
100 BAD_CAST "xml", BAD_CAST "application/xml",
101 BAD_CAST "xsd", BAD_CAST "application/xml",
102 BAD_CAST "zfo", BAD_CAST "application/vnd.software602.filler.form-xml-zip"
105 /* Structure type to hold conversion table from status code to isds_error and
106 * long message */
107 struct code_map_isds_error {
108 const xmlChar **codes; /* NULL terminated array of status codes */
109 const char **meanings; /* Mapping to non-localized long messages */
110 const isds_error *errors; /* Mapping to isds_error code */
113 /* Deallocate structure isds_pki_credentials and NULL it.
114 * Pass-phrase is discarded.
115 * @pki credentials to to free */
116 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
117 if(!pki || !*pki) return;
119 free((*pki)->engine);
120 free((*pki)->certificate);
121 free((*pki)->key);
123 if ((*pki)->passphrase) {
124 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
125 free((*pki)->passphrase);
128 zfree((*pki));
132 /* Free isds_list with all member data.
133 * @list list to free, on return will be NULL */
134 void isds_list_free(struct isds_list **list) {
135 struct isds_list *item, *next_item;
137 if (!list || !*list) return;
139 for(item = *list; item; item = next_item) {
140 if (item->destructor) (item->destructor)(&(item->data));
141 next_item = item->next;
142 free(item);
145 *list = NULL;
149 /* Deallocate structure isds_hash and NULL it.
150 * @hash hash to to free */
151 void isds_hash_free(struct isds_hash **hash) {
152 if(!hash || !*hash) return;
153 free((*hash)->value);
154 zfree((*hash));
158 /* Deallocate structure isds_PersonName recursively and NULL it */
159 void isds_PersonName_free(struct isds_PersonName **person_name) {
160 if (!person_name || !*person_name) return;
162 free((*person_name)->pnFirstName);
163 free((*person_name)->pnMiddleName);
164 free((*person_name)->pnLastName);
165 free((*person_name)->pnLastNameAtBirth);
167 free(*person_name);
168 *person_name = NULL;
172 /* Deallocate structure isds_BirthInfo recursively and NULL it */
173 void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
174 if (!birth_info || !*birth_info) return;
176 free((*birth_info)->biDate);
177 free((*birth_info)->biCity);
178 free((*birth_info)->biCounty);
179 free((*birth_info)->biState);
181 free(*birth_info);
182 *birth_info = NULL;
186 /* Deallocate structure isds_Address recursively and NULL it */
187 void isds_Address_free(struct isds_Address **address) {
188 if (!address || !*address) return;
190 free((*address)->adCity);
191 free((*address)->adStreet);
192 free((*address)->adNumberInStreet);
193 free((*address)->adNumberInMunicipality);
194 free((*address)->adZipCode);
195 free((*address)->adState);
197 free(*address);
198 *address = NULL;
202 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
203 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
204 if (!db_owner_info || !*db_owner_info) return;
206 free((*db_owner_info)->dbID);
207 free((*db_owner_info)->dbType);
208 free((*db_owner_info)->ic);
209 isds_PersonName_free(&((*db_owner_info)->personName));
210 free((*db_owner_info)->firmName);
211 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
212 isds_Address_free(&((*db_owner_info)->address));
213 free((*db_owner_info)->nationality);
214 free((*db_owner_info)->email);
215 free((*db_owner_info)->telNumber);
216 free((*db_owner_info)->identifier);
217 free((*db_owner_info)->registryCode);
218 free((*db_owner_info)->dbState);
219 free((*db_owner_info)->dbEffectiveOVM);
220 free((*db_owner_info)->dbOpenAddressing);
222 free(*db_owner_info);
223 *db_owner_info = NULL;
226 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
227 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
228 if (!db_user_info || !*db_user_info) return;
230 free((*db_user_info)->userID);
231 free((*db_user_info)->userType);
232 free((*db_user_info)->userPrivils);
233 isds_PersonName_free(&((*db_user_info)->personName));
234 isds_Address_free(&((*db_user_info)->address));
235 free((*db_user_info)->biDate);
236 free((*db_user_info)->ic);
237 free((*db_user_info)->firmName);
238 free((*db_user_info)->caStreet);
239 free((*db_user_info)->caCity);
240 free((*db_user_info)->caZipCode);
241 free((*db_user_info)->caState);
243 zfree(*db_user_info);
247 /* Deallocate struct isds_event recursively and NULL it */
248 void isds_event_free(struct isds_event **event) {
249 if (!event || !*event) return;
251 free((*event)->time);
252 free((*event)->type);
253 free((*event)->description);
254 zfree(*event);
258 /* Deallocate struct isds_envelope recursively and NULL it */
259 void isds_envelope_free(struct isds_envelope **envelope) {
260 if (!envelope || !*envelope) return;
262 free((*envelope)->dmID);
263 free((*envelope)->dbIDSender);
264 free((*envelope)->dmSender);
265 free((*envelope)->dmSenderAddress);
266 free((*envelope)->dmSenderType);
267 free((*envelope)->dmRecipient);
268 free((*envelope)->dmRecipientAddress);
269 free((*envelope)->dmAmbiguousRecipient);
270 free((*envelope)->dmType);
272 free((*envelope)->dmOrdinal);
273 free((*envelope)->dmMessageStatus);
274 free((*envelope)->dmDeliveryTime);
275 free((*envelope)->dmAcceptanceTime);
276 isds_hash_free(&(*envelope)->hash);
277 free((*envelope)->timestamp);
278 isds_list_free(&(*envelope)->events);
280 free((*envelope)->dmSenderOrgUnit);
281 free((*envelope)->dmSenderOrgUnitNum);
282 free((*envelope)->dbIDRecipient);
283 free((*envelope)->dmRecipientOrgUnit);
284 free((*envelope)->dmRecipientOrgUnitNum);
285 free((*envelope)->dmToHands);
286 free((*envelope)->dmAnnotation);
287 free((*envelope)->dmRecipientRefNumber);
288 free((*envelope)->dmSenderRefNumber);
289 free((*envelope)->dmRecipientIdent);
290 free((*envelope)->dmSenderIdent);
292 free((*envelope)->dmLegalTitleLaw);
293 free((*envelope)->dmLegalTitleYear);
294 free((*envelope)->dmLegalTitleSect);
295 free((*envelope)->dmLegalTitlePar);
296 free((*envelope)->dmLegalTitlePoint);
298 free((*envelope)->dmPersonalDelivery);
299 free((*envelope)->dmAllowSubstDelivery);
301 free((*envelope)->dmOVM);
302 free((*envelope)->dmPublishOwnID);
304 free(*envelope);
305 *envelope = NULL;
309 /* Deallocate struct isds_message recursively and NULL it */
310 void isds_message_free(struct isds_message **message) {
311 if (!message || !*message) return;
313 free((*message)->raw);
314 isds_envelope_free(&((*message)->envelope));
315 isds_list_free(&((*message)->documents));
316 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
318 free(*message);
319 *message = NULL;
323 /* Deallocate struct isds_document recursively and NULL it */
324 void isds_document_free(struct isds_document **document) {
325 if (!document || !*document) return;
327 if (!(*document)->is_xml) {
328 free((*document)->data);
330 free((*document)->dmMimeType);
331 free((*document)->dmFileGuid);
332 free((*document)->dmUpFileGuid);
333 free((*document)->dmFileDescr);
334 free((*document)->dmFormat);
336 free(*document);
337 *document = NULL;
341 /* Deallocate struct isds_message_copy recursively and NULL it */
342 void isds_message_copy_free(struct isds_message_copy **copy) {
343 if (!copy || !*copy) return;
345 free((*copy)->dbIDRecipient);
346 free((*copy)->dmRecipientOrgUnit);
347 free((*copy)->dmRecipientOrgUnitNum);
348 free((*copy)->dmToHands);
350 free((*copy)->dmStatus);
351 free((*copy)->dmID);
353 zfree(*copy);
357 /* Deallocate struct isds_message_status_change recursively and NULL it */
358 void isds_message_status_change_free(
359 struct isds_message_status_change **message_status_change) {
360 if (!message_status_change || !*message_status_change) return;
362 free((*message_status_change)->dmID);
363 free((*message_status_change)->time);
364 free((*message_status_change)->dmMessageStatus);
366 zfree(*message_status_change);
370 /* Deallocate struct isds_approval recursively and NULL it */
371 void isds_approval_free(struct isds_approval **approval) {
372 if (!approval || !*approval) return;
374 free((*approval)->refference);
376 zfree(*approval);
380 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
381 * The email string is deallocated too. */
382 void isds_credentials_delivery_free(
383 struct isds_credentials_delivery **credentials_delivery) {
384 if (!credentials_delivery || !*credentials_delivery) return;
386 free((*credentials_delivery)->email);
387 free((*credentials_delivery)->token);
388 free((*credentials_delivery)->new_user_name);
390 zfree(*credentials_delivery);
394 /* Deallocate struct isds_commercial_permission recursively and NULL it */
395 void isds_commercial_permission_free(
396 struct isds_commercial_permission **permission) {
397 if (NULL == permission || NULL == *permission) return;
399 free((*permission)->recipient);
400 free((*permission)->payer);
401 free((*permission)->expiration);
402 free((*permission)->count);
403 free((*permission)->reply_identifier);
405 zfree(*permission);
409 /* Deallocate struct isds_credit_event recursively and NULL it */
410 void isds_credit_event_free(struct isds_credit_event **event) {
411 if (NULL == event || NULL == *event) return;
413 free((*event)->time);
414 switch ((*event)->type) {
415 case ISDS_CREDIT_CHARGED:
416 free((*event)->details.charged.transaction);
417 break;
418 case ISDS_CREDIT_DISCHARGED:
419 free((*event)->details.discharged.transaction);
420 break;
421 case ISDS_CREDIT_MESSAGE_SENT:
422 free((*event)->details.message_sent.recipient);
423 free((*event)->details.message_sent.message_id);
424 break;
425 case ISDS_CREDIT_STORAGE_SET:
426 free((*event)->details.storage_set.new_valid_from);
427 free((*event)->details.storage_set.new_valid_to);
428 free((*event)->details.storage_set.old_capacity);
429 free((*event)->details.storage_set.old_valid_from);
430 free((*event)->details.storage_set.old_valid_to);
431 free((*event)->details.storage_set.initiator);
432 break;
433 case ISDS_CREDIT_EXPIRED:
434 break;
437 zfree(*event);
441 /* Deallocate struct isds_fulltext_result recursively and NULL it */
442 void isds_fulltext_result_free(
443 struct isds_fulltext_result **result) {
444 if (NULL == result || NULL == *result) return;
446 free((*result)->dbID);
447 free((*result)->name);
448 isds_list_free(&((*result)->name_match_start));
449 isds_list_free(&((*result)->name_match_end));
450 free((*result)->address);
451 isds_list_free(&((*result)->address_match_start));
452 isds_list_free(&((*result)->address_match_end));
453 free((*result)->ic);
454 free((*result)->biDate);
456 zfree(*result);
460 /* *DUP_OR_ERROR macros needs error label */
461 #define STRDUP_OR_ERROR(new, template) { \
462 if (!template) { \
463 (new) = NULL; \
464 } else { \
465 (new) = strdup(template); \
466 if (!new) goto error; \
470 #define FLATDUP_OR_ERROR(new, template) { \
471 if (!template) { \
472 (new) = NULL; \
473 } else { \
474 (new) = malloc(sizeof(*(new))); \
475 if (!new) goto error; \
476 memcpy((new), (template), sizeof(*(template))); \
480 /* Copy structure isds_pki_credentials recursively. */
481 struct isds_pki_credentials *isds_pki_credentials_duplicate(
482 const struct isds_pki_credentials *template) {
483 struct isds_pki_credentials *new = NULL;
485 if(!template) return NULL;
487 new = calloc(1, sizeof(*new));
488 if (!new) return NULL;
490 STRDUP_OR_ERROR(new->engine, template->engine);
491 new->certificate_format = template->certificate_format;
492 STRDUP_OR_ERROR(new->certificate, template->certificate);
493 new->key_format = template->key_format;
494 STRDUP_OR_ERROR(new->key, template->key);
495 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
497 return new;
499 error:
500 isds_pki_credentials_free(&new);
501 return NULL;
505 /* Copy structure isds_PersonName recursively */
506 struct isds_PersonName *isds_PersonName_duplicate(
507 const struct isds_PersonName *src) {
508 struct isds_PersonName *new = NULL;
510 if (!src) return NULL;
512 new = calloc(1, sizeof(*new));
513 if (!new) return NULL;
515 STRDUP_OR_ERROR(new->pnFirstName, src->pnFirstName);
516 STRDUP_OR_ERROR(new->pnMiddleName, src->pnMiddleName);
517 STRDUP_OR_ERROR(new->pnLastName, src->pnLastName);
518 STRDUP_OR_ERROR(new->pnLastNameAtBirth, src->pnLastNameAtBirth);
520 return new;
522 error:
523 isds_PersonName_free(&new);
524 return NULL;
528 /* Copy structure isds_BirthInfo recursively */
529 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
530 const struct isds_BirthInfo *template) {
531 struct isds_BirthInfo *new = NULL;
533 if (!template) return NULL;
535 new = calloc(1, sizeof(*new));
536 if (!new) return NULL;
538 FLATDUP_OR_ERROR(new->biDate, template->biDate);
539 STRDUP_OR_ERROR(new->biCity, template->biCity);
540 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
541 STRDUP_OR_ERROR(new->biState, template->biState);
543 return new;
545 error:
546 isds_BirthInfo_free(&new);
547 return NULL;
551 /* Copy structure isds_Address recursively */
552 struct isds_Address *isds_Address_duplicate(
553 const struct isds_Address *src) {
554 struct isds_Address *new = NULL;
556 if (!src) return NULL;
558 new = calloc(1, sizeof(*new));
559 if (!new) return NULL;
561 STRDUP_OR_ERROR(new->adCity, src->adCity);
562 STRDUP_OR_ERROR(new->adStreet, src->adStreet);
563 STRDUP_OR_ERROR(new->adNumberInStreet, src->adNumberInStreet);
564 STRDUP_OR_ERROR(new->adNumberInMunicipality,
565 src->adNumberInMunicipality);
566 STRDUP_OR_ERROR(new->adZipCode, src->adZipCode);
567 STRDUP_OR_ERROR(new->adState, src->adState);
569 return new;
571 error:
572 isds_Address_free(&new);
573 return NULL;
577 /* Copy structure isds_DbOwnerInfo recursively */
578 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
579 const struct isds_DbOwnerInfo *src) {
580 struct isds_DbOwnerInfo *new = NULL;
581 if (!src) return NULL;
583 new = calloc(1, sizeof(*new));
584 if (!new) return NULL;
586 STRDUP_OR_ERROR(new->dbID, src->dbID);
587 FLATDUP_OR_ERROR(new->dbType, src->dbType);
588 STRDUP_OR_ERROR(new->ic, src->ic);
590 if (src->personName) {
591 if (!(new->personName =
592 isds_PersonName_duplicate(src->personName)))
593 goto error;
596 STRDUP_OR_ERROR(new->firmName, src->firmName);
598 if (src->birthInfo) {
599 if (!(new->birthInfo =
600 isds_BirthInfo_duplicate(src->birthInfo)))
601 goto error;
604 if (src->address) {
605 if (!(new->address = isds_Address_duplicate(src->address)))
606 goto error;
609 STRDUP_OR_ERROR(new->nationality, src->nationality);
610 STRDUP_OR_ERROR(new->email, src->email);
611 STRDUP_OR_ERROR(new->telNumber, src->telNumber);
612 STRDUP_OR_ERROR(new->identifier, src->identifier);
613 STRDUP_OR_ERROR(new->registryCode, src->registryCode);
614 FLATDUP_OR_ERROR(new->dbState, src->dbState);
615 FLATDUP_OR_ERROR(new->dbEffectiveOVM, src->dbEffectiveOVM);
616 FLATDUP_OR_ERROR(new->dbOpenAddressing, src->dbOpenAddressing);
618 return new;
620 error:
621 isds_DbOwnerInfo_free(&new);
622 return NULL;
626 /* Copy structure isds_DbUserInfo recursively */
627 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
628 const struct isds_DbUserInfo *src) {
629 struct isds_DbUserInfo *new = NULL;
630 if (!src) return NULL;
632 new = calloc(1, sizeof(*new));
633 if (!new) return NULL;
635 STRDUP_OR_ERROR(new->userID, src->userID);
636 FLATDUP_OR_ERROR(new->userType, src->userType);
637 FLATDUP_OR_ERROR(new->userPrivils, src->userPrivils);
639 if (src->personName) {
640 if (!(new->personName =
641 isds_PersonName_duplicate(src->personName)))
642 goto error;
645 if (src->address) {
646 if (!(new->address = isds_Address_duplicate(src->address)))
647 goto error;
650 FLATDUP_OR_ERROR(new->biDate, src->biDate);
651 STRDUP_OR_ERROR(new->ic, src->ic);
652 STRDUP_OR_ERROR(new->firmName, src->firmName);
653 STRDUP_OR_ERROR(new->caStreet, src->caStreet);
654 STRDUP_OR_ERROR(new->caCity, src->caCity);
655 STRDUP_OR_ERROR(new->caZipCode, src->caZipCode);
656 STRDUP_OR_ERROR(new->caState, src->caState);
658 return new;
660 error:
661 isds_DbUserInfo_free(&new);
662 return NULL;
665 #undef FLATDUP_OR_ERROR
666 #undef STRDUP_OR_ERROR
669 /* Logs libxml2 errors. Should be registered to libxml2 library.
670 * @ctx is unused currently
671 * @msg is printf-like formated message from libxml2 (UTF-8?)
672 * @... are variadic arguments for @msg */
673 static void log_xml(void *ctx, const char *msg, ...) {
674 va_list ap;
675 char *text = NULL;
677 /* Silent warning for unused function argument.
678 * The prototype is an API of libxml2's xmlSetGenericErrorFunc(). */
679 (void)ctx;
681 if (!msg) return;
683 va_start(ap, msg);
684 isds_vasprintf(&text, msg, ap);
685 va_end(ap);
687 if (text)
688 isds_log(ILF_XML, ILL_ERR, "%s", text);
689 free(text);
693 /* Initialize ISDS library.
694 * Global function, must be called before other functions.
695 * If it fails you can not use ISDS library and must call isds_cleanup() to
696 * free partially initialized global variables. */
697 isds_error isds_init(void) {
698 /* NULL global variables */
699 log_facilities = ILF_ALL;
700 log_level = ILL_WARNING;
701 log_callback = NULL;
702 log_callback_data = NULL;
704 #if ENABLE_NLS
705 /* Initialize gettext */
706 bindtextdomain(PACKAGE, LOCALEDIR);
707 #endif
709 #if HAVE_LIBCURL
710 /* Initialize CURL */
711 if (curl_global_init(CURL_GLOBAL_ALL)) {
712 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
713 return IE_ERROR;
715 #endif /* HAVE_LIBCURL */
717 /* Initialise cryptographic back-ends. */
718 if (IE_SUCCESS != _isds_init_crypto()) {
719 isds_log(ILF_ISDS, ILL_CRIT,
720 _("Initialization of cryptographic back-end failed\n"));
721 return IE_ERROR;
724 /* This can _exit() current program. Find not so assertive check. */
725 LIBXML_TEST_VERSION;
726 xmlSetGenericErrorFunc(NULL, log_xml);
728 /* Check expat */
729 if (_isds_init_expat(&version_expat)) {
730 isds_log(ILF_ISDS, ILL_CRIT,
731 _("expat library initialization failed\n"));
732 return IE_ERROR;
735 /* Allocate global variables */
738 return IE_SUCCESS;
742 /* Deinitialize ISDS library.
743 * Global function, must be called as last library function. */
744 isds_error isds_cleanup(void) {
745 /* XML */
746 xmlCleanupParser();
748 #if HAVE_LIBCURL
749 /* Curl */
750 curl_global_cleanup();
751 #endif
753 return IE_SUCCESS;
757 /* Return version string of this library. Version of dependencies can be
758 * embedded. Do no try to parse it. You must free it. */
759 char *isds_version(void) {
760 char *buffer = NULL;
762 isds_asprintf(&buffer,
763 #if HAVE_LIBCURL
764 # ifndef USE_OPENSSL_BACKEND
765 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
766 # else
767 _("%s (%s, %s, %s, libxml2 %s)"),
768 # endif
769 #else
770 # ifndef USE_OPENSSL_BACKEND
771 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
772 # else
773 _("%s (%s, %s, libxml2 %s)"),
774 # endif
775 #endif
776 PACKAGE_VERSION,
777 #if HAVE_LIBCURL
778 curl_version(),
779 #endif
780 #ifndef USE_OPENSSL_BACKEND
781 version_gpgme, version_gcrypt,
782 #else
783 version_openssl,
784 #endif
785 version_expat, xmlParserVersion);
786 return buffer;
790 /* Return text description of ISDS error */
791 const char *isds_strerror(const isds_error error) {
792 switch (error) {
793 case IE_SUCCESS:
794 return(_("Success")); break;
795 case IE_ERROR:
796 return(_("Unspecified error")); break;
797 case IE_NOTSUP:
798 return(_("Not supported")); break;
799 case IE_INVAL:
800 return(_("Invalid value")); break;
801 case IE_INVALID_CONTEXT:
802 return(_("Invalid context")); break;
803 case IE_NOT_LOGGED_IN:
804 return(_("Not logged in")); break;
805 case IE_CONNECTION_CLOSED:
806 return(_("Connection closed")); break;
807 case IE_TIMED_OUT:
808 return(_("Timed out")); break;
809 case IE_NOEXIST:
810 return(_("Not exist")); break;
811 case IE_NOMEM:
812 return(_("Out of memory")); break;
813 case IE_NETWORK:
814 return(_("Network problem")); break;
815 case IE_HTTP:
816 return(_("HTTP problem")); break;
817 case IE_SOAP:
818 return(_("SOAP problem")); break;
819 case IE_XML:
820 return(_("XML problem")); break;
821 case IE_ISDS:
822 return(_("ISDS server problem")); break;
823 case IE_ENUM:
824 return(_("Invalid enum value")); break;
825 case IE_DATE:
826 return(_("Invalid date value")); break;
827 case IE_2BIG:
828 return(_("Too big")); break;
829 case IE_2SMALL:
830 return(_("Too small")); break;
831 case IE_NOTUNIQ:
832 return(_("Value not unique")); break;
833 case IE_NOTEQUAL:
834 return(_("Values not equal")); break;
835 case IE_PARTIAL_SUCCESS:
836 return(_("Some suboperations failed")); break;
837 case IE_ABORTED:
838 return(_("Operation aborted")); break;
839 case IE_SECURITY:
840 return(_("Security problem")); break;
841 default:
842 return(_("Unknown error"));
847 /* Create ISDS context.
848 * Each context can be used for different sessions to (possibly) different
849 * ISDS server with different credentials. */
850 struct isds_ctx *isds_ctx_create(void) {
851 struct isds_ctx *context;
852 context = malloc(sizeof(*context));
853 if (context) memset(context, 0, sizeof(*context));
854 return context;
857 #if HAVE_LIBCURL
858 /* Close possibly opened connection to Czech POINT document deposit without
859 * resetting long_message buffer.
860 * XXX: Do not use czp_close_connection() if you do not want to destroy log
861 * message.
862 * @context is Czech POINT session context. */
863 static isds_error czp_do_close_connection(struct isds_ctx *context) {
864 if (!context) return IE_INVALID_CONTEXT;
865 _isds_close_connection(context);
866 return IE_SUCCESS;
870 /* Discard credentials.
871 * @context is ISDS context
872 * @discard_saved_username is true for removing saved username, false for
873 * keeping it.
874 * Only that. It does not cause log out, connection close or similar. */
875 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context,
876 _Bool discard_saved_username) {
877 if(!context) return IE_INVALID_CONTEXT;
879 if (context->username) {
880 memset(context->username, 0, strlen(context->username));
881 zfree(context->username);
883 if (context->password) {
884 memset(context->password, 0, strlen(context->password));
885 zfree(context->password);
887 isds_pki_credentials_free(&context->pki_credentials);
888 if (discard_saved_username && context->saved_username) {
889 memset(context->saved_username, 0, strlen(context->saved_username));
890 zfree(context->saved_username);
893 return IE_SUCCESS;
895 #endif /* HAVE_LIBCURL */
898 /* Destroy ISDS context and free memory.
899 * @context will be NULLed on success. */
900 isds_error isds_ctx_free(struct isds_ctx **context) {
901 if (!context || !*context) {
902 return IE_INVALID_CONTEXT;
905 #if HAVE_LIBCURL
906 /* Discard credentials and close connection */
907 switch ((*context)->type) {
908 case CTX_TYPE_NONE: break;
909 case CTX_TYPE_ISDS: isds_logout(*context); break;
910 case CTX_TYPE_CZP:
911 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
912 czp_do_close_connection(*context); break;
915 /* For sure */
916 _isds_discard_credentials(*context, 1);
918 /* Free other structures */
919 free((*context)->url);
920 free((*context)->tls_verify_server);
921 free((*context)->tls_ca_file);
922 free((*context)->tls_ca_dir);
923 free((*context)->tls_crl_file);
924 #endif /* HAVE_LIBCURL */
925 free((*context)->long_message);
927 free(*context);
928 *context = NULL;
929 return IE_SUCCESS;
933 /* Return long message text produced by library function, e.g. detailed error
934 * message. Returned pointer is only valid until new library function is
935 * called for the same context. Could be NULL, especially if NULL context is
936 * supplied. Return string is locale encoded. */
937 char *isds_long_message(const struct isds_ctx *context) {
938 if (!context) return NULL;
939 return context->long_message;
943 /* Stores message into context' long_message buffer.
944 * Application can pick the message up using isds_long_message().
945 * NULL @message truncates the buffer but does not deallocate it.
946 * @message is coded in locale encoding */
947 _hidden isds_error isds_log_message(struct isds_ctx *context,
948 const char *message) {
949 char *buffer;
950 size_t length;
952 if (!context) return IE_INVALID_CONTEXT;
954 /* FIXME: Check for integer overflow */
955 length = 1 + ((message) ? strlen(message) : 0);
956 buffer = realloc(context->long_message, length);
957 if (!buffer) return IE_NOMEM;
959 if (message)
960 strcpy(buffer, message);
961 else
962 *buffer = '\0';
964 context->long_message = buffer;
965 return IE_SUCCESS;
969 /* Appends message into context' long_message buffer.
970 * Application can pick the message up using isds_long_message().
971 * NULL message has void effect. */
972 _hidden isds_error isds_append_message(struct isds_ctx *context,
973 const char *message) {
974 char *buffer;
975 size_t old_length, length;
977 if (!context) return IE_INVALID_CONTEXT;
978 if (!message) return IE_SUCCESS;
979 if (!context->long_message)
980 return isds_log_message(context, message);
982 old_length = strlen(context->long_message);
983 /* FIXME: Check for integer overflow */
984 length = 1 + old_length + strlen(message);
985 buffer = realloc(context->long_message, length);
986 if (!buffer) return IE_NOMEM;
988 strcpy(buffer + old_length, message);
990 context->long_message = buffer;
991 return IE_SUCCESS;
995 /* Stores formatted message into context' long_message buffer.
996 * Application can pick the message up using isds_long_message(). */
997 _hidden isds_error isds_printf_message(struct isds_ctx *context,
998 const char *format, ...) {
999 va_list ap;
1000 int length;
1002 if (!context) return IE_INVALID_CONTEXT;
1003 va_start(ap, format);
1004 length = isds_vasprintf(&(context->long_message), format, ap);
1005 va_end(ap);
1007 return (length < 0) ? IE_ERROR: IE_SUCCESS;
1011 /* Set logging up.
1012 * @facilities is bit mask of isds_log_facility values,
1013 * @level is verbosity level. */
1014 void isds_set_logging(const unsigned int facilities,
1015 const isds_log_level level) {
1016 log_facilities = facilities;
1017 log_level = level;
1021 /* Register callback function libisds calls when new global log message is
1022 * produced by library. Library logs to stderr by default.
1023 * @callback is function provided by application libisds will call. See type
1024 * definition for @callback argument explanation. Pass NULL to revert logging to
1025 * default behaviour.
1026 * @data is application specific data @callback gets as last argument */
1027 void isds_set_log_callback(isds_log_callback callback, void *data) {
1028 log_callback = callback;
1029 log_callback_data = data;
1033 /* Log @message in class @facility with log @level into global log. @message
1034 * is printf(3) formatting string, variadic arguments may be necessary.
1035 * For debugging purposes. */
1036 _hidden isds_error isds_log(const isds_log_facility facility,
1037 const isds_log_level level, const char *message, ...) {
1038 va_list ap;
1039 char *buffer = NULL;
1040 int length;
1042 if (level > log_level) return IE_SUCCESS;
1043 if (!(log_facilities & facility)) return IE_SUCCESS;
1044 if (!message) return IE_INVAL;
1046 if (log_callback) {
1047 /* Pass message to application supplied callback function */
1048 va_start(ap, message);
1049 length = isds_vasprintf(&buffer, message, ap);
1050 va_end(ap);
1052 if (length == -1) {
1053 return IE_ERROR;
1055 if (length > 0) {
1056 log_callback(facility, level, buffer, length, log_callback_data);
1058 free(buffer);
1059 } else {
1060 /* Default: Log it to stderr */
1061 va_start(ap, message);
1062 vfprintf(stderr, message, ap);
1063 va_end(ap);
1064 /* Line buffered printf is default.
1065 * fflush(stderr);*/
1068 return IE_SUCCESS;
1072 /* Set timeout in milliseconds for each network job like connecting to server
1073 * or sending message. Use 0 to disable timeout limits. */
1074 isds_error isds_set_timeout(struct isds_ctx *context,
1075 const unsigned int timeout) {
1076 if (!context) return IE_INVALID_CONTEXT;
1077 zfree(context->long_message);
1079 #if HAVE_LIBCURL
1080 context->timeout = timeout;
1082 if (context->curl) {
1083 CURLcode curl_err;
1085 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
1086 if (!curl_err)
1087 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1088 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
1089 context->timeout);
1090 #else
1091 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
1092 context->timeout / 1000);
1093 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1094 if (curl_err) return IE_ERROR;
1097 return IE_SUCCESS;
1098 #else /* not HAVE_LIBCURL */
1099 return IE_NOTSUP;
1100 #endif
1104 /* Register callback function libisds calls periodically during HTTP data
1105 * transfer.
1106 * @context is session context
1107 * @callback is function provided by application libisds will call. See type
1108 * definition for @callback argument explanation.
1109 * @data is application specific data @callback gets as last argument */
1110 isds_error isds_set_progress_callback(struct isds_ctx *context,
1111 isds_progress_callback callback, void *data) {
1112 if (!context) return IE_INVALID_CONTEXT;
1113 zfree(context->long_message);
1115 #if HAVE_LIBCURL
1116 context->progress_callback = callback;
1117 context->progress_callback_data = data;
1119 return IE_SUCCESS;
1120 #else /* not HAVE_LIBCURL */
1121 return IE_NOTSUP;
1122 #endif
1126 /* Change context settings.
1127 * @context is context which setting will be applied to
1128 * @option is name of option. It determines the type of last argument. See
1129 * isds_option definition for more info.
1130 * @... is value of new setting. Type is determined by @option
1131 * */
1132 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1133 ...) {
1134 isds_error err = IE_SUCCESS;
1135 va_list ap;
1136 #if HAVE_LIBCURL
1137 char *pointer, *string;
1138 #endif
1140 if (!context) return IE_INVALID_CONTEXT;
1141 zfree(context->long_message);
1143 va_start(ap, option);
1145 #define REPLACE_VA_BOOLEAN(destination) { \
1146 if (!(destination)) { \
1147 (destination) = malloc(sizeof(*(destination))); \
1148 if (!(destination)) { \
1149 err = IE_NOMEM; goto leave; \
1152 *(destination) = (_Bool) !!va_arg(ap, int); \
1155 #define REPLACE_VA_STRING(destination) { \
1156 string = va_arg(ap, char *); \
1157 if (string) { \
1158 pointer = realloc((destination), 1 + strlen(string)); \
1159 if (!pointer) { err = IE_NOMEM; goto leave; } \
1160 strcpy(pointer, string); \
1161 (destination) = pointer; \
1162 } else { \
1163 free(destination); \
1164 (destination) = NULL; \
1168 switch (option) {
1169 case IOPT_TLS_VERIFY_SERVER:
1170 #if HAVE_LIBCURL
1171 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1172 #else
1173 err = IE_NOTSUP; goto leave;
1174 #endif
1175 break;
1176 case IOPT_TLS_CA_FILE:
1177 #if HAVE_LIBCURL
1178 REPLACE_VA_STRING(context->tls_ca_file);
1179 #else
1180 err = IE_NOTSUP; goto leave;
1181 #endif
1182 break;
1183 case IOPT_TLS_CA_DIRECTORY:
1184 #if HAVE_LIBCURL
1185 REPLACE_VA_STRING(context->tls_ca_dir);
1186 #else
1187 err = IE_NOTSUP; goto leave;
1188 #endif
1189 break;
1190 case IOPT_TLS_CRL_FILE:
1191 #if HAVE_LIBCURL
1192 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1193 REPLACE_VA_STRING(context->tls_crl_file);
1194 #else
1195 isds_log_message(context,
1196 _("Curl library does not support CRL definition"));
1197 err = IE_NOTSUP;
1198 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1199 #else
1200 err = IE_NOTSUP; goto leave;
1201 #endif /* not HAVE_LIBCURL */
1202 break;
1203 case IOPT_NORMALIZE_MIME_TYPE:
1204 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1205 break;
1207 default:
1208 err = IE_ENUM; goto leave;
1211 #undef REPLACE_VA_STRING
1212 #undef REPLACE_VA_BOOLEAN
1214 leave:
1215 va_end(ap);
1216 return err;
1220 #if HAVE_LIBCURL
1221 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1222 * Destination for NULL argument will not be touched.
1223 * Destination pointers must be freed before calling this function.
1224 * If @username is @context->saved_username, the saved_username will not be
1225 * replaced. The saved_username is clobbered only if context has set otp
1226 * member.
1227 * Return IE_SUCCESS on success. */
1228 static isds_error _isds_store_credentials(struct isds_ctx *context,
1229 const char *username, const char *password,
1230 const struct isds_pki_credentials *pki_credentials) {
1231 if (NULL == context) return IE_INVALID_CONTEXT;
1233 /* FIXME: mlock password
1234 * (I have a library) */
1236 if (username) {
1237 context->username = strdup(username);
1238 if (context->otp && context->saved_username != username)
1239 context->saved_username = strdup(username);
1241 if (password) {
1242 if (NULL == context->otp_credentials)
1243 context->password = strdup(password);
1244 else
1245 context->password = _isds_astrcat(password,
1246 context->otp_credentials->otp_code);
1248 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1250 if ((NULL != username && NULL == context->username) ||
1251 (NULL != password && NULL == context->password) ||
1252 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1253 (context->otp && NULL != context->username &&
1254 NULL == context->saved_username)) {
1255 return IE_NOMEM;
1258 return IE_SUCCESS;
1260 #endif
1263 /* Connect and log into ISDS server.
1264 * All required arguments will be copied, you do not have to keep them after
1265 * that.
1266 * ISDS supports six different authentication methods. Exact method is
1267 * selected on @username, @password, @pki_credentials, and @otp arguments:
1268 * - If @pki_credentials == NULL, @username and @password must be supplied
1269 * and then
1270 * - If @otp == NULL, simple authentication by username and password will
1271 * be proceeded.
1272 * - If @otp != NULL, authentication by username and password and OTP
1273 * will be used.
1274 * - If @pki_credentials != NULL, then
1275 * - If @username == NULL, only certificate will be used
1276 * - If @username != NULL, then
1277 * - If @password == NULL, then certificate will be used and
1278 * @username shifts meaning to box ID. This is used for hosted
1279 * services.
1280 * - Otherwise all three arguments will be used.
1281 * Please note, that different cases require different certificate type
1282 * (system qualified one or commercial non qualified one). This library
1283 * does not check such political issues. Please see ISDS Specification
1284 * for more details.
1285 * @url is base address of ISDS web service. Pass extern isds_locator
1286 * variable to use production ISDS instance without client certificate
1287 * authentication (or extern isds_cert_locator with client certificate
1288 * authentication or extern isds_otp_locators with OTP authentication).
1289 * Passing NULL has the same effect, autoselection between isds_locator,
1290 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1291 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1292 * isds_otp_testing_locator) variable to select testing instance.
1293 * @username is user name of ISDS user or box ID
1294 * @password is user's secret password
1295 * @pki_credentials defines public key cryptographic material to use in client
1296 * authentication.
1297 * @otp selects one-time password authentication method to use, defines OTP
1298 * code (if known) and returns fine grade resolution of OTP procedure.
1299 * @return:
1300 * IE_SUCCESS if authentication succeeds
1301 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1302 * requested, fine grade reason will be set into @otp->resolution. Error
1303 * message from server can be obtained by isds_long_message() call.
1304 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1305 * server has sent OTP code through side channel. Application is expected to
1306 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1307 * this call to complete second phase of TOTP authentication;
1308 * or other appropriate error. */
1309 isds_error isds_login(struct isds_ctx *context, const char *url,
1310 const char *username, const char *password,
1311 const struct isds_pki_credentials *pki_credentials,
1312 struct isds_otp *otp) {
1313 #if HAVE_LIBCURL
1314 isds_error err = IE_NOT_LOGGED_IN;
1315 isds_error soap_err;
1316 xmlNsPtr isds_ns = NULL;
1317 xmlNodePtr request = NULL;
1318 #endif /* HAVE_LIBCURL */
1320 if (!context) return IE_INVALID_CONTEXT;
1321 zfree(context->long_message);
1323 #if HAVE_LIBCURL
1324 /* Close connection if already logged in */
1325 if (context->curl) {
1326 _isds_close_connection(context);
1329 /* Store configuration */
1330 context->type = CTX_TYPE_ISDS;
1331 zfree(context->url);
1333 /* Mangle base URI according to requested authentication method */
1334 if (NULL == pki_credentials) {
1335 isds_log(ILF_SEC, ILL_INFO,
1336 _("Selected authentication method: no certificate, "
1337 "username and password\n"));
1338 if (!username || !password) {
1339 isds_log_message(context,
1340 _("Both username and password must be supplied"));
1341 return IE_INVAL;
1343 context->otp_credentials = otp;
1344 context->otp = (NULL != context->otp_credentials);
1346 if (!context->otp) {
1347 /* Default locator is official system (without certificate or
1348 * OTP) */
1349 context->url = strdup((NULL != url) ? url : isds_locator);
1350 } else {
1351 const char *authenticator_uri = NULL;
1352 if (!url) url = isds_otp_locator;
1353 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1354 switch (context->otp_credentials->method) {
1355 case OTP_HMAC:
1356 isds_log(ILF_SEC, ILL_INFO,
1357 _("Selected authentication method: "
1358 "HMAC-based one-time password\n"));
1359 authenticator_uri =
1360 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1361 break;
1362 case OTP_TIME:
1363 isds_log(ILF_SEC, ILL_INFO,
1364 _("Selected authentication method: "
1365 "Time-based one-time password\n"));
1366 if (context->otp_credentials->otp_code == NULL) {
1367 isds_log(ILF_SEC, ILL_INFO,
1368 _("OTP code has not been provided by "
1369 "application, requesting server for "
1370 "new one.\n"));
1371 authenticator_uri =
1372 "%1$sas/processLogin?type=totp&sendSms=true&"
1373 "uri=%1$sapps/";
1374 } else {
1375 isds_log(ILF_SEC, ILL_INFO,
1376 _("OTP code has been provided by "
1377 "application, not requesting server "
1378 "for new one.\n"));
1379 authenticator_uri =
1380 "%1$sas/processLogin?type=totp&"
1381 "uri=%1$sapps/";
1383 break;
1384 default:
1385 isds_log_message(context,
1386 _("Unknown one-time password authentication "
1387 "method requested by application"));
1388 return IE_ENUM;
1390 if (-1 == isds_asprintf(&context->url, authenticator_uri, url))
1391 return IE_NOMEM;
1393 } else {
1394 /* Default locator is official system (with client certificate) */
1395 context->otp = 0;
1396 context->otp_credentials = NULL;
1397 if (!url) url = isds_cert_locator;
1399 if (!username) {
1400 isds_log(ILF_SEC, ILL_INFO,
1401 _("Selected authentication method: system certificate, "
1402 "no username and no password\n"));
1403 password = NULL;
1404 context->url = _isds_astrcat(url, "cert/");
1405 } else {
1406 if (!password) {
1407 isds_log(ILF_SEC, ILL_INFO,
1408 _("Selected authentication method: system certificate, "
1409 "box ID and no password\n"));
1410 context->url = _isds_astrcat(url, "hspis/");
1411 } else {
1412 isds_log(ILF_SEC, ILL_INFO,
1413 _("Selected authentication method: commercial "
1414 "certificate, username and password\n"));
1415 context->url = _isds_astrcat(url, "certds/");
1419 if (!(context->url))
1420 return IE_NOMEM;
1422 /* Prepare CURL handle */
1423 context->curl = curl_easy_init();
1424 if (!(context->curl))
1425 return IE_ERROR;
1427 /* Build log-in request */
1428 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1429 if (!request) {
1430 isds_log_message(context, _("Could not build ISDS log-in request"));
1431 return IE_ERROR;
1433 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1434 if(!isds_ns) {
1435 isds_log_message(context, _("Could not create ISDS name space"));
1436 xmlFreeNode(request);
1437 return IE_ERROR;
1439 xmlSetNs(request, isds_ns);
1441 /* Store credentials */
1442 _isds_discard_credentials(context, 1);
1443 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1444 _isds_discard_credentials(context, 1);
1445 xmlFreeNode(request);
1446 return IE_NOMEM;
1449 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1450 username, url);
1452 /* XXX: ISDS documentation does not specify response body for
1453 * DummyOperation request. However real server sends back
1454 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1455 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1456 * SOAP body content, e.g. the dmStatus element. */
1458 /* Send log-in request */
1459 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1461 if (context->otp) {
1462 /* Revert context URL from OTP authentication service URL to OTP web
1463 * service base URL for subsequent calls. Potenial isds_login() retry
1464 * will re-set context URL again. */
1465 zfree(context->url);
1466 context->url = _isds_astrcat(url, "apps/");
1467 if (context->url == NULL) {
1468 soap_err = IE_NOMEM;
1470 /* Detach pointer to OTP credentials from context */
1471 context->otp_credentials = NULL;
1474 /* Remove credentials */
1475 _isds_discard_credentials(context, 0);
1477 /* Destroy log-in request */
1478 xmlFreeNode(request);
1480 if (soap_err) {
1481 _isds_close_connection(context);
1482 return soap_err;
1485 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1486 * authentication succeeded if soap_err == IE_SUCCESS */
1487 err = IE_SUCCESS;
1489 if (!err)
1490 isds_log(ILF_ISDS, ILL_DEBUG,
1491 _("User %s has been logged into server %s successfully\n"),
1492 username, url);
1493 return err;
1494 #else /* not HAVE_LIBCURL */
1495 return IE_NOTSUP;
1496 #endif
1500 /* Log out from ISDS server discards credentials and connection configuration. */
1501 isds_error isds_logout(struct isds_ctx *context) {
1502 if (!context) return IE_INVALID_CONTEXT;
1503 zfree(context->long_message);
1505 #if HAVE_LIBCURL
1506 if (context->curl) {
1507 if (context->otp) {
1508 isds_error err = _isds_invalidate_otp_cookie(context);
1509 if (err) return err;
1512 /* Close connection */
1513 _isds_close_connection(context);
1515 /* Discard credentials for sure. They should not survive isds_login(),
1516 * even successful .*/
1517 _isds_discard_credentials(context, 1);
1519 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1520 } else {
1521 _isds_discard_credentials(context, 1);
1523 zfree(context->url);
1524 return IE_SUCCESS;
1525 #else /* not HAVE_LIBCURL */
1526 return IE_NOTSUP;
1527 #endif
1531 /* Verify connection to ISDS is alive and server is responding.
1532 * Send dummy request to ISDS and expect dummy response. */
1533 isds_error isds_ping(struct isds_ctx *context) {
1534 #if HAVE_LIBCURL
1535 isds_error soap_err;
1536 xmlNsPtr isds_ns = NULL;
1537 xmlNodePtr request = NULL;
1538 #endif /* HAVE_LIBCURL */
1540 if (!context) return IE_INVALID_CONTEXT;
1541 zfree(context->long_message);
1543 #if HAVE_LIBCURL
1544 /* Check if connection is established */
1545 if (!context->curl) return IE_CONNECTION_CLOSED;
1548 /* Build dummy request */
1549 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1550 if (!request) {
1551 isds_log_message(context, _("Could build ISDS dummy request"));
1552 return IE_ERROR;
1554 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1555 if(!isds_ns) {
1556 isds_log_message(context, _("Could not create ISDS name space"));
1557 xmlFreeNode(request);
1558 return IE_ERROR;
1560 xmlSetNs(request, isds_ns);
1562 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1564 /* XXX: ISDS documentation does not specify response body for
1565 * DummyOperation request. However real server sends back
1566 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1567 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1568 * SOAP body content, e.g. the dmStatus element. */
1570 /* Send dummy request */
1571 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1573 /* Destroy log-in request */
1574 xmlFreeNode(request);
1576 if (soap_err) {
1577 isds_log(ILF_ISDS, ILL_DEBUG,
1578 _("ISDS server could not be contacted\n"));
1579 return soap_err;
1582 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1583 * authentication succeeded if soap_err == IE_SUCCESS */
1586 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1588 return IE_SUCCESS;
1589 #else /* not HAVE_LIBCURL */
1590 return IE_NOTSUP;
1591 #endif
1595 /* Send bogus request to ISDS.
1596 * Just for test purposes */
1597 isds_error isds_bogus_request(struct isds_ctx *context) {
1598 #if HAVE_LIBCURL
1599 isds_error err;
1600 xmlNsPtr isds_ns = NULL;
1601 xmlNodePtr request = NULL;
1602 xmlDocPtr response = NULL;
1603 xmlChar *code = NULL, *message = NULL;
1604 #endif
1606 if (!context) return IE_INVALID_CONTEXT;
1607 zfree(context->long_message);
1609 #if HAVE_LIBCURL
1610 /* Check if connection is established */
1611 if (!context->curl) {
1612 /* Testing printf message */
1613 isds_printf_message(context, "%s", _("I said connection closed"));
1614 return IE_CONNECTION_CLOSED;
1618 /* Build dummy request */
1619 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1620 if (!request) {
1621 isds_log_message(context, _("Could build ISDS bogus request"));
1622 return IE_ERROR;
1624 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1625 if(!isds_ns) {
1626 isds_log_message(context, _("Could not create ISDS name space"));
1627 xmlFreeNode(request);
1628 return IE_ERROR;
1630 xmlSetNs(request, isds_ns);
1632 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1634 /* Sent bogus request */
1635 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1637 /* Destroy request */
1638 xmlFreeNode(request);
1640 if (err) {
1641 isds_log(ILF_ISDS, ILL_DEBUG,
1642 _("Processing ISDS response on bogus request failed\n"));
1643 xmlFreeDoc(response);
1644 return err;
1647 /* Check for response status */
1648 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1649 &code, &message, NULL);
1650 if (err) {
1651 isds_log(ILF_ISDS, ILL_DEBUG,
1652 _("ISDS response on bogus request is missing status\n"));
1653 free(code);
1654 free(message);
1655 xmlFreeDoc(response);
1656 return err;
1658 if (xmlStrcmp(code, BAD_CAST "0000")) {
1659 char *code_locale = _isds_utf82locale((char*)code);
1660 char *message_locale = _isds_utf82locale((char*)message);
1661 isds_log(ILF_ISDS, ILL_DEBUG,
1662 _("Server refused bogus request (code=%s, message=%s)\n"),
1663 code_locale, message_locale);
1664 /* XXX: Literal error messages from ISDS are Czech messages
1665 * (English sometimes) in UTF-8. It's hard to catch them for
1666 * translation. Successfully gettextized would return in locale
1667 * encoding, unsuccessfully translated would pass in UTF-8. */
1668 isds_log_message(context, message_locale);
1669 free(code_locale);
1670 free(message_locale);
1671 free(code);
1672 free(message);
1673 xmlFreeDoc(response);
1674 return IE_ISDS;
1678 free(code);
1679 free(message);
1680 xmlFreeDoc(response);
1682 isds_log(ILF_ISDS, ILL_DEBUG,
1683 _("Bogus message accepted by server. This should not happen.\n"));
1685 return IE_SUCCESS;
1686 #else /* not HAVE_LIBCURL */
1687 return IE_NOTSUP;
1688 #endif
1692 #if HAVE_LIBCURL
1693 /* Serialize XML subtree to buffer preserving XML indentation.
1694 * @context is session context
1695 * @subtree is XML element to be serialized (with children)
1696 * @buffer is automatically reallocated buffer where serialize to
1697 * @length is size of serialized stream in bytes
1698 * @return standard error code, free @buffer in case of error */
1699 static isds_error serialize_subtree(struct isds_ctx *context,
1700 xmlNodePtr subtree, void **buffer, size_t *length) {
1701 isds_error err = IE_SUCCESS;
1702 xmlBufferPtr xml_buffer = NULL;
1703 xmlSaveCtxtPtr save_ctx = NULL;
1704 xmlDocPtr subtree_doc = NULL;
1705 xmlNodePtr subtree_copy;
1706 xmlNsPtr isds_ns;
1707 void *new_buffer;
1709 if (!context) return IE_INVALID_CONTEXT;
1710 if (!buffer) return IE_INVAL;
1711 zfree(*buffer);
1712 if (!subtree || !length) return IE_INVAL;
1714 /* Make temporary XML document with @subtree root element */
1715 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1716 * It can result in not well-formed on invalid XML tree (e.g. name space
1717 * prefix definition can miss. */
1718 /*FIXME */
1720 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1721 if (!subtree_doc) {
1722 isds_log_message(context, _("Could not build temporary document"));
1723 err = IE_ERROR;
1724 goto leave;
1727 /* XXX: Copy subtree and attach the copy to document.
1728 * One node can not bee attached into more document at the same time.
1729 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1730 * automatically.
1731 * XXX: Check xmlSaveTree() too. */
1732 subtree_copy = xmlCopyNodeList(subtree);
1733 if (!subtree_copy) {
1734 isds_log_message(context, _("Could not copy subtree"));
1735 err = IE_ERROR;
1736 goto leave;
1738 xmlDocSetRootElement(subtree_doc, subtree_copy);
1740 /* Only this way we get namespace definition as @xmlns:isds,
1741 * otherwise we get namespace prefix without definition */
1742 /* FIXME: Don't overwrite original default namespace */
1743 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1744 if(!isds_ns) {
1745 isds_log_message(context, _("Could not create ISDS name space"));
1746 err = IE_ERROR;
1747 goto leave;
1749 xmlSetNs(subtree_copy, isds_ns);
1752 /* Serialize the document into buffer */
1753 xml_buffer = xmlBufferCreate();
1754 if (!xml_buffer) {
1755 isds_log_message(context, _("Could not create xmlBuffer"));
1756 err = IE_ERROR;
1757 goto leave;
1759 /* Last argument 0 means to not format the XML tree */
1760 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1761 if (!save_ctx) {
1762 isds_log_message(context, _("Could not create XML serializer"));
1763 err = IE_ERROR;
1764 goto leave;
1766 /* XXX: According LibXML documentation, this function does not return
1767 * meaningful value yet */
1768 xmlSaveDoc(save_ctx, subtree_doc);
1769 if (-1 == xmlSaveFlush(save_ctx)) {
1770 isds_log_message(context,
1771 _("Could not serialize XML subtree"));
1772 err = IE_ERROR;
1773 goto leave;
1775 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1776 * even after xmlSaveFlush(). Thus close it here */
1777 xmlSaveClose(save_ctx); save_ctx = NULL;
1780 /* Store and detach buffer from xml_buffer */
1781 *buffer = xml_buffer->content;
1782 *length = xml_buffer->use;
1783 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1785 /* Shrink buffer */
1786 new_buffer = realloc(*buffer, *length);
1787 if (new_buffer) *buffer = new_buffer;
1789 leave:
1790 if (err) {
1791 zfree(*buffer);
1792 *length = 0;
1795 xmlSaveClose(save_ctx);
1796 xmlBufferFree(xml_buffer);
1797 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1798 return err;
1800 #endif /* HAVE_LIBCURL */
1803 #if 0
1804 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1805 * @context is session context
1806 * @document is original document where @nodeset points to
1807 * @nodeset is XPath node set to dump (recursively)
1808 * @buffer is automatically reallocated buffer where serialize to
1809 * @length is size of serialized stream in bytes
1810 * @return standard error code, free @buffer in case of error */
1811 static isds_error dump_nodeset(struct isds_ctx *context,
1812 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1813 void **buffer, size_t *length) {
1814 isds_error err = IE_SUCCESS;
1815 xmlBufferPtr xml_buffer = NULL;
1816 void *new_buffer;
1818 if (!context) return IE_INVALID_CONTEXT;
1819 if (!buffer) return IE_INVAL;
1820 zfree(*buffer);
1821 if (!document || !nodeset || !length) return IE_INVAL;
1822 *length = 0;
1824 /* Empty node set results into NULL buffer */
1825 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1826 goto leave;
1829 /* Resulting the document into buffer */
1830 xml_buffer = xmlBufferCreate();
1831 if (!xml_buffer) {
1832 isds_log_message(context, _("Could not create xmlBuffer"));
1833 err = IE_ERROR;
1834 goto leave;
1837 /* Iterate over all nodes */
1838 for (int i = 0; i < nodeset->nodeNr; i++) {
1839 /* Serialize node.
1840 * XXX: xmlNodeDump() appends to xml_buffer. */
1841 if (-1 ==
1842 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1843 isds_log_message(context, _("Could not dump XML node"));
1844 err = IE_ERROR;
1845 goto leave;
1849 /* Store and detach buffer from xml_buffer */
1850 *buffer = xml_buffer->content;
1851 *length = xml_buffer->use;
1852 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1854 /* Shrink buffer */
1855 new_buffer = realloc(*buffer, *length);
1856 if (new_buffer) *buffer = new_buffer;
1859 leave:
1860 if (err) {
1861 zfree(*buffer);
1862 *length = 0;
1865 xmlBufferFree(xml_buffer);
1866 return err;
1868 #endif
1870 #if 0
1871 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1872 * @context is session context
1873 * @document is original document where @nodeset points to
1874 * @nodeset is XPath node set to dump (recursively)
1875 * @buffer is automatically reallocated buffer where serialize to
1876 * @length is size of serialized stream in bytes
1877 * @return standard error code, free @buffer in case of error */
1878 static isds_error dump_nodeset(struct isds_ctx *context,
1879 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1880 void **buffer, size_t *length) {
1881 isds_error err = IE_SUCCESS;
1882 xmlBufferPtr xml_buffer = NULL;
1883 xmlSaveCtxtPtr save_ctx = NULL;
1884 void *new_buffer;
1886 if (!context) return IE_INVALID_CONTEXT;
1887 if (!buffer) return IE_INVAL;
1888 zfree(*buffer);
1889 if (!document || !nodeset || !length) return IE_INVAL;
1890 *length = 0;
1892 /* Empty node set results into NULL buffer */
1893 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1894 goto leave;
1897 /* Resulting the document into buffer */
1898 xml_buffer = xmlBufferCreate();
1899 if (!xml_buffer) {
1900 isds_log_message(context, _("Could not create xmlBuffer"));
1901 err = IE_ERROR;
1902 goto leave;
1904 if (xmlSubstituteEntitiesDefault(1)) {
1905 isds_log_message(context, _("Could not disable attribute escaping"));
1906 err = IE_ERROR;
1907 goto leave;
1909 /* Last argument means:
1910 * 0 to not format the XML tree
1911 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1912 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1913 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1914 if (!save_ctx) {
1915 isds_log_message(context, _("Could not create XML serializer"));
1916 err = IE_ERROR;
1917 goto leave;
1919 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1920 isds_log_message(context, _("Could not disable attribute escaping"));
1921 err = IE_ERROR;
1922 goto leave;
1926 /* Iterate over all nodes */
1927 for (int i = 0; i < nodeset->nodeNr; i++) {
1928 /* Serialize node.
1929 * XXX: xmlNodeDump() appends to xml_buffer. */
1930 /*if (-1 ==
1931 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1933 /* XXX: According LibXML documentation, this function does not return
1934 * meaningful value yet */
1935 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1936 if (-1 == xmlSaveFlush(save_ctx)) {
1937 isds_log_message(context,
1938 _("Could not serialize XML subtree"));
1939 err = IE_ERROR;
1940 goto leave;
1944 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1945 * even after xmlSaveFlush(). Thus close it here */
1946 xmlSaveClose(save_ctx); save_ctx = NULL;
1948 /* Store and detach buffer from xml_buffer */
1949 *buffer = xml_buffer->content;
1950 *length = xml_buffer->use;
1951 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1953 /* Shrink buffer */
1954 new_buffer = realloc(*buffer, *length);
1955 if (new_buffer) *buffer = new_buffer;
1957 leave:
1958 if (err) {
1959 zfree(*buffer);
1960 *length = 0;
1963 xmlSaveClose(save_ctx);
1964 xmlBufferFree(xml_buffer);
1965 return err;
1967 #endif
1970 #if HAVE_LIBCURL
1971 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1972 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1973 if (!string || !type) return IE_INVAL;
1975 if (!xmlStrcmp(string, BAD_CAST "FO"))
1976 *type = DBTYPE_FO;
1977 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1978 *type = DBTYPE_PFO;
1979 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1980 *type = DBTYPE_PFO_ADVOK;
1981 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1982 *type = DBTYPE_PFO_DANPOR;
1983 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1984 *type = DBTYPE_PFO_INSSPR;
1985 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1986 *type = DBTYPE_PO;
1987 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1988 *type = DBTYPE_PO_ZAK;
1989 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1990 *type = DBTYPE_PO_REQ;
1991 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1992 *type = DBTYPE_OVM;
1993 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1994 *type = DBTYPE_OVM_NOTAR;
1995 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1996 *type = DBTYPE_OVM_EXEKUT;
1997 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1998 *type = DBTYPE_OVM_REQ;
1999 else
2000 return IE_ENUM;
2001 return IE_SUCCESS;
2005 /* Convert ISDS dbType enum @type to UTF-8 string.
2006 * @Return pointer to static string, or NULL if unknown enum value */
2007 static const xmlChar *isds_DbType2string(const isds_DbType type) {
2008 switch(type) {
2009 /* DBTYPE_SYSTEM is invalid value from point of view of public
2010 * SOAP interface. */
2011 case DBTYPE_FO: return(BAD_CAST "FO"); break;
2012 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
2013 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
2014 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
2015 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
2016 case DBTYPE_PO: return(BAD_CAST "PO"); break;
2017 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
2018 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
2019 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
2020 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
2021 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
2022 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
2023 default: return NULL; break;
2028 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2029 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
2030 if (!string || !type) return IE_INVAL;
2032 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2033 *type = USERTYPE_PRIMARY;
2034 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2035 *type = USERTYPE_ENTRUSTED;
2036 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2037 *type = USERTYPE_ADMINISTRATOR;
2038 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2039 *type = USERTYPE_OFFICIAL;
2040 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2041 *type = USERTYPE_OFFICIAL_CERT;
2042 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2043 *type = USERTYPE_LIQUIDATOR;
2044 else
2045 return IE_ENUM;
2046 return IE_SUCCESS;
2050 /* Convert ISDS userType enum @type to UTF-8 string.
2051 * @Return pointer to static string, or NULL if unknown enum value */
2052 static const xmlChar *isds_UserType2string(const isds_UserType type) {
2053 switch(type) {
2054 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
2055 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
2056 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
2057 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
2058 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
2059 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
2060 default: return NULL; break;
2065 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2066 static isds_error string2isds_sender_type(const xmlChar *string,
2067 isds_sender_type *type) {
2068 if (!string || !type) return IE_INVAL;
2070 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2071 *type = SENDERTYPE_PRIMARY;
2072 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2073 *type = SENDERTYPE_ENTRUSTED;
2074 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2075 *type = SENDERTYPE_ADMINISTRATOR;
2076 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2077 *type = SENDERTYPE_OFFICIAL;
2078 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2079 *type = SENDERTYPE_VIRTUAL;
2080 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2081 *type = SENDERTYPE_OFFICIAL_CERT;
2082 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2083 *type = SENDERTYPE_LIQUIDATOR;
2084 else
2085 return IE_ENUM;
2086 return IE_SUCCESS;
2090 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2091 static isds_error string2isds_payment_type(const xmlChar *string,
2092 isds_payment_type *type) {
2093 if (!string || !type) return IE_INVAL;
2095 if (!xmlStrcmp(string, BAD_CAST "K"))
2096 *type = PAYMENT_SENDER;
2097 else if (!xmlStrcmp(string, BAD_CAST "O"))
2098 *type = PAYMENT_RESPONSE;
2099 else if (!xmlStrcmp(string, BAD_CAST "G"))
2100 *type = PAYMENT_SPONSOR;
2101 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2102 *type = PAYMENT_SPONSOR_LIMITED;
2103 else if (!xmlStrcmp(string, BAD_CAST "D"))
2104 *type = PAYMENT_SPONSOR_EXTERNAL;
2105 else if (!xmlStrcmp(string, BAD_CAST "E"))
2106 *type = PAYMENT_STAMP;
2107 else
2108 return IE_ENUM;
2109 return IE_SUCCESS;
2113 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2114 * ciEventType is integer but we convert it from string representation
2115 * directly. */
2116 static isds_error string2isds_credit_event_type(const xmlChar *string,
2117 isds_credit_event_type *type) {
2118 if (!string || !type) return IE_INVAL;
2120 if (!xmlStrcmp(string, BAD_CAST "1"))
2121 *type = ISDS_CREDIT_CHARGED;
2122 else if (!xmlStrcmp(string, BAD_CAST "2"))
2123 *type = ISDS_CREDIT_DISCHARGED;
2124 else if (!xmlStrcmp(string, BAD_CAST "3"))
2125 *type = ISDS_CREDIT_MESSAGE_SENT;
2126 else if (!xmlStrcmp(string, BAD_CAST "4"))
2127 *type = ISDS_CREDIT_STORAGE_SET;
2128 else if (!xmlStrcmp(string, BAD_CAST "5"))
2129 *type = ISDS_CREDIT_EXPIRED;
2130 else
2131 return IE_ENUM;
2132 return IE_SUCCESS;
2136 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2137 * @Return pointer to static string, or NULL if unknown enum value */
2138 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2139 switch(type) {
2140 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2141 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2142 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2143 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2144 default: return NULL; break;
2149 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2150 * ISDSSearch2/searchType value.
2151 * @Return pointer to static string, or NULL if unknown enum value */
2152 static const xmlChar *isds_fulltext_target2string(
2153 const isds_fulltext_target type) {
2154 switch(type) {
2155 case FULLTEXT_ALL: return(BAD_CAST "GENERAL"); break;
2156 case FULLTEXT_ADDRESS: return(BAD_CAST "ADDRESS"); break;
2157 case FULLTEXT_IC: return(BAD_CAST "ICO"); break;
2158 case FULLTEXT_BOX_ID: return(BAD_CAST "DBID"); break;
2159 default: return NULL; break;
2162 #endif /* HAVE_LIBCURL */
2165 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2166 * @Return IE_ENUM if @string is not valid enum member */
2167 static isds_error string2isds_FileMetaType(const xmlChar *string,
2168 isds_FileMetaType *type) {
2169 if (!string || !type) return IE_INVAL;
2171 if (!xmlStrcmp(string, BAD_CAST "main"))
2172 *type = FILEMETATYPE_MAIN;
2173 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2174 *type = FILEMETATYPE_ENCLOSURE;
2175 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2176 *type = FILEMETATYPE_SIGNATURE;
2177 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2178 *type = FILEMETATYPE_META;
2179 else
2180 return IE_ENUM;
2181 return IE_SUCCESS;
2185 /* Convert UTF-8 @string to ISDS hash @algorithm.
2186 * @Return IE_ENUM if @string is not valid enum member */
2187 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2188 isds_hash_algorithm *algorithm) {
2189 if (!string || !algorithm) return IE_INVAL;
2191 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2192 *algorithm = HASH_ALGORITHM_MD5;
2193 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2194 *algorithm = HASH_ALGORITHM_SHA_1;
2195 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2196 *algorithm = HASH_ALGORITHM_SHA_224;
2197 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2198 *algorithm = HASH_ALGORITHM_SHA_256;
2199 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2200 *algorithm = HASH_ALGORITHM_SHA_384;
2201 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2202 *algorithm = HASH_ALGORITHM_SHA_512;
2203 else
2204 return IE_ENUM;
2205 return IE_SUCCESS;
2209 #if HAVE_LIBCURL
2210 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2211 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2212 if (!time || !string) return IE_INVAL;
2214 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2215 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2216 return IE_ERROR;
2218 return IE_SUCCESS;
2222 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2223 * respects the @time microseconds too. */
2224 static isds_error timeval2timestring(const struct timeval *time,
2225 xmlChar **string) {
2226 struct tm broken;
2227 time_t seconds_as_time_t;
2229 if (!time || !string) return IE_INVAL;
2231 /* MinGW32 GCC 4.8+ uses 64-bit time_t but time->tv_sec is defined as
2232 * 32-bit long in Microsoft API. Convert value to the type expected by
2233 * gmtime_r(). */
2234 seconds_as_time_t = time->tv_sec;
2235 if (!gmtime_r(&seconds_as_time_t, &broken)) return IE_DATE;
2236 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2238 /* TODO: small negative year should be formatted as "-0012". This is not
2239 * true for glibc "%04d". We should implement it.
2240 * time->tv_usec type is su_seconds_t which is required to be signed
2241 * integer to accomodate values from range [-1, 1000000].
2242 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2243 if (-1 == isds_asprintf((char **) string,
2244 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRIdMAX,
2245 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2246 broken.tm_hour, broken.tm_min, broken.tm_sec,
2247 (intmax_t)time->tv_usec))
2248 return IE_ERROR;
2250 return IE_SUCCESS;
2252 #endif /* HAVE_LIBCURL */
2255 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2256 * It respects microseconds too. Microseconds are rounded half up.
2257 * In case of error, @time will be freed. */
2258 static isds_error timestring2timeval(const xmlChar *string,
2259 struct timeval **time) {
2260 struct tm broken;
2261 char *offset, *delim, *endptr;
2262 const int subsecond_resolution = 6;
2263 char subseconds[subsecond_resolution + 1];
2264 _Bool round_up = 0;
2265 int offset_hours, offset_minutes;
2266 int i;
2267 long int long_number;
2268 #ifdef _WIN32
2269 int tmp;
2270 #endif
2272 if (!time) return IE_INVAL;
2273 if (!string) {
2274 zfree(*time);
2275 return IE_INVAL;
2278 memset(&broken, 0, sizeof(broken));
2280 if (!*time) {
2281 *time = calloc(1, sizeof(**time));
2282 if (!*time) return IE_NOMEM;
2283 } else {
2284 memset(*time, 0, sizeof(**time));
2288 /* xsd:date is ISO 8601 string, thus ASCII */
2289 /*TODO: negative year */
2291 #ifdef _WIN32
2292 i = 0;
2293 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2294 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2295 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2296 &i)) < 6) {
2297 zfree(*time);
2298 return IE_DATE;
2301 broken.tm_year -= 1900;
2302 broken.tm_mon--;
2303 offset = (char*)string + i;
2304 #else
2305 /* Parse date and time without subseconds and offset */
2306 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2307 if (!offset) {
2308 zfree(*time);
2309 return IE_DATE;
2311 #endif
2313 /* Get subseconds */
2314 if (*offset == '.' ) {
2315 offset++;
2317 /* Copy first 6 digits, pad it with zeros.
2318 * Current server implementation uses only millisecond resolution. */
2319 /* TODO: isdigit() is locale sensitive */
2320 for (i = 0;
2321 i < subsecond_resolution && isdigit(*offset);
2322 i++, offset++) {
2323 subseconds[i] = *offset;
2325 if (subsecond_resolution == i && isdigit(*offset)) {
2326 /* Check 7th digit for rounding */
2327 if (*offset >= '5') round_up = 1;
2328 offset++;
2330 for (; i < subsecond_resolution; i++) {
2331 subseconds[i] = '0';
2333 subseconds[subsecond_resolution] = '\0';
2335 /* Convert it into integer */
2336 long_number = strtol(subseconds, &endptr, 10);
2337 if (*endptr != '\0' || long_number == LONG_MIN ||
2338 long_number == LONG_MAX) {
2339 zfree(*time);
2340 return IE_DATE;
2342 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2343 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2344 * microseconds" and "the type shall be a signed integer capable of
2345 * storing values at least in the range [-1, 1000000]. */
2346 if (long_number < -1 || long_number >= 1000000) {
2347 zfree(*time);
2348 return IE_DATE;
2350 (*time)->tv_usec = long_number;
2352 /* Round the subseconds */
2353 if (round_up) {
2354 if (999999 == (*time)->tv_usec) {
2355 (*time)->tv_usec = 0;
2356 broken.tm_sec++;
2357 } else {
2358 (*time)->tv_usec++;
2362 /* move to the zone offset delimiter or signal NULL*/
2363 delim = strchr(offset, '-');
2364 if (!delim)
2365 delim = strchr(offset, '+');
2366 if (!delim)
2367 delim = strchr(offset, 'Z');
2368 offset = delim;
2371 /* Get zone offset */
2372 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2373 * "" equals to "Z" and it means UTC zone. */
2374 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2375 * colon separator */
2376 if (offset && (*offset == '-' || *offset == '+')) {
2377 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2378 zfree(*time);
2379 return IE_DATE;
2381 if (*offset == '+') {
2382 broken.tm_hour -= offset_hours;
2383 broken.tm_min -= offset_minutes;
2384 } else {
2385 broken.tm_hour += offset_hours;
2386 broken.tm_min += offset_minutes;
2390 /* Convert to time_t */
2391 (*time)->tv_sec = _isds_timegm(&broken);
2392 if ((*time)->tv_sec == (time_t) -1) {
2393 zfree(*time);
2394 return IE_DATE;
2397 return IE_SUCCESS;
2401 /* Convert unsigned int into isds_message_status.
2402 * @context is session context
2403 * @number is pointer to number value. NULL will be treated as invalid value.
2404 * @status is automatically reallocated status
2405 * @return IE_SUCCESS, or error code and free status */
2406 static isds_error uint2isds_message_status(struct isds_ctx *context,
2407 const unsigned long int *number, isds_message_status **status) {
2408 if (!context) return IE_INVALID_CONTEXT;
2409 if (!status) return IE_INVAL;
2411 free(*status); *status = NULL;
2412 if (!number) return IE_INVAL;
2414 if (*number < 1 || *number > 10) {
2415 isds_printf_message(context, _("Invalid message status value: %lu"),
2416 *number);
2417 return IE_ENUM;
2420 *status = malloc(sizeof(**status));
2421 if (!*status) return IE_NOMEM;
2423 **status = 1 << *number;
2424 return IE_SUCCESS;
2428 /* Convert event description string into isds_event members type and
2429 * description
2430 * @string is raw event description starting with event prefix
2431 * @event is structure where to store type and stripped description to
2432 * @return standard error code, unknown prefix is not classified as an error.
2433 * */
2434 static isds_error eventstring2event(const xmlChar *string,
2435 struct isds_event* event) {
2436 const xmlChar *known_prefixes[] = {
2437 BAD_CAST "EV0:",
2438 BAD_CAST "EV1:",
2439 BAD_CAST "EV2:",
2440 BAD_CAST "EV3:",
2441 BAD_CAST "EV4:",
2442 BAD_CAST "EV5:",
2443 BAD_CAST "EV11:",
2444 BAD_CAST "EV12:",
2445 BAD_CAST "EV13:"
2447 const isds_event_type types[] = {
2448 EVENT_ENTERED_SYSTEM,
2449 EVENT_ACCEPTED_BY_RECIPIENT,
2450 EVENT_ACCEPTED_BY_FICTION,
2451 EVENT_UNDELIVERABLE,
2452 EVENT_COMMERCIAL_ACCEPTED,
2453 EVENT_DELIVERED,
2454 EVENT_PRIMARY_LOGIN,
2455 EVENT_ENTRUSTED_LOGIN,
2456 EVENT_SYSCERT_LOGIN
2458 unsigned int index;
2459 size_t length;
2461 if (!string || !event) return IE_INVAL;
2463 if (!event->type) {
2464 event->type = malloc(sizeof(*event->type));
2465 if (!(event->type)) return IE_NOMEM;
2467 zfree(event->description);
2469 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2470 index++) {
2471 length = xmlUTF8Strlen(known_prefixes[index]);
2473 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2474 /* Prefix is known */
2475 *event->type = types[index];
2477 /* Strip prefix from description and spaces */
2478 /* TODO: Recognize all white spaces from UCS blank class and
2479 * operate on UTF-8 chars. */
2480 for (; string[length] != '\0' && string[length] == ' '; length++);
2481 event->description = strdup((char *) (string + length));
2482 if (!(event->description)) return IE_NOMEM;
2484 return IE_SUCCESS;
2488 /* Unknown event prefix.
2489 * XSD allows any string */
2490 char *string_locale = _isds_utf82locale((char *) string);
2491 isds_log(ILF_ISDS, ILL_WARNING,
2492 _("Unknown delivery info event prefix: %s\n"), string_locale);
2493 free(string_locale);
2495 *event->type = EVENT_UKNOWN;
2496 event->description = strdup((char *) string);
2497 if (!(event->description)) return IE_NOMEM;
2499 return IE_SUCCESS;
2503 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2504 * and leave label */
2505 #define EXTRACT_STRING(element, string) { \
2506 xmlXPathFreeObject(result); \
2507 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2508 if (NULL == (result)) { \
2509 err = IE_ERROR; \
2510 goto leave; \
2512 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2513 if (result->nodesetval->nodeNr > 1) { \
2514 isds_printf_message(context, _("Multiple %s element"), element); \
2515 err = IE_ERROR; \
2516 goto leave; \
2518 (string) = (char *) \
2519 xmlXPathCastNodeSetToString(result->nodesetval); \
2520 if (NULL == (string)) { \
2521 err = IE_ERROR; \
2522 goto leave; \
2527 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2529 char *string = NULL; \
2530 EXTRACT_STRING(element, string); \
2532 if (string) { \
2533 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2534 if (!(booleanPtr)) { \
2535 free(string); \
2536 err = IE_NOMEM; \
2537 goto leave; \
2540 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2541 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2542 *(booleanPtr) = 1; \
2543 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2544 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2545 *(booleanPtr) = 0; \
2546 else { \
2547 char *string_locale = _isds_utf82locale((char*)string); \
2548 isds_printf_message(context, \
2549 _("%s value is not valid boolean: %s"), \
2550 element, string_locale); \
2551 free(string_locale); \
2552 free(string); \
2553 err = IE_ERROR; \
2554 goto leave; \
2557 free(string); \
2561 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2563 char *string = NULL; \
2564 EXTRACT_STRING(element, string); \
2566 if (NULL == string) { \
2567 isds_printf_message(context, _("%s element is empty"), element); \
2568 err = IE_ERROR; \
2569 goto leave; \
2571 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2572 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2573 (boolean) = 1; \
2574 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2575 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2576 (boolean) = 0; \
2577 else { \
2578 char *string_locale = _isds_utf82locale((char*)string); \
2579 isds_printf_message(context, \
2580 _("%s value is not valid boolean: %s"), \
2581 element, string_locale); \
2582 free(string_locale); \
2583 free(string); \
2584 err = IE_ERROR; \
2585 goto leave; \
2588 free(string); \
2591 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2593 char *string = NULL; \
2594 EXTRACT_STRING(element, string); \
2595 if (string) { \
2596 long int number; \
2597 char *endptr; \
2599 number = strtol((char*)string, &endptr, 10); \
2601 if (*endptr != '\0') { \
2602 char *string_locale = _isds_utf82locale((char *)string); \
2603 isds_printf_message(context, \
2604 _("%s is not valid integer: %s"), \
2605 element, string_locale); \
2606 free(string_locale); \
2607 free(string); \
2608 err = IE_ISDS; \
2609 goto leave; \
2612 if (number == LONG_MIN || number == LONG_MAX) { \
2613 char *string_locale = _isds_utf82locale((char *)string); \
2614 isds_printf_message(context, \
2615 _("%s value out of range of long int: %s"), \
2616 element, string_locale); \
2617 free(string_locale); \
2618 free(string); \
2619 err = IE_ERROR; \
2620 goto leave; \
2623 free(string); string = NULL; \
2625 if (!(preallocated)) { \
2626 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2627 if (!(longintPtr)) { \
2628 err = IE_NOMEM; \
2629 goto leave; \
2632 *(longintPtr) = number; \
2636 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2638 char *string = NULL; \
2639 EXTRACT_STRING(element, string); \
2640 if (string) { \
2641 long int number; \
2642 char *endptr; \
2644 number = strtol((char*)string, &endptr, 10); \
2646 if (*endptr != '\0') { \
2647 char *string_locale = _isds_utf82locale((char *)string); \
2648 isds_printf_message(context, \
2649 _("%s is not valid integer: %s"), \
2650 element, string_locale); \
2651 free(string_locale); \
2652 free(string); \
2653 err = IE_ISDS; \
2654 goto leave; \
2657 if (number == LONG_MIN || number == LONG_MAX) { \
2658 char *string_locale = _isds_utf82locale((char *)string); \
2659 isds_printf_message(context, \
2660 _("%s value out of range of long int: %s"), \
2661 element, string_locale); \
2662 free(string_locale); \
2663 free(string); \
2664 err = IE_ERROR; \
2665 goto leave; \
2668 free(string); string = NULL; \
2669 if (number < 0) { \
2670 isds_printf_message(context, \
2671 _("%s value is negative: %ld"), element, number); \
2672 err = IE_ERROR; \
2673 goto leave; \
2676 if (!(preallocated)) { \
2677 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2678 if (!(ulongintPtr)) { \
2679 err = IE_NOMEM; \
2680 goto leave; \
2683 *(ulongintPtr) = number; \
2687 #define EXTRACT_DATE(element, tmPtr) { \
2688 char *string = NULL; \
2689 EXTRACT_STRING(element, string); \
2690 if (NULL != string) { \
2691 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2692 if (NULL == (tmPtr)) { \
2693 free(string); \
2694 err = IE_NOMEM; \
2695 goto leave; \
2697 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2698 if (err) { \
2699 if (err == IE_NOTSUP) { \
2700 err = IE_ISDS; \
2701 char *string_locale = _isds_utf82locale(string); \
2702 char *element_locale = _isds_utf82locale(element); \
2703 isds_printf_message(context, _("Invalid %s value: %s"), \
2704 element_locale, string_locale); \
2705 free(string_locale); \
2706 free(element_locale); \
2708 free(string); \
2709 goto leave; \
2711 free(string); \
2715 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2716 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2717 NULL); \
2718 if ((required) && (!string)) { \
2719 char *attribute_locale = _isds_utf82locale(attribute); \
2720 char *element_locale = \
2721 _isds_utf82locale((char *)xpath_ctx->node->name); \
2722 isds_printf_message(context, \
2723 _("Could not extract required %s attribute value from " \
2724 "%s element"), attribute_locale, element_locale); \
2725 free(element_locale); \
2726 free(attribute_locale); \
2727 err = IE_ERROR; \
2728 goto leave; \
2733 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2735 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2736 (xmlChar *) (string)); \
2737 if (!node) { \
2738 isds_printf_message(context, \
2739 _("Could not add %s child to %s element"), \
2740 element, (parent)->name); \
2741 err = IE_ERROR; \
2742 goto leave; \
2746 #define INSERT_STRING(parent, element, string) \
2747 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2749 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2751 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2752 else { INSERT_STRING(parent, element, "false"); } \
2755 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2757 if (booleanPtr) { \
2758 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2759 } else { \
2760 INSERT_STRING(parent, element, NULL); \
2764 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2765 if ((longintPtr)) { \
2766 /* FIXME: locale sensitive */ \
2767 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2768 err = IE_NOMEM; \
2769 goto leave; \
2771 INSERT_STRING(parent, element, buffer) \
2772 free(buffer); (buffer) = NULL; \
2773 } else { INSERT_STRING(parent, element, NULL) } \
2776 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2777 if ((ulongintPtr)) { \
2778 /* FIXME: locale sensitive */ \
2779 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2780 err = IE_NOMEM; \
2781 goto leave; \
2783 INSERT_STRING(parent, element, buffer) \
2784 free(buffer); (buffer) = NULL; \
2785 } else { INSERT_STRING(parent, element, NULL) } \
2788 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2790 /* FIXME: locale sensitive */ \
2791 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2792 err = IE_NOMEM; \
2793 goto leave; \
2795 INSERT_STRING(parent, element, buffer) \
2796 free(buffer); (buffer) = NULL; \
2799 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2800 * new attribute. */
2801 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2803 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2804 (xmlChar *) (string)); \
2805 if (!attribute_node) { \
2806 isds_printf_message(context, _("Could not add %s " \
2807 "attribute to %s element"), \
2808 (attribute), (parent)->name); \
2809 err = IE_ERROR; \
2810 goto leave; \
2814 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2815 if (string) { \
2816 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2817 if (length > (maximum)) { \
2818 isds_printf_message(context, \
2819 ngettext("%s has more than %d characters", \
2820 "%s has more than %d characters", (maximum)), \
2821 (name), (maximum)); \
2822 err = IE_2BIG; \
2823 goto leave; \
2825 if (length < (minimum)) { \
2826 isds_printf_message(context, \
2827 ngettext("%s has less than %d characters", \
2828 "%s has less than %d characters", (minimum)), \
2829 (name), (minimum)); \
2830 err = IE_2SMALL; \
2831 goto leave; \
2836 #define INSERT_ELEMENT(child, parent, element) \
2838 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2839 if (!(child)) { \
2840 isds_printf_message(context, \
2841 _("Could not add %s child to %s element"), \
2842 (element), (parent)->name); \
2843 err = IE_ERROR; \
2844 goto leave; \
2849 /* Find child element by name in given XPath context and switch context onto
2850 * it. The child must be uniq and must exist. Otherwise fails.
2851 * @context is ISDS context
2852 * @child is child element name
2853 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2854 * into it child. In error case, the @xpath_ctx keeps original value. */
2855 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2856 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2857 isds_error err = IE_SUCCESS;
2858 xmlXPathObjectPtr result = NULL;
2860 if (!context) return IE_INVALID_CONTEXT;
2861 if (!child || !xpath_ctx) return IE_INVAL;
2863 /* Find child */
2864 result = xmlXPathEvalExpression(child, xpath_ctx);
2865 if (!result) {
2866 err = IE_XML;
2867 goto leave;
2870 /* No match */
2871 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2872 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2873 char *child_locale = _isds_utf82locale((char*) child);
2874 isds_printf_message(context,
2875 _("%s element does not contain %s child"),
2876 parent_locale, child_locale);
2877 free(child_locale);
2878 free(parent_locale);
2879 err = IE_NOEXIST;
2880 goto leave;
2883 /* More matches */
2884 if (result->nodesetval->nodeNr > 1) {
2885 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2886 char *child_locale = _isds_utf82locale((char*) child);
2887 isds_printf_message(context,
2888 _("%s element contains multiple %s children"),
2889 parent_locale, child_locale);
2890 free(child_locale);
2891 free(parent_locale);
2892 err = IE_NOTUNIQ;
2893 goto leave;
2896 /* Switch context */
2897 xpath_ctx->node = result->nodesetval->nodeTab[0];
2899 leave:
2900 xmlXPathFreeObject(result);
2901 return err;
2906 #if HAVE_LIBCURL
2907 /* Find and convert XSD:gPersonName group in current node into structure
2908 * @context is ISDS context
2909 * @personName is automatically reallocated person name structure. If no member
2910 * value is found, will be freed.
2911 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2912 * elements
2913 * In case of error @personName will be freed. */
2914 static isds_error extract_gPersonName(struct isds_ctx *context,
2915 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2916 isds_error err = IE_SUCCESS;
2917 xmlXPathObjectPtr result = NULL;
2919 if (!context) return IE_INVALID_CONTEXT;
2920 if (!personName) return IE_INVAL;
2921 isds_PersonName_free(personName);
2922 if (!xpath_ctx) return IE_INVAL;
2925 *personName = calloc(1, sizeof(**personName));
2926 if (!*personName) {
2927 err = IE_NOMEM;
2928 goto leave;
2931 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2932 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2933 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2934 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2936 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2937 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2938 isds_PersonName_free(personName);
2940 leave:
2941 if (err) isds_PersonName_free(personName);
2942 xmlXPathFreeObject(result);
2943 return err;
2947 /* Find and convert XSD:gAddress group in current node into structure
2948 * @context is ISDS context
2949 * @address is automatically reallocated address structure. If no member
2950 * value is found, will be freed.
2951 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2952 * elements
2953 * In case of error @address will be freed. */
2954 static isds_error extract_gAddress(struct isds_ctx *context,
2955 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2956 isds_error err = IE_SUCCESS;
2957 xmlXPathObjectPtr result = NULL;
2959 if (!context) return IE_INVALID_CONTEXT;
2960 if (!address) return IE_INVAL;
2961 isds_Address_free(address);
2962 if (!xpath_ctx) return IE_INVAL;
2965 *address = calloc(1, sizeof(**address));
2966 if (!*address) {
2967 err = IE_NOMEM;
2968 goto leave;
2971 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2972 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2973 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2974 EXTRACT_STRING("isds:adNumberInMunicipality",
2975 (*address)->adNumberInMunicipality);
2976 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2977 EXTRACT_STRING("isds:adState", (*address)->adState);
2979 if (!(*address)->adCity && !(*address)->adStreet &&
2980 !(*address)->adNumberInStreet &&
2981 !(*address)->adNumberInMunicipality &&
2982 !(*address)->adZipCode && !(*address)->adState)
2983 isds_Address_free(address);
2985 leave:
2986 if (err) isds_Address_free(address);
2987 xmlXPathFreeObject(result);
2988 return err;
2992 /* Find and convert isds:biDate element in current node into structure
2993 * @context is ISDS context
2994 * @biDate is automatically reallocated birth date structure. If no member
2995 * value is found, will be freed.
2996 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2997 * element
2998 * In case of error @biDate will be freed. */
2999 static isds_error extract_BiDate(struct isds_ctx *context,
3000 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
3001 isds_error err = IE_SUCCESS;
3002 xmlXPathObjectPtr result = NULL;
3003 char *string = NULL;
3005 if (!context) return IE_INVALID_CONTEXT;
3006 if (!biDate) return IE_INVAL;
3007 zfree(*biDate);
3008 if (!xpath_ctx) return IE_INVAL;
3010 EXTRACT_STRING("isds:biDate", string);
3011 if (string) {
3012 *biDate = calloc(1, sizeof(**biDate));
3013 if (!*biDate) {
3014 err = IE_NOMEM;
3015 goto leave;
3017 err = _isds_datestring2tm((xmlChar *)string, *biDate);
3018 if (err) {
3019 if (err == IE_NOTSUP) {
3020 err = IE_ISDS;
3021 char *string_locale = _isds_utf82locale(string);
3022 isds_printf_message(context,
3023 _("Invalid isds:biDate value: %s"), string_locale);
3024 free(string_locale);
3026 goto leave;
3030 leave:
3031 if (err) zfree(*biDate);
3032 free(string);
3033 xmlXPathFreeObject(result);
3034 return err;
3038 /* Convert isds:dBOwnerInfo XML tree into structure
3039 * @context is ISDS context
3040 * @db_owner_info is automatically reallocated box owner info structure
3041 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
3042 * In case of error @db_owner_info will be freed. */
3043 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
3044 struct isds_DbOwnerInfo **db_owner_info,
3045 xmlXPathContextPtr xpath_ctx) {
3046 isds_error err = IE_SUCCESS;
3047 xmlXPathObjectPtr result = NULL;
3048 char *string = NULL;
3050 if (!context) return IE_INVALID_CONTEXT;
3051 if (!db_owner_info) return IE_INVAL;
3052 isds_DbOwnerInfo_free(db_owner_info);
3053 if (!xpath_ctx) return IE_INVAL;
3056 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3057 if (!*db_owner_info) {
3058 err = IE_NOMEM;
3059 goto leave;
3062 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
3064 EXTRACT_STRING("isds:dbType", string);
3065 if (string) {
3066 (*db_owner_info)->dbType =
3067 calloc(1, sizeof(*((*db_owner_info)->dbType)));
3068 if (!(*db_owner_info)->dbType) {
3069 err = IE_NOMEM;
3070 goto leave;
3072 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
3073 if (err) {
3074 zfree((*db_owner_info)->dbType);
3075 if (err == IE_ENUM) {
3076 err = IE_ISDS;
3077 char *string_locale = _isds_utf82locale(string);
3078 isds_printf_message(context, _("Unknown isds:dbType: %s"),
3079 string_locale);
3080 free(string_locale);
3082 goto leave;
3084 zfree(string);
3087 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
3089 err = extract_gPersonName(context, &(*db_owner_info)->personName,
3090 xpath_ctx);
3091 if (err) goto leave;
3093 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
3095 (*db_owner_info)->birthInfo =
3096 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
3097 if (!(*db_owner_info)->birthInfo) {
3098 err = IE_NOMEM;
3099 goto leave;
3101 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
3102 xpath_ctx);
3103 if (err) goto leave;
3104 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3105 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3106 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3107 if (!(*db_owner_info)->birthInfo->biDate &&
3108 !(*db_owner_info)->birthInfo->biCity &&
3109 !(*db_owner_info)->birthInfo->biCounty &&
3110 !(*db_owner_info)->birthInfo->biState)
3111 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3113 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3114 if (err) goto leave;
3116 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3117 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3118 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3119 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3120 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3122 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3124 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3125 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3126 (*db_owner_info)->dbOpenAddressing);
3128 leave:
3129 if (err) isds_DbOwnerInfo_free(db_owner_info);
3130 free(string);
3131 xmlXPathFreeObject(result);
3132 return err;
3136 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3137 * @context is session context
3138 * @owner is libisds structure with box description
3139 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3140 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3141 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
3143 isds_error err = IE_SUCCESS;
3144 xmlNodePtr node;
3145 xmlChar *string = NULL;
3147 if (!context) return IE_INVALID_CONTEXT;
3148 if (!owner || !db_owner_info) return IE_INVAL;
3151 /* Build XSD:tDbOwnerInfo */
3152 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3153 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3155 /* dbType */
3156 if (owner->dbType) {
3157 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
3158 if (!type_string) {
3159 isds_printf_message(context, _("Invalid dbType value: %d"),
3160 *(owner->dbType));
3161 err = IE_ENUM;
3162 goto leave;
3164 INSERT_STRING(db_owner_info, "dbType", type_string);
3166 INSERT_STRING(db_owner_info, "ic", owner->ic);
3167 if (owner->personName) {
3168 INSERT_STRING(db_owner_info, "pnFirstName",
3169 owner->personName->pnFirstName);
3170 INSERT_STRING(db_owner_info, "pnMiddleName",
3171 owner->personName->pnMiddleName);
3172 INSERT_STRING(db_owner_info, "pnLastName",
3173 owner->personName->pnLastName);
3174 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3175 owner->personName->pnLastNameAtBirth);
3177 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3178 if (owner->birthInfo) {
3179 if (owner->birthInfo->biDate) {
3180 if (!tm2datestring(owner->birthInfo->biDate, &string))
3181 INSERT_STRING(db_owner_info, "biDate", string);
3182 free(string); string = NULL;
3184 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
3185 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
3186 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
3188 if (owner->address) {
3189 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
3190 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
3191 INSERT_STRING(db_owner_info, "adNumberInStreet",
3192 owner->address->adNumberInStreet);
3193 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3194 owner->address->adNumberInMunicipality);
3195 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
3196 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
3198 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3199 INSERT_STRING(db_owner_info, "email", owner->email);
3200 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3202 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3203 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3205 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3206 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3208 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3210 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3211 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3212 owner->dbOpenAddressing);
3214 leave:
3215 free(string);
3216 return err;
3220 /* Convert XSD:tDbUserInfo XML tree into structure
3221 * @context is ISDS context
3222 * @db_user_info is automatically reallocated user info structure
3223 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3224 * In case of error @db_user_info will be freed. */
3225 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3226 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3227 isds_error err = IE_SUCCESS;
3228 xmlXPathObjectPtr result = NULL;
3229 char *string = NULL;
3231 if (!context) return IE_INVALID_CONTEXT;
3232 if (!db_user_info) return IE_INVAL;
3233 isds_DbUserInfo_free(db_user_info);
3234 if (!xpath_ctx) return IE_INVAL;
3237 *db_user_info = calloc(1, sizeof(**db_user_info));
3238 if (!*db_user_info) {
3239 err = IE_NOMEM;
3240 goto leave;
3243 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3245 EXTRACT_STRING("isds:userType", string);
3246 if (string) {
3247 (*db_user_info)->userType =
3248 calloc(1, sizeof(*((*db_user_info)->userType)));
3249 if (!(*db_user_info)->userType) {
3250 err = IE_NOMEM;
3251 goto leave;
3253 err = string2isds_UserType((xmlChar *)string,
3254 (*db_user_info)->userType);
3255 if (err) {
3256 zfree((*db_user_info)->userType);
3257 if (err == IE_ENUM) {
3258 err = IE_ISDS;
3259 char *string_locale = _isds_utf82locale(string);
3260 isds_printf_message(context,
3261 _("Unknown isds:userType value: %s"), string_locale);
3262 free(string_locale);
3264 goto leave;
3266 zfree(string);
3269 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3271 (*db_user_info)->personName =
3272 calloc(1, sizeof(*((*db_user_info)->personName)));
3273 if (!(*db_user_info)->personName) {
3274 err = IE_NOMEM;
3275 goto leave;
3278 err = extract_gPersonName(context, &(*db_user_info)->personName,
3279 xpath_ctx);
3280 if (err) goto leave;
3282 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3283 if (err) goto leave;
3285 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3286 if (err) goto leave;
3288 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3289 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3291 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3292 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3293 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3295 /* ???: Default value is "CZ" according specification. Should we provide
3296 * it? */
3297 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3299 leave:
3300 if (err) isds_DbUserInfo_free(db_user_info);
3301 free(string);
3302 xmlXPathFreeObject(result);
3303 return err;
3307 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3308 * @context is session context
3309 * @user is libisds structure with user description
3310 * @db_user_info is XML element of XSD:tDbUserInfo */
3311 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3312 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3314 isds_error err = IE_SUCCESS;
3315 xmlNodePtr node;
3316 xmlChar *string = NULL;
3318 if (!context) return IE_INVALID_CONTEXT;
3319 if (!user || !db_user_info) return IE_INVAL;
3321 /* Build XSD:tDbUserInfo */
3322 if (user->personName) {
3323 INSERT_STRING(db_user_info, "pnFirstName",
3324 user->personName->pnFirstName);
3325 INSERT_STRING(db_user_info, "pnMiddleName",
3326 user->personName->pnMiddleName);
3327 INSERT_STRING(db_user_info, "pnLastName",
3328 user->personName->pnLastName);
3329 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3330 user->personName->pnLastNameAtBirth);
3332 if (user->address) {
3333 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3334 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3335 INSERT_STRING(db_user_info, "adNumberInStreet",
3336 user->address->adNumberInStreet);
3337 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3338 user->address->adNumberInMunicipality);
3339 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3340 INSERT_STRING(db_user_info, "adState", user->address->adState);
3342 if (user->biDate) {
3343 if (!tm2datestring(user->biDate, &string))
3344 INSERT_STRING(db_user_info, "biDate", string);
3345 zfree(string);
3347 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3348 INSERT_STRING(db_user_info, "userID", user->userID);
3350 /* userType */
3351 if (user->userType) {
3352 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3353 if (!type_string) {
3354 isds_printf_message(context, _("Invalid userType value: %d"),
3355 *(user->userType));
3356 err = IE_ENUM;
3357 goto leave;
3359 INSERT_STRING(db_user_info, "userType", type_string);
3362 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3363 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3364 INSERT_STRING(db_user_info, "ic", user->ic);
3365 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3366 INSERT_STRING(db_user_info, "firmName", user->firmName);
3367 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3368 INSERT_STRING(db_user_info, "caCity", user->caCity);
3369 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3370 INSERT_STRING(db_user_info, "caState", user->caState);
3372 leave:
3373 free(string);
3374 return err;
3378 /* Convert XSD:tPDZRec XML tree into structure
3379 * @context is ISDS context
3380 * @permission is automatically reallocated commercial permission structure
3381 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3382 * In case of error @permission will be freed. */
3383 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3384 struct isds_commercial_permission **permission,
3385 xmlXPathContextPtr xpath_ctx) {
3386 isds_error err = IE_SUCCESS;
3387 xmlXPathObjectPtr result = NULL;
3388 char *string = NULL;
3390 if (!context) return IE_INVALID_CONTEXT;
3391 if (!permission) return IE_INVAL;
3392 isds_commercial_permission_free(permission);
3393 if (!xpath_ctx) return IE_INVAL;
3396 *permission = calloc(1, sizeof(**permission));
3397 if (!*permission) {
3398 err = IE_NOMEM;
3399 goto leave;
3402 EXTRACT_STRING("isds:PDZType", string);
3403 if (string) {
3404 err = string2isds_payment_type((xmlChar *)string,
3405 &(*permission)->type);
3406 if (err) {
3407 if (err == IE_ENUM) {
3408 err = IE_ISDS;
3409 char *string_locale = _isds_utf82locale(string);
3410 isds_printf_message(context,
3411 _("Unknown isds:PDZType value: %s"), string_locale);
3412 free(string_locale);
3414 goto leave;
3416 zfree(string);
3419 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3420 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3422 EXTRACT_STRING("isds:PDZExpire", string);
3423 if (string) {
3424 err = timestring2timeval((xmlChar *) string,
3425 &((*permission)->expiration));
3426 if (err) {
3427 char *string_locale = _isds_utf82locale(string);
3428 if (err == IE_DATE) err = IE_ISDS;
3429 isds_printf_message(context,
3430 _("Could not convert PDZExpire as ISO time: %s"),
3431 string_locale);
3432 free(string_locale);
3433 goto leave;
3435 zfree(string);
3438 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3439 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3441 leave:
3442 if (err) isds_commercial_permission_free(permission);
3443 free(string);
3444 xmlXPathFreeObject(result);
3445 return err;
3449 /* Convert XSD:tCiRecord XML tree into structure
3450 * @context is ISDS context
3451 * @event is automatically reallocated commercial credit event structure
3452 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3453 * In case of error @event will be freed. */
3454 static isds_error extract_CiRecord(struct isds_ctx *context,
3455 struct isds_credit_event **event,
3456 xmlXPathContextPtr xpath_ctx) {
3457 isds_error err = IE_SUCCESS;
3458 xmlXPathObjectPtr result = NULL;
3459 char *string = NULL;
3460 long int *number_ptr;
3462 if (!context) return IE_INVALID_CONTEXT;
3463 if (!event) return IE_INVAL;
3464 isds_credit_event_free(event);
3465 if (!xpath_ctx) return IE_INVAL;
3468 *event = calloc(1, sizeof(**event));
3469 if (!*event) {
3470 err = IE_NOMEM;
3471 goto leave;
3474 EXTRACT_STRING("isds:ciEventTime", string);
3475 if (string) {
3476 err = timestring2timeval((xmlChar *) string,
3477 &(*event)->time);
3478 if (err) {
3479 char *string_locale = _isds_utf82locale(string);
3480 if (err == IE_DATE) err = IE_ISDS;
3481 isds_printf_message(context,
3482 _("Could not convert ciEventTime as ISO time: %s"),
3483 string_locale);
3484 free(string_locale);
3485 goto leave;
3487 zfree(string);
3490 EXTRACT_STRING("isds:ciEventType", string);
3491 if (string) {
3492 err = string2isds_credit_event_type((xmlChar *)string,
3493 &(*event)->type);
3494 if (err) {
3495 if (err == IE_ENUM) {
3496 err = IE_ISDS;
3497 char *string_locale = _isds_utf82locale(string);
3498 isds_printf_message(context,
3499 _("Unknown isds:ciEventType value: %s"), string_locale);
3500 free(string_locale);
3502 goto leave;
3504 zfree(string);
3507 number_ptr = &((*event)->credit_change);
3508 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3509 number_ptr = &(*event)->new_credit;
3510 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3512 switch((*event)->type) {
3513 case ISDS_CREDIT_CHARGED:
3514 EXTRACT_STRING("isds:ciTransID",
3515 (*event)->details.charged.transaction);
3516 break;
3517 case ISDS_CREDIT_DISCHARGED:
3518 EXTRACT_STRING("isds:ciTransID",
3519 (*event)->details.discharged.transaction);
3520 break;
3521 case ISDS_CREDIT_MESSAGE_SENT:
3522 EXTRACT_STRING("isds:ciRecipientID",
3523 (*event)->details.message_sent.recipient);
3524 EXTRACT_STRING("isds:ciPDZID",
3525 (*event)->details.message_sent.message_id);
3526 break;
3527 case ISDS_CREDIT_STORAGE_SET:
3528 number_ptr = &((*event)->details.storage_set.new_capacity);
3529 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3530 EXTRACT_DATE("isds:ciNewFrom",
3531 (*event)->details.storage_set.new_valid_from);
3532 EXTRACT_DATE("isds:ciNewTo",
3533 (*event)->details.storage_set.new_valid_to);
3534 EXTRACT_LONGINT("isds:ciOldCapacity",
3535 (*event)->details.storage_set.old_capacity, 0);
3536 EXTRACT_DATE("isds:ciOldFrom",
3537 (*event)->details.storage_set.old_valid_from);
3538 EXTRACT_DATE("isds:ciOldTo",
3539 (*event)->details.storage_set.old_valid_to);
3540 EXTRACT_STRING("isds:ciDoneBy",
3541 (*event)->details.storage_set.initiator);
3542 break;
3543 case ISDS_CREDIT_EXPIRED:
3544 break;
3547 leave:
3548 if (err) isds_credit_event_free(event);
3549 free(string);
3550 xmlXPathFreeObject(result);
3551 return err;
3555 #endif /* HAVE_LIBCURL */
3558 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3559 * isds_envelope structure. The envelope is automatically allocated but not
3560 * reallocated. The date are just appended into envelope structure.
3561 * @context is ISDS context
3562 * @envelope is automatically allocated message envelope structure
3563 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3564 * In case of error @envelope will be freed. */
3565 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3566 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3567 isds_error err = IE_SUCCESS;
3568 xmlXPathObjectPtr result = NULL;
3570 if (!context) return IE_INVALID_CONTEXT;
3571 if (!envelope) return IE_INVAL;
3572 if (!xpath_ctx) return IE_INVAL;
3575 if (!*envelope) {
3576 /* Allocate envelope */
3577 *envelope = calloc(1, sizeof(**envelope));
3578 if (!*envelope) {
3579 err = IE_NOMEM;
3580 goto leave;
3582 } else {
3583 /* Else free former data */
3584 zfree((*envelope)->dmSenderOrgUnit);
3585 zfree((*envelope)->dmSenderOrgUnitNum);
3586 zfree((*envelope)->dbIDRecipient);
3587 zfree((*envelope)->dmRecipientOrgUnit);
3588 zfree((*envelope)->dmRecipientOrgUnitNum);
3589 zfree((*envelope)->dmToHands);
3590 zfree((*envelope)->dmAnnotation);
3591 zfree((*envelope)->dmRecipientRefNumber);
3592 zfree((*envelope)->dmSenderRefNumber);
3593 zfree((*envelope)->dmRecipientIdent);
3594 zfree((*envelope)->dmSenderIdent);
3595 zfree((*envelope)->dmLegalTitleLaw);
3596 zfree((*envelope)->dmLegalTitleYear);
3597 zfree((*envelope)->dmLegalTitleSect);
3598 zfree((*envelope)->dmLegalTitlePar);
3599 zfree((*envelope)->dmLegalTitlePoint);
3600 zfree((*envelope)->dmPersonalDelivery);
3601 zfree((*envelope)->dmAllowSubstDelivery);
3604 /* Extract envelope elements added by sender or ISDS
3605 * (XSD: gMessageEnvelopeSub type) */
3606 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3607 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3608 (*envelope)->dmSenderOrgUnitNum, 0);
3609 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3610 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3611 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3612 (*envelope)->dmRecipientOrgUnitNum, 0);
3613 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3614 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3615 EXTRACT_STRING("isds:dmRecipientRefNumber",
3616 (*envelope)->dmRecipientRefNumber);
3617 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3618 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3619 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3621 /* Extract envelope elements regarding law reference */
3622 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3623 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3624 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3625 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3626 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3628 /* Extract envelope other elements */
3629 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3630 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3631 (*envelope)->dmAllowSubstDelivery);
3633 leave:
3634 if (err) isds_envelope_free(envelope);
3635 xmlXPathFreeObject(result);
3636 return err;
3641 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3642 * isds_envelope structure. The envelope is automatically allocated but not
3643 * reallocated. The date are just appended into envelope structure.
3644 * @context is ISDS context
3645 * @envelope is automatically allocated message envelope structure
3646 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3647 * In case of error @envelope will be freed. */
3648 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3649 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3650 isds_error err = IE_SUCCESS;
3651 xmlXPathObjectPtr result = NULL;
3653 if (!context) return IE_INVALID_CONTEXT;
3654 if (!envelope) return IE_INVAL;
3655 if (!xpath_ctx) return IE_INVAL;
3658 if (!*envelope) {
3659 /* Allocate envelope */
3660 *envelope = calloc(1, sizeof(**envelope));
3661 if (!*envelope) {
3662 err = IE_NOMEM;
3663 goto leave;
3665 } else {
3666 /* Else free former data */
3667 zfree((*envelope)->dmID);
3668 zfree((*envelope)->dbIDSender);
3669 zfree((*envelope)->dmSender);
3670 zfree((*envelope)->dmSenderAddress);
3671 zfree((*envelope)->dmSenderType);
3672 zfree((*envelope)->dmRecipient);
3673 zfree((*envelope)->dmRecipientAddress);
3674 zfree((*envelope)->dmAmbiguousRecipient);
3677 /* Extract envelope elements added by ISDS
3678 * (XSD: gMessageEnvelope type) */
3679 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3680 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3681 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3682 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3683 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3684 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3685 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3686 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3687 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3688 (*envelope)->dmAmbiguousRecipient);
3690 /* Extract envelope elements added by sender and ISDS
3691 * (XSD: gMessageEnvelope type) */
3692 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3693 if (err) goto leave;
3695 leave:
3696 if (err) isds_envelope_free(envelope);
3697 xmlXPathFreeObject(result);
3698 return err;
3702 /* Convert other envelope elements from XML tree into isds_envelope structure:
3703 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3704 * The envelope is automatically allocated but not reallocated.
3705 * The data are just appended into envelope structure.
3706 * @context is ISDS context
3707 * @envelope is automatically allocated message envelope structure
3708 * @xpath_ctx is XPath context with current node as parent desired elements
3709 * In case of error @envelope will be freed. */
3710 static isds_error append_status_size_times(struct isds_ctx *context,
3711 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3712 isds_error err = IE_SUCCESS;
3713 xmlXPathObjectPtr result = NULL;
3714 char *string = NULL;
3715 unsigned long int *unumber = NULL;
3717 if (!context) return IE_INVALID_CONTEXT;
3718 if (!envelope) return IE_INVAL;
3719 if (!xpath_ctx) return IE_INVAL;
3722 if (!*envelope) {
3723 /* Allocate new */
3724 *envelope = calloc(1, sizeof(**envelope));
3725 if (!*envelope) {
3726 err = IE_NOMEM;
3727 goto leave;
3729 } else {
3730 /* Free old data */
3731 zfree((*envelope)->dmMessageStatus);
3732 zfree((*envelope)->dmAttachmentSize);
3733 zfree((*envelope)->dmDeliveryTime);
3734 zfree((*envelope)->dmAcceptanceTime);
3738 /* dmMessageStatus element is mandatory */
3739 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3740 if (!unumber) {
3741 isds_log_message(context,
3742 _("Missing mandatory sisds:dmMessageStatus integer"));
3743 err = IE_ISDS;
3744 goto leave;
3746 err = uint2isds_message_status(context, unumber,
3747 &((*envelope)->dmMessageStatus));
3748 if (err) {
3749 if (err == IE_ENUM) err = IE_ISDS;
3750 goto leave;
3752 free(unumber); unumber = NULL;
3754 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3757 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3758 if (string) {
3759 err = timestring2timeval((xmlChar *) string,
3760 &((*envelope)->dmDeliveryTime));
3761 if (err) {
3762 char *string_locale = _isds_utf82locale(string);
3763 if (err == IE_DATE) err = IE_ISDS;
3764 isds_printf_message(context,
3765 _("Could not convert dmDeliveryTime as ISO time: %s"),
3766 string_locale);
3767 free(string_locale);
3768 goto leave;
3770 zfree(string);
3773 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3774 if (string) {
3775 err = timestring2timeval((xmlChar *) string,
3776 &((*envelope)->dmAcceptanceTime));
3777 if (err) {
3778 char *string_locale = _isds_utf82locale(string);
3779 if (err == IE_DATE) err = IE_ISDS;
3780 isds_printf_message(context,
3781 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3782 string_locale);
3783 free(string_locale);
3784 goto leave;
3786 zfree(string);
3789 leave:
3790 if (err) isds_envelope_free(envelope);
3791 free(unumber);
3792 free(string);
3793 xmlXPathFreeObject(result);
3794 return err;
3798 /* Convert message type attribute of current element into isds_envelope
3799 * structure.
3800 * TODO: This function can be incorporated into append_status_size_times() as
3801 * they are called always together.
3802 * The envelope is automatically allocated but not reallocated.
3803 * The data are just appended into envelope structure.
3804 * @context is ISDS context
3805 * @envelope is automatically allocated message envelope structure
3806 * @xpath_ctx is XPath context with current node as parent of attribute
3807 * carrying message type
3808 * In case of error @envelope will be freed. */
3809 static isds_error append_message_type(struct isds_ctx *context,
3810 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3811 isds_error err = IE_SUCCESS;
3813 if (!context) return IE_INVALID_CONTEXT;
3814 if (!envelope) return IE_INVAL;
3815 if (!xpath_ctx) return IE_INVAL;
3818 if (!*envelope) {
3819 /* Allocate new */
3820 *envelope = calloc(1, sizeof(**envelope));
3821 if (!*envelope) {
3822 err = IE_NOMEM;
3823 goto leave;
3825 } else {
3826 /* Free old data */
3827 zfree((*envelope)->dmType);
3831 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3833 if (!(*envelope)->dmType) {
3834 /* Use default value */
3835 (*envelope)->dmType = strdup("V");
3836 if (!(*envelope)->dmType) {
3837 err = IE_NOMEM;
3838 goto leave;
3840 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3841 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3842 isds_printf_message(context,
3843 _("Message type in dmType attribute is not 1 character long: "
3844 "%s"),
3845 type_locale);
3846 free(type_locale);
3847 err = IE_ISDS;
3848 goto leave;
3851 leave:
3852 if (err) isds_envelope_free(envelope);
3853 return err;
3857 #if HAVE_LIBCURL
3858 /* Convert dmType isds_envelope member into XML attribute and append it to
3859 * current node.
3860 * @context is ISDS context
3861 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3862 * @dm_envelope is XML element the resulting attribute will be appended to.
3863 * @return error code, in case of error context' message is filled. */
3864 static isds_error insert_message_type(struct isds_ctx *context,
3865 const char *type, xmlNodePtr dm_envelope) {
3866 isds_error err = IE_SUCCESS;
3867 xmlAttrPtr attribute_node;
3869 if (!context) return IE_INVALID_CONTEXT;
3870 if (!dm_envelope) return IE_INVAL;
3872 /* Insert optional message type */
3873 if (type) {
3874 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3875 char *type_locale = _isds_utf82locale(type);
3876 isds_printf_message(context,
3877 _("Message type in envelope is not 1 character long: %s"),
3878 type_locale);
3879 free(type_locale);
3880 err = IE_INVAL;
3881 goto leave;
3883 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3886 leave:
3887 return err;
3889 #endif /* HAVE_LIBCURL */
3892 /* Extract message document into reallocated document structure
3893 * @context is ISDS context
3894 * @document is automatically reallocated message documents structure
3895 * @xpath_ctx is XPath context with current node as isds:dmFile
3896 * In case of error @document will be freed. */
3897 static isds_error extract_document(struct isds_ctx *context,
3898 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3899 isds_error err = IE_SUCCESS;
3900 xmlXPathObjectPtr result = NULL;
3901 xmlNodePtr file_node;
3902 char *string = NULL;
3904 if (!context) return IE_INVALID_CONTEXT;
3905 if (!document) return IE_INVAL;
3906 isds_document_free(document);
3907 if (!xpath_ctx) return IE_INVAL;
3908 file_node = xpath_ctx->node;
3910 *document = calloc(1, sizeof(**document));
3911 if (!*document) {
3912 err = IE_NOMEM;
3913 goto leave;
3916 /* Extract document meta data */
3917 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3918 if (context->normalize_mime_type) {
3919 const char *normalized_type =
3920 isds_normalize_mime_type((*document)->dmMimeType);
3921 if (NULL != normalized_type &&
3922 normalized_type != (*document)->dmMimeType) {
3923 char *new_type = strdup(normalized_type);
3924 if (NULL == new_type) {
3925 isds_printf_message(context,
3926 _("Not enough memory to normalize document MIME type"));
3927 err = IE_NOMEM;
3928 goto leave;
3930 free((*document)->dmMimeType);
3931 (*document)->dmMimeType = new_type;
3935 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3936 err = string2isds_FileMetaType((xmlChar*)string,
3937 &((*document)->dmFileMetaType));
3938 if (err) {
3939 char *meta_type_locale = _isds_utf82locale(string);
3940 isds_printf_message(context,
3941 _("Document has invalid dmFileMetaType attribute value: %s"),
3942 meta_type_locale);
3943 free(meta_type_locale);
3944 err = IE_ISDS;
3945 goto leave;
3947 zfree(string);
3949 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3950 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3951 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3952 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3955 /* Extract document data.
3956 * Base64 encoded blob or XML subtree must be presented. */
3958 /* Check for dmEncodedContent */
3959 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3960 xpath_ctx);
3961 if (!result) {
3962 err = IE_XML;
3963 goto leave;
3966 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3967 /* Here we have Base64 blob */
3968 (*document)->is_xml = 0;
3970 if (result->nodesetval->nodeNr > 1) {
3971 isds_printf_message(context,
3972 _("Document has more dmEncodedContent elements"));
3973 err = IE_ISDS;
3974 goto leave;
3977 xmlXPathFreeObject(result); result = NULL;
3978 EXTRACT_STRING("isds:dmEncodedContent", string);
3980 /* Decode non-empty document */
3981 if (string && string[0] != '\0') {
3982 (*document)->data_length =
3983 _isds_b64decode(string, &((*document)->data));
3984 if ((*document)->data_length == (size_t) -1) {
3985 isds_printf_message(context,
3986 _("Error while Base64-decoding document content"));
3987 err = IE_ERROR;
3988 goto leave;
3991 } else {
3992 /* No Base64 blob, try XML document */
3993 xmlXPathFreeObject(result); result = NULL;
3994 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3995 xpath_ctx);
3996 if (!result) {
3997 err = IE_XML;
3998 goto leave;
4001 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4002 /* Here we have XML document */
4003 (*document)->is_xml = 1;
4005 if (result->nodesetval->nodeNr > 1) {
4006 isds_printf_message(context,
4007 _("Document has more dmXMLContent elements"));
4008 err = IE_ISDS;
4009 goto leave;
4012 /* XXX: We cannot serialize the content simply because:
4013 * - XML document may point out of its scope (e.g. to message
4014 * envelope)
4015 * - isds:dmXMLContent can contain more elements, no element,
4016 * a text node only
4017 * - it's not the XML way
4018 * Thus we provide the only right solution: XML DOM. Let's
4019 * application to cope with this hot potato :) */
4020 (*document)->xml_node_list =
4021 result->nodesetval->nodeTab[0]->children;
4022 } else {
4023 /* No base64 blob, nor XML document */
4024 isds_printf_message(context,
4025 _("Document has no dmEncodedContent, nor dmXMLContent "
4026 "element"));
4027 err = IE_ISDS;
4028 goto leave;
4033 leave:
4034 if (err) isds_document_free(document);
4035 free(string);
4036 xmlXPathFreeObject(result);
4037 xpath_ctx->node = file_node;
4038 return err;
4043 /* Extract message documents into reallocated list of documents
4044 * @context is ISDS context
4045 * @documents is automatically reallocated message documents list structure
4046 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4047 * In case of error @documents will be freed. */
4048 static isds_error extract_documents(struct isds_ctx *context,
4049 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
4050 isds_error err = IE_SUCCESS;
4051 xmlXPathObjectPtr result = NULL;
4052 xmlNodePtr files_node;
4053 struct isds_list *document, *prev_document = NULL;
4055 if (!context) return IE_INVALID_CONTEXT;
4056 if (!documents) return IE_INVAL;
4057 isds_list_free(documents);
4058 if (!xpath_ctx) return IE_INVAL;
4059 files_node = xpath_ctx->node;
4061 /* Find documents */
4062 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
4063 if (!result) {
4064 err = IE_XML;
4065 goto leave;
4068 /* No match */
4069 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4070 isds_printf_message(context,
4071 _("Message does not contain any document"));
4072 err = IE_ISDS;
4073 goto leave;
4077 /* Iterate over documents */
4078 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4080 /* Allocate and append list item */
4081 document = calloc(1, sizeof(*document));
4082 if (!document) {
4083 err = IE_NOMEM;
4084 goto leave;
4086 document->destructor = (void (*)(void **))isds_document_free;
4087 if (i == 0) *documents = document;
4088 else prev_document->next = document;
4089 prev_document = document;
4091 /* Extract document */
4092 xpath_ctx->node = result->nodesetval->nodeTab[i];
4093 err = extract_document(context,
4094 (struct isds_document **) &(document->data), xpath_ctx);
4095 if (err) goto leave;
4099 leave:
4100 if (err) isds_list_free(documents);
4101 xmlXPathFreeObject(result);
4102 xpath_ctx->node = files_node;
4103 return err;
4107 #if HAVE_LIBCURL
4108 /* Convert isds:dmRecord XML tree into structure
4109 * @context is ISDS context
4110 * @envelope is automatically reallocated message envelope structure
4111 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4112 * In case of error @envelope will be freed. */
4113 static isds_error extract_DmRecord(struct isds_ctx *context,
4114 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4115 isds_error err = IE_SUCCESS;
4116 xmlXPathObjectPtr result = NULL;
4118 if (!context) return IE_INVALID_CONTEXT;
4119 if (!envelope) return IE_INVAL;
4120 isds_envelope_free(envelope);
4121 if (!xpath_ctx) return IE_INVAL;
4124 *envelope = calloc(1, sizeof(**envelope));
4125 if (!*envelope) {
4126 err = IE_NOMEM;
4127 goto leave;
4131 /* Extract tRecord data */
4132 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4134 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4135 * dmAcceptanceTime. */
4136 err = append_status_size_times(context, envelope, xpath_ctx);
4137 if (err) goto leave;
4139 /* Extract envelope elements added by sender and ISDS
4140 * (XSD: gMessageEnvelope type) */
4141 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4142 if (err) goto leave;
4144 /* Get message type */
4145 err = append_message_type(context, envelope, xpath_ctx);
4146 if (err) goto leave;
4149 leave:
4150 if (err) isds_envelope_free(envelope);
4151 xmlXPathFreeObject(result);
4152 return err;
4156 /* Convert XSD:tStateChangesRecord type XML tree into structure
4157 * @context is ISDS context
4158 * @changed_status is automatically reallocated message state change structure
4159 * @xpath_ctx is XPath context with current node as element of
4160 * XSD:tStateChangesRecord type
4161 * In case of error @changed_status will be freed. */
4162 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4163 struct isds_message_status_change **changed_status,
4164 xmlXPathContextPtr xpath_ctx) {
4165 isds_error err = IE_SUCCESS;
4166 xmlXPathObjectPtr result = NULL;
4167 unsigned long int *unumber = NULL;
4168 char *string = NULL;
4170 if (!context) return IE_INVALID_CONTEXT;
4171 if (!changed_status) return IE_INVAL;
4172 isds_message_status_change_free(changed_status);
4173 if (!xpath_ctx) return IE_INVAL;
4176 *changed_status = calloc(1, sizeof(**changed_status));
4177 if (!*changed_status) {
4178 err = IE_NOMEM;
4179 goto leave;
4183 /* Extract tGetStateChangesInput data */
4184 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4186 /* dmEventTime is mandatory */
4187 EXTRACT_STRING("isds:dmEventTime", string);
4188 if (string) {
4189 err = timestring2timeval((xmlChar *) string,
4190 &((*changed_status)->time));
4191 if (err) {
4192 char *string_locale = _isds_utf82locale(string);
4193 if (err == IE_DATE) err = IE_ISDS;
4194 isds_printf_message(context,
4195 _("Could not convert dmEventTime as ISO time: %s"),
4196 string_locale);
4197 free(string_locale);
4198 goto leave;
4200 zfree(string);
4203 /* dmMessageStatus element is mandatory */
4204 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4205 if (!unumber) {
4206 isds_log_message(context,
4207 _("Missing mandatory isds:dmMessageStatus integer"));
4208 err = IE_ISDS;
4209 goto leave;
4211 err = uint2isds_message_status(context, unumber,
4212 &((*changed_status)->dmMessageStatus));
4213 if (err) {
4214 if (err == IE_ENUM) err = IE_ISDS;
4215 goto leave;
4217 zfree(unumber);
4220 leave:
4221 free(unumber);
4222 free(string);
4223 if (err) isds_message_status_change_free(changed_status);
4224 xmlXPathFreeObject(result);
4225 return err;
4227 #endif /* HAVE_LIBCURL */
4230 /* Find and convert isds:dmHash XML tree into structure
4231 * @context is ISDS context
4232 * @envelope is automatically reallocated message hash structure
4233 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4234 * In case of error @hash will be freed. */
4235 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4236 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4237 isds_error err = IE_SUCCESS;
4238 xmlNodePtr old_ctx_node;
4239 xmlXPathObjectPtr result = NULL;
4240 char *string = NULL;
4242 if (!context) return IE_INVALID_CONTEXT;
4243 if (!hash) return IE_INVAL;
4244 isds_hash_free(hash);
4245 if (!xpath_ctx) return IE_INVAL;
4247 old_ctx_node = xpath_ctx->node;
4249 *hash = calloc(1, sizeof(**hash));
4250 if (!*hash) {
4251 err = IE_NOMEM;
4252 goto leave;
4255 /* Locate dmHash */
4256 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4257 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4258 err = IE_ISDS;
4259 goto leave;
4261 if (err) {
4262 err = IE_ERROR;
4263 goto leave;
4266 /* Get hash algorithm */
4267 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4268 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4269 if (err) {
4270 if (err == IE_ENUM) {
4271 char *string_locale = _isds_utf82locale(string);
4272 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4273 string_locale);
4274 free(string_locale);
4276 goto leave;
4278 zfree(string);
4280 /* Get hash value */
4281 EXTRACT_STRING(".", string);
4282 if (!string) {
4283 isds_printf_message(context,
4284 _("sisds:dmHash element is missing hash value"));
4285 err = IE_ISDS;
4286 goto leave;
4288 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4289 if ((*hash)->length == (size_t) -1) {
4290 isds_printf_message(context,
4291 _("Error while Base64-decoding hash value"));
4292 err = IE_ERROR;
4293 goto leave;
4296 leave:
4297 if (err) isds_hash_free(hash);
4298 free(string);
4299 xmlXPathFreeObject(result);
4300 xpath_ctx->node = old_ctx_node;
4301 return err;
4305 /* Find and append isds:dmQTimestamp XML tree into envelope.
4306 * Because one service is allowed to miss time-stamp content, and we think
4307 * other could too (flaw in specification), this function is deliberated and
4308 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4309 * @context is ISDS context
4310 * @envelope is automatically allocated envelope structure
4311 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4312 * child
4313 * In case of error @envelope will be freed. */
4314 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4315 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4316 isds_error err = IE_SUCCESS;
4317 xmlXPathObjectPtr result = NULL;
4318 char *string = NULL;
4320 if (!context) return IE_INVALID_CONTEXT;
4321 if (!envelope) return IE_INVAL;
4322 if (!xpath_ctx) {
4323 isds_envelope_free(envelope);
4324 return IE_INVAL;
4327 if (!*envelope) {
4328 *envelope = calloc(1, sizeof(**envelope));
4329 if (!*envelope) {
4330 err = IE_NOMEM;
4331 goto leave;
4333 } else {
4334 zfree((*envelope)->timestamp);
4335 (*envelope)->timestamp_length = 0;
4338 /* Get dmQTimestamp */
4339 EXTRACT_STRING("sisds:dmQTimestamp", string);
4340 if (!string) {
4341 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4342 goto leave;
4344 (*envelope)->timestamp_length =
4345 _isds_b64decode(string, &((*envelope)->timestamp));
4346 if ((*envelope)->timestamp_length == (size_t) -1) {
4347 isds_printf_message(context,
4348 _("Error while Base64-decoding time stamp value"));
4349 err = IE_ERROR;
4350 goto leave;
4353 leave:
4354 if (err) isds_envelope_free(envelope);
4355 free(string);
4356 xmlXPathFreeObject(result);
4357 return err;
4361 /* Convert XSD tReturnedMessage XML tree into message structure.
4362 * It does not store serialized XML tree into message->raw.
4363 * It does store (pointer to) parsed XML tree into message->xml if needed.
4364 * @context is ISDS context
4365 * @include_documents Use true if documents must be extracted
4366 * (tReturnedMessage XSD type), use false if documents shall be omitted
4367 * (tReturnedMessageEnvelope).
4368 * @message is automatically reallocated message structure
4369 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4370 * type
4371 * In case of error @message will be freed. */
4372 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4373 const _Bool include_documents, struct isds_message **message,
4374 xmlXPathContextPtr xpath_ctx) {
4375 isds_error err = IE_SUCCESS;
4376 xmlNodePtr message_node;
4378 if (!context) return IE_INVALID_CONTEXT;
4379 if (!message) return IE_INVAL;
4380 isds_message_free(message);
4381 if (!xpath_ctx) return IE_INVAL;
4384 *message = calloc(1, sizeof(**message));
4385 if (!*message) {
4386 err = IE_NOMEM;
4387 goto leave;
4390 /* Save message XPATH context node */
4391 message_node = xpath_ctx->node;
4394 /* Extract dmDM */
4395 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4396 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4397 if (err) { err = IE_ERROR; goto leave; }
4398 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4399 if (err) goto leave;
4401 if (include_documents) {
4402 struct isds_list *item;
4404 /* Extract dmFiles */
4405 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4406 xpath_ctx);
4407 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4408 err = IE_ISDS; goto leave;
4410 if (err) { err = IE_ERROR; goto leave; }
4411 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4412 if (err) goto leave;
4414 /* Store xmlDoc of this message if needed */
4415 /* Only if we got a XML document in all the documents. */
4416 for (item = (*message)->documents; item; item = item->next) {
4417 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4418 (*message)->xml = xpath_ctx->doc;
4419 break;
4425 /* Restore context to message */
4426 xpath_ctx->node = message_node;
4428 /* Extract dmHash */
4429 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4430 xpath_ctx);
4431 if (err) goto leave;
4433 /* Extract dmQTimestamp, */
4434 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4435 xpath_ctx);
4436 if (err) goto leave;
4438 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4439 * dmAcceptanceTime. */
4440 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4441 if (err) goto leave;
4443 /* Get message type */
4444 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4445 if (err) goto leave;
4447 leave:
4448 if (err) isds_message_free(message);
4449 return err;
4453 /* Extract message event into reallocated isds_event structure
4454 * @context is ISDS context
4455 * @event is automatically reallocated message event structure
4456 * @xpath_ctx is XPath context with current node as isds:dmEvent
4457 * In case of error @event will be freed. */
4458 static isds_error extract_event(struct isds_ctx *context,
4459 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4460 isds_error err = IE_SUCCESS;
4461 xmlXPathObjectPtr result = NULL;
4462 xmlNodePtr event_node;
4463 char *string = NULL;
4465 if (!context) return IE_INVALID_CONTEXT;
4466 if (!event) return IE_INVAL;
4467 isds_event_free(event);
4468 if (!xpath_ctx) return IE_INVAL;
4469 event_node = xpath_ctx->node;
4471 *event = calloc(1, sizeof(**event));
4472 if (!*event) {
4473 err = IE_NOMEM;
4474 goto leave;
4477 /* Extract event data.
4478 * All elements are optional according XSD. That's funny. */
4479 EXTRACT_STRING("sisds:dmEventTime", string);
4480 if (string) {
4481 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4482 if (err) {
4483 char *string_locale = _isds_utf82locale(string);
4484 if (err == IE_DATE) err = IE_ISDS;
4485 isds_printf_message(context,
4486 _("Could not convert dmEventTime as ISO time: %s"),
4487 string_locale);
4488 free(string_locale);
4489 goto leave;
4491 zfree(string);
4494 /* dmEventDescr element has prefix and the rest */
4495 EXTRACT_STRING("sisds:dmEventDescr", string);
4496 if (string) {
4497 err = eventstring2event((xmlChar *) string, *event);
4498 if (err) goto leave;
4499 zfree(string);
4502 leave:
4503 if (err) isds_event_free(event);
4504 free(string);
4505 xmlXPathFreeObject(result);
4506 xpath_ctx->node = event_node;
4507 return err;
4511 /* Convert element of XSD tEventsArray type from XML tree into
4512 * isds_list of isds_event's structure. The list is automatically reallocated.
4513 * @context is ISDS context
4514 * @events is automatically reallocated list of event structures
4515 * @xpath_ctx is XPath context with current node as tEventsArray
4516 * In case of error @events will be freed. */
4517 static isds_error extract_events(struct isds_ctx *context,
4518 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4519 isds_error err = IE_SUCCESS;
4520 xmlXPathObjectPtr result = NULL;
4521 xmlNodePtr events_node;
4522 struct isds_list *event, *prev_event = NULL;
4524 if (!context) return IE_INVALID_CONTEXT;
4525 if (!events) return IE_INVAL;
4526 if (!xpath_ctx) return IE_INVAL;
4527 events_node = xpath_ctx->node;
4529 /* Free old list */
4530 isds_list_free(events);
4532 /* Find events */
4533 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4534 if (!result) {
4535 err = IE_XML;
4536 goto leave;
4539 /* No match */
4540 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4541 isds_printf_message(context,
4542 _("Delivery info does not contain any event"));
4543 err = IE_ISDS;
4544 goto leave;
4548 /* Iterate over events */
4549 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4551 /* Allocate and append list item */
4552 event = calloc(1, sizeof(*event));
4553 if (!event) {
4554 err = IE_NOMEM;
4555 goto leave;
4557 event->destructor = (void (*)(void **))isds_event_free;
4558 if (i == 0) *events = event;
4559 else prev_event->next = event;
4560 prev_event = event;
4562 /* Extract event */
4563 xpath_ctx->node = result->nodesetval->nodeTab[i];
4564 err = extract_event(context,
4565 (struct isds_event **) &(event->data), xpath_ctx);
4566 if (err) goto leave;
4570 leave:
4571 if (err) isds_list_free(events);
4572 xmlXPathFreeObject(result);
4573 xpath_ctx->node = events_node;
4574 return err;
4578 #if HAVE_LIBCURL
4579 /* Insert Base64 encoded data as element with text child.
4580 * @context is session context
4581 * @parent is XML node to append @element with @data as child
4582 * @ns is XML namespace of @element, use NULL to inherit from @parent
4583 * @element is UTF-8 encoded name of new element
4584 * @data is bit stream to encode into @element
4585 * @length is size of @data in bytes
4586 * @return standard error code and fill long error message if needed */
4587 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4588 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4589 const void *data, size_t length) {
4590 isds_error err = IE_SUCCESS;
4591 xmlNodePtr node;
4593 if (!context) return IE_INVALID_CONTEXT;
4594 if (!data && length > 0) return IE_INVAL;
4595 if (!parent || !element) return IE_INVAL;
4597 xmlChar *base64data = NULL;
4598 base64data = (xmlChar *) _isds_b64encode(data, length);
4599 if (!base64data) {
4600 isds_printf_message(context,
4601 ngettext("Not enough memory to encode %zd byte into Base64",
4602 "Not enough memory to encode %zd bytes into Base64",
4603 length),
4604 length);
4605 err = IE_NOMEM;
4606 goto leave;
4608 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4610 leave:
4611 free(base64data);
4612 return err;
4616 /* Convert isds_document structure into XML tree and append to dmFiles node.
4617 * @context is session context
4618 * @document is ISDS document
4619 * @dm_files is XML element the resulting tree will be appended to as a child.
4620 * @return error code, in case of error context' message is filled. */
4621 static isds_error insert_document(struct isds_ctx *context,
4622 struct isds_document *document, xmlNodePtr dm_files) {
4623 isds_error err = IE_SUCCESS;
4624 xmlNodePtr new_file = NULL, file = NULL, node;
4625 xmlAttrPtr attribute_node;
4627 if (!context) return IE_INVALID_CONTEXT;
4628 if (!document || !dm_files) return IE_INVAL;
4630 /* Allocate new dmFile */
4631 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4632 if (!new_file) {
4633 isds_printf_message(context, _("Could not allocate main dmFile"));
4634 err = IE_ERROR;
4635 goto leave;
4637 /* Append the new dmFile.
4638 * XXX: Main document must go first */
4639 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4640 file = xmlAddPrevSibling(dm_files->children, new_file);
4641 else
4642 file = xmlAddChild(dm_files, new_file);
4644 if (!file) {
4645 xmlFreeNode(new_file); new_file = NULL;
4646 isds_printf_message(context, _("Could not add dmFile child to "
4647 "%s element"), dm_files->name);
4648 err = IE_ERROR;
4649 goto leave;
4652 /* @dmMimeType is required */
4653 if (!document->dmMimeType) {
4654 isds_log_message(context,
4655 _("Document is missing mandatory MIME type definition"));
4656 err = IE_INVAL;
4657 goto leave;
4659 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4661 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4662 if (!string) {
4663 isds_printf_message(context,
4664 _("Document has unknown dmFileMetaType: %ld"),
4665 document->dmFileMetaType);
4666 err = IE_ENUM;
4667 goto leave;
4669 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4671 if (document->dmFileGuid) {
4672 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4674 if (document->dmUpFileGuid) {
4675 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4678 /* @dmFileDescr is required */
4679 if (!document->dmFileDescr) {
4680 isds_log_message(context,
4681 _("Document is missing mandatory description (title)"));
4682 err = IE_INVAL;
4683 goto leave;
4685 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4687 if (document->dmFormat) {
4688 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4692 /* Insert content (body) of the document. */
4693 if (document->is_xml) {
4694 /* XML document requested */
4696 /* Allocate new dmXMLContent */
4697 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4698 if (!xmlcontent) {
4699 isds_printf_message(context,
4700 _("Could not allocate dmXMLContent element"));
4701 err = IE_ERROR;
4702 goto leave;
4704 /* Append it */
4705 node = xmlAddChild(file, xmlcontent);
4706 if (!node) {
4707 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4708 isds_printf_message(context,
4709 _("Could not add dmXMLContent child to %s element"),
4710 file->name);
4711 err = IE_ERROR;
4712 goto leave;
4715 /* Copy non-empty node list */
4716 if (document->xml_node_list) {
4717 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4718 document->xml_node_list);
4719 if (!content) {
4720 isds_printf_message(context,
4721 _("Not enough memory to copy XML document"));
4722 err = IE_NOMEM;
4723 goto leave;
4726 if (!xmlAddChildList(node, content)) {
4727 xmlFreeNodeList(content);
4728 isds_printf_message(context,
4729 _("Error while adding XML document into dmXMLContent"));
4730 err = IE_XML;
4731 goto leave;
4733 /* XXX: We cannot free the content here because it's part of node's
4734 * document since now. It will be freed with it automatically. */
4736 } else {
4737 /* Binary document requested */
4738 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4739 document->data, document->data_length);
4740 if (err) goto leave;
4743 leave:
4744 return err;
4748 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4749 * The copy must be preallocated, the date are just appended into structure.
4750 * @context is ISDS context
4751 * @copy is message copy structure
4752 * @xpath_ctx is XPath context with current node as tMStatus */
4753 static isds_error append_TMStatus(struct isds_ctx *context,
4754 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4755 isds_error err = IE_SUCCESS;
4756 xmlXPathObjectPtr result = NULL;
4757 char *code = NULL, *message = NULL;
4759 if (!context) return IE_INVALID_CONTEXT;
4760 if (!copy || !xpath_ctx) return IE_INVAL;
4762 /* Free old values */
4763 zfree(copy->dmStatus);
4764 zfree(copy->dmID);
4766 /* Get error specific to this copy */
4767 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4768 if (!code) {
4769 isds_log_message(context,
4770 _("Missing isds:dmStatusCode under "
4771 "XSD:tMStatus type element"));
4772 err = IE_ISDS;
4773 goto leave;
4776 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4777 /* This copy failed */
4778 copy->error = IE_ISDS;
4779 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4780 if (message) {
4781 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4782 if (!copy->dmStatus) {
4783 copy->dmStatus = code;
4784 code = NULL;
4786 } else {
4787 copy->dmStatus = code;
4788 code = NULL;
4790 } else {
4791 /* This copy succeeded. In this case only, message ID is valid */
4792 copy->error = IE_SUCCESS;
4794 EXTRACT_STRING("isds:dmID", copy->dmID);
4795 if (!copy->dmID) {
4796 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4797 "but did not returned assigned message ID\n"));
4798 err = IE_ISDS;
4802 leave:
4803 free(code);
4804 free(message);
4805 xmlXPathFreeObject(result);
4806 return err;
4810 /* Insert struct isds_approval data (box approval) into XML tree
4811 * @context is session context
4812 * @approval is libisds structure with approval description. NULL is
4813 * acceptable.
4814 * @parent is XML element to append @approval to */
4815 static isds_error insert_GExtApproval(struct isds_ctx *context,
4816 const struct isds_approval *approval, xmlNodePtr parent) {
4818 isds_error err = IE_SUCCESS;
4819 xmlNodePtr node;
4821 if (!context) return IE_INVALID_CONTEXT;
4822 if (!parent) return IE_INVAL;
4824 if (!approval) return IE_SUCCESS;
4826 /* Build XSD:gExtApproval */
4827 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4828 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4830 leave:
4831 return err;
4835 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4836 * code
4837 * @context is session context
4838 * @service_name is name of SERVICE_DB_ACCESS
4839 * @response is reallocated server SOAP body response as XML document
4840 * @raw_response is reallocated bit stream with response body. Use
4841 * NULL if you don't care
4842 * @raw_response_length is size of @raw_response in bytes
4843 * @code is reallocated ISDS status code
4844 * @status_message is reallocated ISDS status message
4845 * @return error coded from lower layer, context message will be set up
4846 * appropriately. */
4847 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4848 const xmlChar *service_name,
4849 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4850 xmlChar **code, xmlChar **status_message) {
4852 isds_error err = IE_SUCCESS;
4853 char *service_name_locale = NULL;
4854 xmlNodePtr request = NULL, node;
4855 xmlNsPtr isds_ns = NULL;
4857 if (!context) return IE_INVALID_CONTEXT;
4858 if (!service_name) return IE_INVAL;
4859 if (!response || !code || !status_message) return IE_INVAL;
4860 if (!raw_response_length && raw_response) return IE_INVAL;
4862 /* Free output argument */
4863 xmlFreeDoc(*response); *response = NULL;
4864 if (raw_response) zfree(*raw_response);
4865 zfree(*code);
4866 zfree(*status_message);
4869 /* Check if connection is established
4870 * TODO: This check should be done downstairs. */
4871 if (!context->curl) return IE_CONNECTION_CLOSED;
4873 service_name_locale = _isds_utf82locale((char*)service_name);
4874 if (!service_name_locale) {
4875 err = IE_NOMEM;
4876 goto leave;
4879 /* Build request */
4880 request = xmlNewNode(NULL, service_name);
4881 if (!request) {
4882 isds_printf_message(context,
4883 _("Could not build %s request"), service_name_locale);
4884 err = IE_ERROR;
4885 goto leave;
4887 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4888 if(!isds_ns) {
4889 isds_log_message(context, _("Could not create ISDS name space"));
4890 err = IE_ERROR;
4891 goto leave;
4893 xmlSetNs(request, isds_ns);
4896 /* Add XSD:tDummyInput child */
4897 INSERT_STRING(request, "dbDummy", NULL);
4900 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4901 service_name_locale);
4903 /* Send request */
4904 err = _isds(context, SERVICE_DB_ACCESS, request, response,
4905 raw_response, raw_response_length);
4906 xmlFreeNode(request); request = NULL;
4908 if (err) {
4909 isds_log(ILF_ISDS, ILL_DEBUG,
4910 _("Processing ISDS response on %s request failed\n"),
4911 service_name_locale);
4912 goto leave;
4915 /* Check for response status */
4916 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4917 code, status_message, NULL);
4918 if (err) {
4919 isds_log(ILF_ISDS, ILL_DEBUG,
4920 _("ISDS response on %s request is missing status\n"),
4921 service_name_locale);
4922 goto leave;
4925 /* Request processed, but nothing found */
4926 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4927 char *code_locale = _isds_utf82locale((char*) *code);
4928 char *status_message_locale =
4929 _isds_utf82locale((char*) *status_message);
4930 isds_log(ILF_ISDS, ILL_DEBUG,
4931 _("Server refused %s request (code=%s, message=%s)\n"),
4932 service_name_locale, code_locale, status_message_locale);
4933 isds_log_message(context, status_message_locale);
4934 free(code_locale);
4935 free(status_message_locale);
4936 err = IE_ISDS;
4937 goto leave;
4940 leave:
4941 free(service_name_locale);
4942 xmlFreeNode(request);
4943 return err;
4945 #endif
4948 /* Get data about logged in user and his box. */
4949 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4950 struct isds_DbOwnerInfo **db_owner_info) {
4951 isds_error err = IE_SUCCESS;
4952 #if HAVE_LIBCURL
4953 xmlDocPtr response = NULL;
4954 xmlChar *code = NULL, *message = NULL;
4955 xmlXPathContextPtr xpath_ctx = NULL;
4956 xmlXPathObjectPtr result = NULL;
4957 char *string = NULL;
4958 #endif
4960 if (!context) return IE_INVALID_CONTEXT;
4961 zfree(context->long_message);
4962 if (!db_owner_info) return IE_INVAL;
4963 isds_DbOwnerInfo_free(db_owner_info);
4965 #if HAVE_LIBCURL
4966 /* Check if connection is established */
4967 if (!context->curl) return IE_CONNECTION_CLOSED;
4970 /* Do request and check for success */
4971 err = build_send_check_dbdummy_request(context,
4972 BAD_CAST "GetOwnerInfoFromLogin",
4973 &response, NULL, NULL, &code, &message);
4974 if (err) goto leave;
4977 /* Extract data */
4978 /* Prepare structure */
4979 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4980 if (!*db_owner_info) {
4981 err = IE_NOMEM;
4982 goto leave;
4984 xpath_ctx = xmlXPathNewContext(response);
4985 if (!xpath_ctx) {
4986 err = IE_ERROR;
4987 goto leave;
4989 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4990 err = IE_ERROR;
4991 goto leave;
4994 /* Set context node */
4995 result = xmlXPathEvalExpression(BAD_CAST
4996 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4997 if (!result) {
4998 err = IE_ERROR;
4999 goto leave;
5001 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5002 isds_log_message(context, _("Missing dbOwnerInfo element"));
5003 err = IE_ISDS;
5004 goto leave;
5006 if (result->nodesetval->nodeNr > 1) {
5007 isds_log_message(context, _("Multiple dbOwnerInfo element"));
5008 err = IE_ISDS;
5009 goto leave;
5011 xpath_ctx->node = result->nodesetval->nodeTab[0];
5012 xmlXPathFreeObject(result); result = NULL;
5014 /* Extract it */
5015 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
5018 leave:
5019 if (err) {
5020 isds_DbOwnerInfo_free(db_owner_info);
5023 free(string);
5024 xmlXPathFreeObject(result);
5025 xmlXPathFreeContext(xpath_ctx);
5027 free(code);
5028 free(message);
5029 xmlFreeDoc(response);
5031 if (!err)
5032 isds_log(ILF_ISDS, ILL_DEBUG,
5033 _("GetOwnerInfoFromLogin request processed by server "
5034 "successfully.\n"));
5035 #else /* not HAVE_LIBCURL */
5036 err = IE_NOTSUP;
5037 #endif
5039 return err;
5043 /* Get data about logged in user. */
5044 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
5045 struct isds_DbUserInfo **db_user_info) {
5046 isds_error err = IE_SUCCESS;
5047 #if HAVE_LIBCURL
5048 xmlDocPtr response = NULL;
5049 xmlChar *code = NULL, *message = NULL;
5050 xmlXPathContextPtr xpath_ctx = NULL;
5051 xmlXPathObjectPtr result = NULL;
5052 #endif
5054 if (!context) return IE_INVALID_CONTEXT;
5055 zfree(context->long_message);
5056 if (!db_user_info) return IE_INVAL;
5057 isds_DbUserInfo_free(db_user_info);
5059 #if HAVE_LIBCURL
5060 /* Check if connection is established */
5061 if (!context->curl) return IE_CONNECTION_CLOSED;
5064 /* Do request and check for success */
5065 err = build_send_check_dbdummy_request(context,
5066 BAD_CAST "GetUserInfoFromLogin",
5067 &response, NULL, NULL, &code, &message);
5068 if (err) goto leave;
5071 /* Extract data */
5072 /* Prepare structure */
5073 *db_user_info = calloc(1, sizeof(**db_user_info));
5074 if (!*db_user_info) {
5075 err = IE_NOMEM;
5076 goto leave;
5078 xpath_ctx = xmlXPathNewContext(response);
5079 if (!xpath_ctx) {
5080 err = IE_ERROR;
5081 goto leave;
5083 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5084 err = IE_ERROR;
5085 goto leave;
5088 /* Set context node */
5089 result = xmlXPathEvalExpression(BAD_CAST
5090 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
5091 if (!result) {
5092 err = IE_ERROR;
5093 goto leave;
5095 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5096 isds_log_message(context, _("Missing dbUserInfo element"));
5097 err = IE_ISDS;
5098 goto leave;
5100 if (result->nodesetval->nodeNr > 1) {
5101 isds_log_message(context, _("Multiple dbUserInfo element"));
5102 err = IE_ISDS;
5103 goto leave;
5105 xpath_ctx->node = result->nodesetval->nodeTab[0];
5106 xmlXPathFreeObject(result); result = NULL;
5108 /* Extract it */
5109 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5111 leave:
5112 if (err) {
5113 isds_DbUserInfo_free(db_user_info);
5116 xmlXPathFreeObject(result);
5117 xmlXPathFreeContext(xpath_ctx);
5119 free(code);
5120 free(message);
5121 xmlFreeDoc(response);
5123 if (!err)
5124 isds_log(ILF_ISDS, ILL_DEBUG,
5125 _("GetUserInfoFromLogin request processed by server "
5126 "successfully.\n"));
5127 #else /* not HAVE_LIBCURL */
5128 err = IE_NOTSUP;
5129 #endif
5131 return err;
5135 /* Get expiration time of current password
5136 * @context is session context
5137 * @expiration is automatically reallocated time when password expires. If
5138 * password expiration is disabled, NULL will be returned. In case of error
5139 * it will be nulled too. */
5140 isds_error isds_get_password_expiration(struct isds_ctx *context,
5141 struct timeval **expiration) {
5142 isds_error err = IE_SUCCESS;
5143 #if HAVE_LIBCURL
5144 xmlDocPtr response = NULL;
5145 xmlChar *code = NULL, *message = NULL;
5146 xmlXPathContextPtr xpath_ctx = NULL;
5147 xmlXPathObjectPtr result = NULL;
5148 char *string = NULL;
5149 #endif
5151 if (!context) return IE_INVALID_CONTEXT;
5152 zfree(context->long_message);
5153 if (!expiration) return IE_INVAL;
5154 zfree(*expiration);
5156 #if HAVE_LIBCURL
5157 /* Check if connection is established */
5158 if (!context->curl) return IE_CONNECTION_CLOSED;
5161 /* Do request and check for success */
5162 err = build_send_check_dbdummy_request(context,
5163 BAD_CAST "GetPasswordInfo",
5164 &response, NULL, NULL, &code, &message);
5165 if (err) goto leave;
5168 /* Extract data */
5169 xpath_ctx = xmlXPathNewContext(response);
5170 if (!xpath_ctx) {
5171 err = IE_ERROR;
5172 goto leave;
5174 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5175 err = IE_ERROR;
5176 goto leave;
5179 /* Set context node */
5180 result = xmlXPathEvalExpression(BAD_CAST
5181 "/isds:GetPasswordInfoResponse", xpath_ctx);
5182 if (!result) {
5183 err = IE_ERROR;
5184 goto leave;
5186 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5187 isds_log_message(context,
5188 _("Missing GetPasswordInfoResponse element"));
5189 err = IE_ISDS;
5190 goto leave;
5192 if (result->nodesetval->nodeNr > 1) {
5193 isds_log_message(context,
5194 _("Multiple GetPasswordInfoResponse element"));
5195 err = IE_ISDS;
5196 goto leave;
5198 xpath_ctx->node = result->nodesetval->nodeTab[0];
5199 xmlXPathFreeObject(result); result = NULL;
5201 /* Extract expiration date */
5202 EXTRACT_STRING("isds:pswExpDate", string);
5203 if (string) {
5204 /* And convert it if any returned. Otherwise expiration is disabled. */
5205 err = timestring2timeval((xmlChar *) string, expiration);
5206 if (err) {
5207 char *string_locale = _isds_utf82locale(string);
5208 if (err == IE_DATE) err = IE_ISDS;
5209 isds_printf_message(context,
5210 _("Could not convert pswExpDate as ISO time: %s"),
5211 string_locale);
5212 free(string_locale);
5213 goto leave;
5217 leave:
5218 if (err) {
5219 if (*expiration) {
5220 zfree(*expiration);
5224 free(string);
5225 xmlXPathFreeObject(result);
5226 xmlXPathFreeContext(xpath_ctx);
5228 free(code);
5229 free(message);
5230 xmlFreeDoc(response);
5232 if (!err)
5233 isds_log(ILF_ISDS, ILL_DEBUG,
5234 _("GetPasswordInfo request processed by server "
5235 "successfully.\n"));
5236 #else /* not HAVE_LIBCURL */
5237 err = IE_NOTSUP;
5238 #endif
5240 return err;
5244 #if HAVE_LIBCURL
5245 /* Request delivering new TOTP code from ISDS through side channel before
5246 * changing password.
5247 * @context is session context
5248 * @password is current password.
5249 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5250 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5251 * function for more details.
5252 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5253 * NULL, if you don't care.
5254 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5255 * error code. */
5256 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5257 const char *password, struct isds_otp *otp, char **refnumber) {
5258 isds_error err = IE_SUCCESS;
5259 char *saved_url = NULL; /* No copy */
5260 #if HAVE_CURL_REAUTHORIZATION_BUG
5261 CURL *saved_curl = NULL; /* No copy */
5262 #endif
5263 xmlNsPtr isds_ns = NULL;
5264 xmlNodePtr request = NULL;
5265 xmlDocPtr response = NULL;
5266 xmlChar *code = NULL, *message = NULL;
5267 const xmlChar *codes[] = {
5268 BAD_CAST "2300",
5269 BAD_CAST "2301",
5270 BAD_CAST "2302"
5272 const char *meanings[] = {
5273 N_("Unexpected error"),
5274 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5275 N_("One-time code could not been sent. Try later again.")
5277 const isds_otp_resolution resolutions[] = {
5278 OTP_RESOLUTION_UNKNOWN,
5279 OTP_RESOLUTION_TO_FAST,
5280 OTP_RESOLUTION_TOTP_NOT_SENT
5283 if (NULL == context) return IE_INVALID_CONTEXT;
5284 zfree(context->long_message);
5285 if (NULL == password) {
5286 isds_log_message(context,
5287 _("Second argument (password) of isds_change_password() "
5288 "is NULL"));
5289 return IE_INVAL;
5292 /* Check if connection is established
5293 * TODO: This check should be done downstairs. */
5294 if (!context->curl) return IE_CONNECTION_CLOSED;
5296 if (!context->otp) {
5297 isds_log_message(context, _("This function requires OTP-authenticated "
5298 "context"));
5299 return IE_INVALID_CONTEXT;
5301 if (NULL == otp) {
5302 isds_log_message(context, _("If one-time password authentication "
5303 "method is in use, requesting new OTP code requires "
5304 "one-time credentials argument either"));
5305 return IE_INVAL;
5307 if (otp->method != OTP_TIME) {
5308 isds_log_message(context, _("Requesting new time-based OTP code from "
5309 "server requires one-time password authentication "
5310 "method"));
5311 return IE_INVAL;
5313 if (otp->otp_code != NULL) {
5314 isds_log_message(context, _("Requesting new time-based OTP code from "
5315 "server requires undefined OTP code member in "
5316 "one-time credentials argument"));
5317 return IE_INVAL;
5321 /* Build request */
5322 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5323 if (!request) {
5324 isds_log_message(context, _("Could not build SendSMSCode request"));
5325 return IE_ERROR;
5327 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5328 if(!isds_ns) {
5329 isds_log_message(context, _("Could not create ISDS name space"));
5330 xmlFreeNode(request);
5331 return IE_ERROR;
5333 xmlSetNs(request, isds_ns);
5335 /* Change URL temporarily for sending this request only */
5337 char *new_url = NULL;
5338 if ((err = _isds_build_url_from_context(context,
5339 "%1$.*2$sasws/changePassword", &new_url))) {
5340 goto leave;
5342 saved_url = context->url;
5343 context->url = new_url;
5346 /* Store credentials for sending this request only */
5347 context->otp_credentials = otp;
5348 _isds_discard_credentials(context, 0);
5349 if ((err = _isds_store_credentials(context, context->saved_username,
5350 password, NULL))) {
5351 _isds_discard_credentials(context, 0);
5352 goto leave;
5354 #if HAVE_CURL_REAUTHORIZATION_BUG
5355 saved_curl = context->curl;
5356 context->curl = curl_easy_init();
5357 if (NULL == context->curl) {
5358 err = IE_ERROR;
5359 goto leave;
5361 if (context->timeout) {
5362 err = isds_set_timeout(context, context->timeout);
5363 if (err) goto leave;
5365 #endif
5367 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5369 /* Sent request */
5370 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5372 /* Remove temporal credentials */
5373 _isds_discard_credentials(context, 0);
5374 /* Detach pointer to OTP credentials from context */
5375 context->otp_credentials = NULL;
5376 /* Keep context->otp true to keep signaling this is OTP session */
5378 /* Destroy request */
5379 xmlFreeNode(request); request = NULL;
5381 if (err) {
5382 isds_log(ILF_ISDS, ILL_DEBUG,
5383 _("Processing ISDS response on SendSMSCode request failed\n"));
5384 goto leave;
5387 /* Check for response status */
5388 err = isds_response_status(context, SERVICE_ASWS, response,
5389 &code, &message, (xmlChar **)refnumber);
5390 if (err) {
5391 isds_log(ILF_ISDS, ILL_DEBUG,
5392 _("ISDS response on SendSMSCode request is missing "
5393 "status\n"));
5394 goto leave;
5397 /* Check for error */
5398 if (xmlStrcmp(code, BAD_CAST "0000")) {
5399 char *code_locale = _isds_utf82locale((char*)code);
5400 char *message_locale = _isds_utf82locale((char*)message);
5401 size_t i;
5402 isds_log(ILF_ISDS, ILL_DEBUG,
5403 _("Server refused to send new code on SendSMSCode "
5404 "request (code=%s, message=%s)\n"),
5405 code_locale, message_locale);
5407 /* Check for known error codes */
5408 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5409 if (!xmlStrcmp(code, codes[i])) break;
5411 if (i < sizeof(codes)/sizeof(*codes)) {
5412 isds_log_message(context, _(meanings[i]));
5413 /* Mimic otp->resolution according to the code, specification does
5414 * prescribe OTP header to be available. */
5415 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5416 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5417 otp->resolution = resolutions[i];
5418 } else
5419 isds_log_message(context, message_locale);
5421 free(code_locale);
5422 free(message_locale);
5424 err = IE_ISDS;
5425 goto leave;
5428 /* Otherwise new code sent successfully */
5429 /* Mimic otp->resolution according to the code, specification does
5430 * prescribe OTP header to be available. */
5431 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5432 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5434 leave:
5435 if (NULL != saved_url) {
5436 /* Revert URL to original one */
5437 zfree(context->url);
5438 context->url = saved_url;
5440 #if HAVE_CURL_REAUTHORIZATION_BUG
5441 if (NULL != saved_curl) {
5442 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5443 context->curl = saved_curl;
5445 #endif
5447 free(code);
5448 free(message);
5449 xmlFreeDoc(response);
5450 xmlFreeNode(request);
5452 if (!err)
5453 isds_log(ILF_ISDS, ILL_DEBUG,
5454 _("New OTP code has been sent successfully on SendSMSCode "
5455 "request.\n"));
5456 return err;
5460 /* Convert response status code to isds_error code and set long message
5461 * @context is context to save long message to
5462 * @map is mapping from codes to errors and messages. Pass NULL for generic
5463 * handling.
5464 * @code is status code to translate
5465 * @message is non-localized status message to put into long message in case
5466 * of uknown error. It can be NULL if server did not provide any.
5467 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5468 * invalid invocation. */
5469 static isds_error statuscode2isds_error(struct isds_ctx *context,
5470 const struct code_map_isds_error *map,
5471 const xmlChar *code, const xmlChar *message) {
5472 if (NULL == code) {
5473 isds_log_message(context,
5474 _("NULL status code passed to statuscode2isds_error()"));
5475 return IE_INVAL;
5478 if (NULL != map) {
5479 /* Check for known error codes */
5480 for (int i=0; map->codes[i] != NULL; i++) {
5481 if (!xmlStrcmp(code, map->codes[i])) {
5482 isds_log_message(context, _(map->meanings[i]));
5483 return map->errors[i];
5488 /* Other error */
5489 if (xmlStrcmp(code, BAD_CAST "0000")) {
5490 char *message_locale = _isds_utf82locale((char*)message);
5491 if (NULL == message_locale)
5492 isds_log_message(context, _("ISDS server returned unknown error"));
5493 else
5494 isds_log_message(context, message_locale);
5495 free(message_locale);
5496 return IE_ISDS;
5499 return IE_SUCCESS;
5501 #endif
5504 /* Change user password in ISDS.
5505 * User must supply old password, new password will takes effect after some
5506 * time, current session can continue. Password must fulfill some constraints.
5507 * @context is session context
5508 * @old_password is current password.
5509 * @new_password is requested new password
5510 * @otp auxiliary data required if one-time password authentication is in use,
5511 * defines OTP code (if known) and returns fine grade resolution of OTP
5512 * procedure. Pass NULL, if one-time password authentication is not needed.
5513 * Please note the @otp argument must match OTP method used at log-in time. See
5514 * isds_login() function for more details.
5515 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5516 * NULL, if you don't care.
5517 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5518 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5519 * awaiting OTP code that has been delivered by side channel to the user. */
5520 isds_error isds_change_password(struct isds_ctx *context,
5521 const char *old_password, const char *new_password,
5522 struct isds_otp *otp, char **refnumber) {
5523 isds_error err = IE_SUCCESS;
5524 #if HAVE_LIBCURL
5525 char *saved_url = NULL; /* No copy */
5526 #if HAVE_CURL_REAUTHORIZATION_BUG
5527 CURL *saved_curl = NULL; /* No copy */
5528 #endif
5529 xmlNsPtr isds_ns = NULL;
5530 xmlNodePtr request = NULL, node;
5531 xmlDocPtr response = NULL;
5532 xmlChar *code = NULL, *message = NULL;
5533 const xmlChar *codes[] = {
5534 BAD_CAST "1066",
5535 BAD_CAST "1067",
5536 BAD_CAST "1079",
5537 BAD_CAST "1080",
5538 BAD_CAST "1081",
5539 BAD_CAST "1082",
5540 BAD_CAST "1083",
5541 BAD_CAST "1090",
5542 BAD_CAST "1091",
5543 BAD_CAST "2300",
5544 BAD_CAST "9204"
5546 const char *meanings[] = {
5547 N_("Password length must be between 8 and 32 characters"),
5548 N_("Password cannot be reused"), /* Server does not distinguish 1067
5549 and 1091 on ChangePasswordOTP */
5550 N_("Password contains forbidden character"),
5551 N_("Password must contain at least one upper-case letter, "
5552 "one lower-case, and one digit"),
5553 N_("Password cannot contain sequence of three identical characters"),
5554 N_("Password cannot contain user identifier"),
5555 N_("Password is too simmple"),
5556 N_("Old password is not valid"),
5557 N_("Password cannot be reused"),
5558 N_("Unexpected error"),
5559 N_("LDAP update error")
5561 #endif
5563 if (!context) return IE_INVALID_CONTEXT;
5564 zfree(context->long_message);
5565 if (NULL != refnumber)
5566 zfree(*refnumber);
5567 if (NULL == old_password) {
5568 isds_log_message(context,
5569 _("Second argument (old password) of isds_change_password() "
5570 "is NULL"));
5571 return IE_INVAL;
5573 if (NULL == otp && NULL == new_password) {
5574 isds_log_message(context,
5575 _("Third argument (new password) of isds_change_password() "
5576 "is NULL"));
5577 return IE_INVAL;
5580 #if HAVE_LIBCURL
5581 /* Check if connection is established
5582 * TODO: This check should be done downstairs. */
5583 if (!context->curl) return IE_CONNECTION_CLOSED;
5585 if (context->otp && NULL == otp) {
5586 isds_log_message(context, _("If one-time password authentication "
5587 "method is in use, changing password requires one-time "
5588 "credentials either"));
5589 return IE_INVAL;
5592 /* Build ChangeISDSPassword request */
5593 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5594 BAD_CAST "ChangePasswordOTP");
5595 if (!request) {
5596 isds_log_message(context, (NULL == otp) ?
5597 _("Could not build ChangeISDSPassword request") :
5598 _("Could not build ChangePasswordOTP request"));
5599 return IE_ERROR;
5601 isds_ns = xmlNewNs(request,
5602 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5603 NULL);
5604 if(!isds_ns) {
5605 isds_log_message(context, _("Could not create ISDS name space"));
5606 xmlFreeNode(request);
5607 return IE_ERROR;
5609 xmlSetNs(request, isds_ns);
5611 INSERT_STRING(request, "dbOldPassword", old_password);
5612 INSERT_STRING(request, "dbNewPassword", new_password);
5614 if (NULL != otp) {
5615 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5616 switch (otp->method) {
5617 case OTP_HMAC:
5618 isds_log(ILF_SEC, ILL_INFO,
5619 _("Selected authentication method: "
5620 "HMAC-based one-time password\n"));
5621 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5622 break;
5623 case OTP_TIME:
5624 isds_log(ILF_SEC, ILL_INFO,
5625 _("Selected authentication method: "
5626 "Time-based one-time password\n"));
5627 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5628 if (otp->otp_code == NULL) {
5629 isds_log(ILF_SEC, ILL_INFO,
5630 _("OTP code has not been provided by "
5631 "application, requesting server for "
5632 "new one.\n"));
5633 err = _isds_request_totp_code(context, old_password, otp,
5634 refnumber);
5635 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5636 goto leave;
5638 } else {
5639 isds_log(ILF_SEC, ILL_INFO,
5640 _("OTP code has been provided by "
5641 "application, not requesting server "
5642 "for new one.\n"));
5644 break;
5645 default:
5646 isds_log_message(context,
5647 _("Unknown one-time password authentication "
5648 "method requested by application"));
5649 err = IE_ENUM;
5650 goto leave;
5653 /* Change URL temporarily for sending this request only */
5655 char *new_url = NULL;
5656 if ((err = _isds_build_url_from_context(context,
5657 "%1$.*2$sasws/changePassword", &new_url))) {
5658 goto leave;
5660 saved_url = context->url;
5661 context->url = new_url;
5664 /* Store credentials for sending this request only */
5665 context->otp_credentials = otp;
5666 _isds_discard_credentials(context, 0);
5667 if ((err = _isds_store_credentials(context, context->saved_username,
5668 old_password, NULL))) {
5669 _isds_discard_credentials(context, 0);
5670 goto leave;
5672 #if HAVE_CURL_REAUTHORIZATION_BUG
5673 saved_curl = context->curl;
5674 context->curl = curl_easy_init();
5675 if (NULL == context->curl) {
5676 err = IE_ERROR;
5677 goto leave;
5679 if (context->timeout) {
5680 err = isds_set_timeout(context, context->timeout);
5681 if (err) goto leave;
5683 #endif
5686 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5687 _("Sending ChangeISDSPassword request to ISDS\n") :
5688 _("Sending ChangePasswordOTP request to ISDS\n"));
5690 /* Sent request */
5691 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5692 request, &response, NULL, NULL);
5694 if (otp) {
5695 /* Remove temporal credentials */
5696 _isds_discard_credentials(context, 0);
5697 /* Detach pointer to OTP credentials from context */
5698 context->otp_credentials = NULL;
5699 /* Keep context->otp true to keep signaling this is OTP session */
5702 /* Destroy request */
5703 xmlFreeNode(request); request = NULL;
5705 if (err) {
5706 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5707 _("Processing ISDS response on ChangeISDSPassword "
5708 "request failed\n") :
5709 _("Processing ISDS response on ChangePasswordOTP "
5710 "request failed\n"));
5711 goto leave;
5714 /* Check for response status */
5715 err = isds_response_status(context,
5716 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5717 &code, &message, (xmlChar **)refnumber);
5718 if (err) {
5719 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5720 _("ISDS response on ChangeISDSPassword request is missing "
5721 "status\n") :
5722 _("ISDS response on ChangePasswordOTP request is missing "
5723 "status\n"));
5724 goto leave;
5727 /* Check for known error codes */
5728 for (size_t i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5729 if (!xmlStrcmp(code, codes[i])) {
5730 char *code_locale = _isds_utf82locale((char*)code);
5731 char *message_locale = _isds_utf82locale((char*)message);
5732 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5733 _("Server refused to change password on ChangeISDSPassword "
5734 "request (code=%s, message=%s)\n") :
5735 _("Server refused to change password on ChangePasswordOTP "
5736 "request (code=%s, message=%s)\n"),
5737 code_locale, message_locale);
5738 free(code_locale);
5739 free(message_locale);
5740 isds_log_message(context, _(meanings[i]));
5741 err = IE_INVAL;
5742 goto leave;
5746 /* Other error */
5747 if (xmlStrcmp(code, BAD_CAST "0000")) {
5748 char *code_locale = _isds_utf82locale((char*)code);
5749 char *message_locale = _isds_utf82locale((char*)message);
5750 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5751 _("Server refused to change password on ChangeISDSPassword "
5752 "request (code=%s, message=%s)\n") :
5753 _("Server refused to change password on ChangePasswordOTP "
5754 "request (code=%s, message=%s)\n"),
5755 code_locale, message_locale);
5756 isds_log_message(context, message_locale);
5757 free(code_locale);
5758 free(message_locale);
5759 err = IE_ISDS;
5760 goto leave;
5763 /* Otherwise password changed successfully */
5765 leave:
5766 if (NULL != saved_url) {
5767 /* Revert URL to original one */
5768 zfree(context->url);
5769 context->url = saved_url;
5771 #if HAVE_CURL_REAUTHORIZATION_BUG
5772 if (NULL != saved_curl) {
5773 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5774 context->curl = saved_curl;
5776 #endif
5778 free(code);
5779 free(message);
5780 xmlFreeDoc(response);
5781 xmlFreeNode(request);
5783 if (!err)
5784 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5785 _("Password changed successfully on ChangeISDSPassword "
5786 "request.\n") :
5787 _("Password changed successfully on ChangePasswordOTP "
5788 "request.\n"));
5789 #else /* not HAVE_LIBCURL */
5790 err = IE_NOTSUP;
5791 #endif
5793 return err;
5797 #if HAVE_LIBCURL
5798 /* Generic middle part with request sending and response check.
5799 * It sends prepared request and checks for error code.
5800 * @context is ISDS session context.
5801 * @service is ISDS service handler
5802 * @service_name is name in scope of given @service
5803 * @request is XML tree with request. Will be freed to save memory.
5804 * @response is XML document outputting ISDS response.
5805 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5806 * @map is mapping from status code to library error. Pass NULL if no special
5807 * handling is requested.
5808 * NULL, if you don't care. */
5809 static isds_error send_destroy_request_check_response(
5810 struct isds_ctx *context,
5811 const isds_service service, const xmlChar *service_name,
5812 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5813 const struct code_map_isds_error *map) {
5814 isds_error err = IE_SUCCESS;
5815 char *service_name_locale = NULL;
5816 xmlChar *code = NULL, *message = NULL;
5819 if (!context) return IE_INVALID_CONTEXT;
5820 if (!service_name || *service_name == '\0' || !request || !*request ||
5821 !response)
5822 return IE_INVAL;
5824 /* Check if connection is established
5825 * TODO: This check should be done downstairs. */
5826 if (!context->curl) return IE_CONNECTION_CLOSED;
5828 service_name_locale = _isds_utf82locale((char*) service_name);
5829 if (!service_name_locale) {
5830 err = IE_NOMEM;
5831 goto leave;
5834 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5835 service_name_locale);
5837 /* Send request */
5838 err = _isds(context, service, *request, response, NULL, NULL);
5839 xmlFreeNode(*request); *request = NULL;
5841 if (err) {
5842 isds_log(ILF_ISDS, ILL_DEBUG,
5843 _("Processing ISDS response on %s request failed\n"),
5844 service_name_locale);
5845 goto leave;
5848 /* Check for response status */
5849 err = isds_response_status(context, service, *response,
5850 &code, &message, refnumber);
5851 if (err) {
5852 isds_log(ILF_ISDS, ILL_DEBUG,
5853 _("ISDS response on %s request is missing status\n"),
5854 service_name_locale);
5855 goto leave;
5858 err = statuscode2isds_error(context, map, code, message);
5860 /* Request processed, but server failed */
5861 if (xmlStrcmp(code, BAD_CAST "0000")) {
5862 char *code_locale = _isds_utf82locale((char*) code);
5863 char *message_locale = _isds_utf82locale((char*) message);
5864 isds_log(ILF_ISDS, ILL_DEBUG,
5865 _("Server refused %s request (code=%s, message=%s)\n"),
5866 service_name_locale, code_locale, message_locale);
5867 free(code_locale);
5868 free(message_locale);
5869 goto leave;
5873 leave:
5874 free(code);
5875 free(message);
5876 if (err && *response) {
5877 xmlFreeDoc(*response);
5878 *response = NULL;
5880 if (*request) {
5881 xmlFreeNode(*request);
5882 *request = NULL;
5884 free(service_name_locale);
5886 return err;
5890 /* Generic bottom half with request sending.
5891 * It sends prepared request, checks for error code, destroys response and
5892 * request and log success or failure.
5893 * @context is ISDS session context.
5894 * @service is ISDS service handler
5895 * @service_name is name in scope of given @service
5896 * @request is XML tree with request. Will be freed to save memory.
5897 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5898 * NULL, if you don't care. */
5899 static isds_error send_request_check_drop_response(
5900 struct isds_ctx *context,
5901 const isds_service service, const xmlChar *service_name,
5902 xmlNodePtr *request, xmlChar **refnumber) {
5903 isds_error err = IE_SUCCESS;
5904 xmlDocPtr response = NULL;
5907 if (!context) return IE_INVALID_CONTEXT;
5908 if (!service_name || *service_name == '\0' || !request || !*request)
5909 return IE_INVAL;
5911 /* Send request and check response*/
5912 err = send_destroy_request_check_response(context,
5913 service, service_name, request, &response, refnumber, NULL);
5915 xmlFreeDoc(response);
5917 if (*request) {
5918 xmlFreeNode(*request);
5919 *request = NULL;
5922 if (!err) {
5923 char *service_name_locale = _isds_utf82locale((char *) service_name);
5924 isds_log(ILF_ISDS, ILL_DEBUG,
5925 _("%s request processed by server successfully.\n"),
5926 service_name_locale);
5927 free(service_name_locale);
5930 return err;
5934 /* Insert isds_credentials_delivery structure into XML request if not NULL
5935 * @context is session context
5936 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5937 * credentials delivery. The email field is passed.
5938 * @parent is XML element where to insert */
5939 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5940 const struct isds_credentials_delivery *credentials_delivery,
5941 xmlNodePtr parent) {
5942 isds_error err = IE_SUCCESS;
5943 xmlNodePtr node;
5945 if (!context) return IE_INVALID_CONTEXT;
5946 if (!parent) return IE_INVAL;
5948 if (credentials_delivery) {
5949 /* Following elements are valid only for services:
5950 * NewAccessData, AddDataBoxUser, CreateDataBox */
5951 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5952 INSERT_STRING(parent, "email", credentials_delivery->email);
5955 leave:
5956 return err;
5960 /* Extract credentials delivery from ISDS response.
5961 * @context is session context
5962 * @credentials_delivery is pointer to valid structure to fill in returned
5963 * user's password (and new log-in name). If NULL, do not extract the data.
5964 * @response is pointer to XML document with ISDS response
5965 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5966 * @return IE_SUCCESS even if new user name has not been found because it's not
5967 * clear whether it's returned always. */
5968 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5969 struct isds_credentials_delivery *credentials_delivery,
5970 xmlDocPtr response, const char *request_name) {
5971 isds_error err = IE_SUCCESS;
5972 xmlXPathContextPtr xpath_ctx = NULL;
5973 xmlXPathObjectPtr result = NULL;
5974 char *xpath_query = NULL;
5976 if (!context) return IE_INVALID_CONTEXT;
5977 if (credentials_delivery) {
5978 zfree(credentials_delivery->token);
5979 zfree(credentials_delivery->new_user_name);
5981 if (!response || !request_name || !*request_name) return IE_INVAL;
5984 /* Extract optional token */
5985 if (credentials_delivery) {
5986 xpath_ctx = xmlXPathNewContext(response);
5987 if (!xpath_ctx) {
5988 err = IE_ERROR;
5989 goto leave;
5991 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5992 err = IE_ERROR;
5993 goto leave;
5996 /* Verify root element */
5997 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
5998 request_name)) {
5999 err = IE_NOMEM;
6000 goto leave;
6002 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
6003 if (!result) {
6004 err = IE_ERROR;
6005 goto leave;
6007 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6008 char *request_name_locale = _isds_utf82locale(request_name);
6009 isds_log(ILF_ISDS, ILL_WARNING,
6010 _("Wrong element in ISDS response for %s request "
6011 "while extracting credentials delivery details\n"),
6012 request_name_locale);
6013 free(request_name_locale);
6014 err = IE_ERROR;
6015 goto leave;
6017 xpath_ctx->node = result->nodesetval->nodeTab[0];
6020 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6021 * optional. */
6022 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
6024 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
6025 if (!credentials_delivery->token) {
6026 char *request_name_locale = _isds_utf82locale(request_name);
6027 isds_log(ILF_ISDS, ILL_ERR,
6028 _("ISDS did not return token on %s request "
6029 "even if requested\n"), request_name_locale);
6030 free(request_name_locale);
6031 err = IE_ERROR;
6035 leave:
6036 free(xpath_query);
6037 xmlXPathFreeObject(result);
6038 xmlXPathFreeContext(xpath_ctx);
6040 return err;
6044 /* Build XSD:tCreateDBInput request type for box creating.
6045 * @context is session context
6046 * @request outputs built XML tree
6047 * @service_name is request name of SERVICE_DB_MANIPULATION service
6048 * @box is box description to create including single primary user (in case of
6049 * FO box type)
6050 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6051 * box, or contact address of PFO box owner)
6052 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6053 * @upper_box_id is optional ID of supper box if currently created box is
6054 * subordinated.
6055 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6056 * don't care.
6057 * @credentials_delivery is valid pointer if ISDS should return token that box
6058 * owner can use to obtain his new credentials in on-line way. Then valid email
6059 * member value should be supplied.
6060 * @approval is optional external approval of box manipulation */
6061 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
6062 xmlNodePtr *request, const xmlChar *service_name,
6063 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6064 const xmlChar *former_names, const xmlChar *upper_box_id,
6065 const xmlChar *ceo_label,
6066 const struct isds_credentials_delivery *credentials_delivery,
6067 const struct isds_approval *approval) {
6068 isds_error err = IE_SUCCESS;
6069 xmlNsPtr isds_ns = NULL;
6070 xmlNodePtr node, dbPrimaryUsers;
6071 xmlChar *string = NULL;
6072 const struct isds_list *item;
6075 if (!context) return IE_INVALID_CONTEXT;
6076 if (!request || !service_name || service_name[0] == '\0' || !box)
6077 return IE_INVAL;
6080 /* Build CreateDataBox-similar request */
6081 *request = xmlNewNode(NULL, service_name);
6082 if (!*request) {
6083 char *service_name_locale = _isds_utf82locale((char*) service_name);
6084 isds_printf_message(context, _("Could build %s request"),
6085 service_name_locale);
6086 free(service_name_locale);
6087 return IE_ERROR;
6089 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
6090 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
6091 if (!isds_ns) {
6092 isds_log_message(context, _("Could not create ISDS1 name space"));
6093 xmlFreeNode(*request);
6094 return IE_ERROR;
6096 } else {
6097 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6098 if (!isds_ns) {
6099 isds_log_message(context, _("Could not create ISDS name space"));
6100 xmlFreeNode(*request);
6101 return IE_ERROR;
6104 xmlSetNs(*request, isds_ns);
6106 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6107 err = insert_DbOwnerInfo(context, box, node);
6108 if (err) goto leave;
6110 /* Insert users */
6111 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6112 * verbose documentation allows none dbUserInfo */
6113 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6114 for (item = users; item; item = item->next) {
6115 if (item->data) {
6116 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6117 err = insert_DbUserInfo(context,
6118 (struct isds_DbUserInfo *) item->data, node);
6119 if (err) goto leave;
6123 INSERT_STRING(*request, "dbFormerNames", former_names);
6124 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6125 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6127 err = insert_credentials_delivery(context, credentials_delivery, *request);
6128 if (err) goto leave;
6130 err = insert_GExtApproval(context, approval, *request);
6131 if (err) goto leave;
6133 leave:
6134 if (err) {
6135 xmlFreeNode(*request);
6136 *request = NULL;
6138 free(string);
6139 return err;
6141 #endif /* HAVE_LIBCURL */
6144 /* Create new box.
6145 * @context is session context
6146 * @box is box description to create including single primary user (in case of
6147 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6148 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6149 * box, or contact address of PFO box owner)
6150 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6151 * @upper_box_id is optional ID of supper box if currently created box is
6152 * subordinated.
6153 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6154 * @credentials_delivery is NULL if new password should be delivered off-line
6155 * to box owner. It is valid pointer if owner should obtain new password on-line
6156 * on dedicated web server. Then input @credentials_delivery.email value is
6157 * his e-mail address he must provide to dedicated web server together
6158 * with output reallocated @credentials_delivery.token member. Output
6159 * member @credentials_delivery.new_user_name is unused up on this call.
6160 * @approval is optional external approval of box manipulation
6161 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6162 * NULL, if you don't care.*/
6163 isds_error isds_add_box(struct isds_ctx *context,
6164 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6165 const char *former_names, const char *upper_box_id,
6166 const char *ceo_label,
6167 struct isds_credentials_delivery *credentials_delivery,
6168 const struct isds_approval *approval, char **refnumber) {
6169 isds_error err = IE_SUCCESS;
6170 #if HAVE_LIBCURL
6171 xmlNodePtr request = NULL;
6172 xmlDocPtr response = NULL;
6173 xmlXPathContextPtr xpath_ctx = NULL;
6174 xmlXPathObjectPtr result = NULL;
6175 #endif
6178 if (!context) return IE_INVALID_CONTEXT;
6179 zfree(context->long_message);
6180 if (credentials_delivery) {
6181 zfree(credentials_delivery->token);
6182 zfree(credentials_delivery->new_user_name);
6184 if (!box) return IE_INVAL;
6186 #if HAVE_LIBCURL
6187 /* Scratch box ID */
6188 zfree(box->dbID);
6190 /* Build CreateDataBox request */
6191 err = build_CreateDBInput_request(context,
6192 &request, BAD_CAST "CreateDataBox",
6193 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6194 (xmlChar *) ceo_label, credentials_delivery, approval);
6195 if (err) goto leave;
6197 /* Send it to server and process response */
6198 err = send_destroy_request_check_response(context,
6199 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6200 &response, (xmlChar **) refnumber, NULL);
6202 /* Extract box ID */
6203 xpath_ctx = xmlXPathNewContext(response);
6204 if (!xpath_ctx) {
6205 err = IE_ERROR;
6206 goto leave;
6208 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6209 err = IE_ERROR;
6210 goto leave;
6212 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6214 /* Extract optional token */
6215 err = extract_credentials_delivery(context, credentials_delivery, response,
6216 "CreateDataBox");
6218 leave:
6219 xmlXPathFreeObject(result);
6220 xmlXPathFreeContext(xpath_ctx);
6221 xmlFreeDoc(response);
6222 xmlFreeNode(request);
6224 if (!err) {
6225 isds_log(ILF_ISDS, ILL_DEBUG,
6226 _("CreateDataBox request processed by server successfully.\n"));
6228 #else /* not HAVE_LIBCURL */
6229 err = IE_NOTSUP;
6230 #endif
6232 return err;
6236 /* Notify ISDS about new PFO entity.
6237 * This function has no real effect.
6238 * @context is session context
6239 * @box is PFO description including single primary user.
6240 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6241 * @former_names is optional undocumented string. Pass NULL if you don't care.
6242 * @upper_box_id is optional ID of supper box if currently created box is
6243 * subordinated.
6244 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6245 * @approval is optional external approval of box manipulation
6246 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6247 * NULL, if you don't care.*/
6248 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6249 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6250 const char *former_names, const char *upper_box_id,
6251 const char *ceo_label, const struct isds_approval *approval,
6252 char **refnumber) {
6253 isds_error err = IE_SUCCESS;
6254 #if HAVE_LIBCURL
6255 xmlNodePtr request = NULL;
6256 #endif
6258 if (!context) return IE_INVALID_CONTEXT;
6259 zfree(context->long_message);
6260 if (!box) return IE_INVAL;
6262 #if HAVE_LIBCURL
6263 /* Build CreateDataBoxPFOInfo request */
6264 err = build_CreateDBInput_request(context,
6265 &request, BAD_CAST "CreateDataBoxPFOInfo",
6266 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6267 (xmlChar *) ceo_label, NULL, approval);
6268 if (err) goto leave;
6270 /* Send it to server and process response */
6271 err = send_request_check_drop_response(context,
6272 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6273 (xmlChar **) refnumber);
6274 /* XXX: XML Schema names output dbID element but textual documentation
6275 * states no box identifier is returned. */
6276 leave:
6277 xmlFreeNode(request);
6278 #else /* not HAVE_LIBCURL */
6279 err = IE_NOTSUP;
6280 #endif
6281 return err;
6285 /* Common implementation for removing given box.
6286 * @context is session context
6287 * @service_name is UTF-8 encoded name fo ISDS service
6288 * @box is box description to delete
6289 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6290 * carry sane value. If NULL, do not inject this information into request.
6291 * @approval is optional external approval of box manipulation
6292 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6293 * NULL, if you don't care.*/
6294 isds_error _isds_delete_box_common(struct isds_ctx *context,
6295 const xmlChar *service_name,
6296 const struct isds_DbOwnerInfo *box, const struct tm *since,
6297 const struct isds_approval *approval, char **refnumber) {
6298 isds_error err = IE_SUCCESS;
6299 #if HAVE_LIBCURL
6300 xmlNsPtr isds_ns = NULL;
6301 xmlNodePtr request = NULL;
6302 xmlNodePtr node;
6303 xmlChar *string = NULL;
6304 #endif
6307 if (!context) return IE_INVALID_CONTEXT;
6308 zfree(context->long_message);
6309 if (!service_name || !*service_name || !box) return IE_INVAL;
6312 #if HAVE_LIBCURL
6313 /* Build DeleteDataBox(Promptly) request */
6314 request = xmlNewNode(NULL, service_name);
6315 if (!request) {
6316 char *service_name_locale = _isds_utf82locale((char*)service_name);
6317 isds_printf_message(context,
6318 _("Could build %s request"), service_name_locale);
6319 free(service_name_locale);
6320 return IE_ERROR;
6322 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6323 if(!isds_ns) {
6324 isds_log_message(context, _("Could not create ISDS name space"));
6325 xmlFreeNode(request);
6326 return IE_ERROR;
6328 xmlSetNs(request, isds_ns);
6330 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6331 err = insert_DbOwnerInfo(context, box, node);
6332 if (err) goto leave;
6334 if (since) {
6335 err = tm2datestring(since, &string);
6336 if (err) {
6337 isds_log_message(context,
6338 _("Could not convert `since' argument to ISO date string"));
6339 goto leave;
6341 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6342 zfree(string);
6345 err = insert_GExtApproval(context, approval, request);
6346 if (err) goto leave;
6349 /* Send it to server and process response */
6350 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6351 service_name, &request, (xmlChar **) refnumber);
6353 leave:
6354 xmlFreeNode(request);
6355 free(string);
6356 #else /* not HAVE_LIBCURL */
6357 err = IE_NOTSUP;
6358 #endif
6359 return err;
6363 /* Remove given box permanently.
6364 * @context is session context
6365 * @box is box description to delete
6366 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6367 * carry sane value.
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_delete_box(struct isds_ctx *context,
6372 const struct isds_DbOwnerInfo *box, const struct tm *since,
6373 const struct isds_approval *approval, char **refnumber) {
6374 if (!context) return IE_INVALID_CONTEXT;
6375 zfree(context->long_message);
6376 if (!box || !since) return IE_INVAL;
6378 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6379 box, since, approval, refnumber);
6383 /* Undocumented function.
6384 * @context is session context
6385 * @box is box description to delete
6386 * @approval is optional external approval of box manipulation
6387 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6388 * NULL, if you don't care.*/
6389 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6390 const struct isds_DbOwnerInfo *box,
6391 const struct isds_approval *approval, char **refnumber) {
6392 if (!context) return IE_INVALID_CONTEXT;
6393 zfree(context->long_message);
6394 if (!box) return IE_INVAL;
6396 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6397 box, NULL, approval, refnumber);
6401 /* Update data about given box.
6402 * @context is session context
6403 * @old_box current box description
6404 * @new_box are updated data about @old_box
6405 * @approval is optional external approval of box manipulation
6406 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6407 * NULL, if you don't care.*/
6408 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6409 const struct isds_DbOwnerInfo *old_box,
6410 const struct isds_DbOwnerInfo *new_box,
6411 const struct isds_approval *approval, char **refnumber) {
6412 isds_error err = IE_SUCCESS;
6413 #if HAVE_LIBCURL
6414 xmlNsPtr isds_ns = NULL;
6415 xmlNodePtr request = NULL;
6416 xmlNodePtr node;
6417 #endif
6420 if (!context) return IE_INVALID_CONTEXT;
6421 zfree(context->long_message);
6422 if (!old_box || !new_box) return IE_INVAL;
6425 #if HAVE_LIBCURL
6426 /* Build UpdateDataBoxDescr request */
6427 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6428 if (!request) {
6429 isds_log_message(context,
6430 _("Could build UpdateDataBoxDescr request"));
6431 return IE_ERROR;
6433 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6434 if(!isds_ns) {
6435 isds_log_message(context, _("Could not create ISDS name space"));
6436 xmlFreeNode(request);
6437 return IE_ERROR;
6439 xmlSetNs(request, isds_ns);
6441 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6442 err = insert_DbOwnerInfo(context, old_box, node);
6443 if (err) goto leave;
6445 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6446 err = insert_DbOwnerInfo(context, new_box, node);
6447 if (err) goto leave;
6449 err = insert_GExtApproval(context, approval, request);
6450 if (err) goto leave;
6453 /* Send it to server and process response */
6454 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6455 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6457 leave:
6458 xmlFreeNode(request);
6459 #else /* not HAVE_LIBCURL */
6460 err = IE_NOTSUP;
6461 #endif
6463 return err;
6467 #if HAVE_LIBCURL
6468 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6469 * code
6470 * @context is session context
6471 * @service is SOAP service
6472 * @service_name is name of request in @service
6473 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6474 * @box_id is box ID of interest
6475 * @approval is optional external approval of box manipulation
6476 * @response is server SOAP body response as XML document
6477 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6478 * NULL, if you don't care.
6479 * @return error coded from lower layer, context message will be set up
6480 * appropriately. */
6481 static isds_error build_send_dbid_request_check_response(
6482 struct isds_ctx *context, const isds_service service,
6483 const xmlChar *service_name, const xmlChar *box_id_element,
6484 const xmlChar *box_id, const struct isds_approval *approval,
6485 xmlDocPtr *response, xmlChar **refnumber) {
6487 isds_error err = IE_SUCCESS;
6488 char *service_name_locale = NULL, *box_id_locale = NULL;
6489 xmlNodePtr request = NULL, node;
6490 xmlNsPtr isds_ns = NULL;
6492 if (!context) return IE_INVALID_CONTEXT;
6493 if (!service_name || !box_id) return IE_INVAL;
6494 if (!response) return IE_INVAL;
6496 /* Free output argument */
6497 xmlFreeDoc(*response); *response = NULL;
6499 /* Prepare strings */
6500 service_name_locale = _isds_utf82locale((char*)service_name);
6501 if (!service_name_locale) {
6502 err = IE_NOMEM;
6503 goto leave;
6505 box_id_locale = _isds_utf82locale((char*)box_id);
6506 if (!box_id_locale) {
6507 err = IE_NOMEM;
6508 goto leave;
6511 /* Build request */
6512 request = xmlNewNode(NULL, service_name);
6513 if (!request) {
6514 isds_printf_message(context,
6515 _("Could not build %s request for %s box"), service_name_locale,
6516 box_id_locale);
6517 err = IE_ERROR;
6518 goto leave;
6520 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6521 if(!isds_ns) {
6522 isds_log_message(context, _("Could not create ISDS name space"));
6523 err = IE_ERROR;
6524 goto leave;
6526 xmlSetNs(request, isds_ns);
6528 /* Add XSD:tIdDbInput children */
6529 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6530 INSERT_STRING(request, box_id_element, box_id);
6531 err = insert_GExtApproval(context, approval, request);
6532 if (err) goto leave;
6534 /* Send request and check response*/
6535 err = send_destroy_request_check_response(context,
6536 service, service_name, &request, response, refnumber, NULL);
6538 leave:
6539 free(service_name_locale);
6540 free(box_id_locale);
6541 xmlFreeNode(request);
6542 return err;
6544 #endif /* HAVE_LIBCURL */
6547 /* Get data about all users assigned to given box.
6548 * @context is session context
6549 * @box_id is box ID
6550 * @users is automatically reallocated list of struct isds_DbUserInfo */
6551 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6552 struct isds_list **users) {
6553 isds_error err = IE_SUCCESS;
6554 #if HAVE_LIBCURL
6555 xmlDocPtr response = NULL;
6556 xmlXPathContextPtr xpath_ctx = NULL;
6557 xmlXPathObjectPtr result = NULL;
6558 int i;
6559 struct isds_list *item, *prev_item = NULL;
6560 #endif
6562 if (!context) return IE_INVALID_CONTEXT;
6563 zfree(context->long_message);
6564 if (!users || !box_id) return IE_INVAL;
6565 isds_list_free(users);
6568 #if HAVE_LIBCURL
6569 /* Do request and check for success */
6570 err = build_send_dbid_request_check_response(context,
6571 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6572 BAD_CAST box_id, NULL, &response, NULL);
6573 if (err) goto leave;
6576 /* Extract data */
6577 /* Prepare structure */
6578 xpath_ctx = xmlXPathNewContext(response);
6579 if (!xpath_ctx) {
6580 err = IE_ERROR;
6581 goto leave;
6583 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6584 err = IE_ERROR;
6585 goto leave;
6588 /* Set context node */
6589 result = xmlXPathEvalExpression(BAD_CAST
6590 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6591 xpath_ctx);
6592 if (!result) {
6593 err = IE_ERROR;
6594 goto leave;
6596 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6597 /* Iterate over all users */
6598 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6600 /* Prepare structure */
6601 item = calloc(1, sizeof(*item));
6602 if (!item) {
6603 err = IE_NOMEM;
6604 goto leave;
6606 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6607 if (i == 0) *users = item;
6608 else prev_item->next = item;
6609 prev_item = item;
6611 /* Extract it */
6612 xpath_ctx->node = result->nodesetval->nodeTab[i];
6613 err = extract_DbUserInfo(context,
6614 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6615 if (err) goto leave;
6619 leave:
6620 if (err) {
6621 isds_list_free(users);
6624 xmlXPathFreeObject(result);
6625 xmlXPathFreeContext(xpath_ctx);
6626 xmlFreeDoc(response);
6628 if (!err)
6629 isds_log(ILF_ISDS, ILL_DEBUG,
6630 _("GetDataBoxUsers request processed by server "
6631 "successfully.\n"));
6632 #else /* not HAVE_LIBCURL */
6633 err = IE_NOTSUP;
6634 #endif
6636 return err;
6640 /* Update data about user assigned to given box.
6641 * @context is session context
6642 * @box is box identification
6643 * @old_user identifies user to update
6644 * @new_user are updated data about @old_user
6645 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6646 * NULL, if you don't care.*/
6647 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6648 const struct isds_DbOwnerInfo *box,
6649 const struct isds_DbUserInfo *old_user,
6650 const struct isds_DbUserInfo *new_user,
6651 char **refnumber) {
6652 isds_error err = IE_SUCCESS;
6653 #if HAVE_LIBCURL
6654 xmlNsPtr isds_ns = NULL;
6655 xmlNodePtr request = NULL;
6656 xmlNodePtr node;
6657 #endif
6660 if (!context) return IE_INVALID_CONTEXT;
6661 zfree(context->long_message);
6662 if (!box || !old_user || !new_user) return IE_INVAL;
6665 #if HAVE_LIBCURL
6666 /* Build UpdateDataBoxUser request */
6667 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6668 if (!request) {
6669 isds_log_message(context,
6670 _("Could build UpdateDataBoxUser request"));
6671 return IE_ERROR;
6673 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6674 if(!isds_ns) {
6675 isds_log_message(context, _("Could not create ISDS name space"));
6676 xmlFreeNode(request);
6677 return IE_ERROR;
6679 xmlSetNs(request, isds_ns);
6681 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6682 err = insert_DbOwnerInfo(context, box, node);
6683 if (err) goto leave;
6685 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6686 err = insert_DbUserInfo(context, old_user, node);
6687 if (err) goto leave;
6689 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6690 err = insert_DbUserInfo(context, new_user, node);
6691 if (err) goto leave;
6693 /* Send it to server and process response */
6694 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6695 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6697 leave:
6698 xmlFreeNode(request);
6699 #else /* not HAVE_LIBCURL */
6700 err = IE_NOTSUP;
6701 #endif
6703 return err;
6707 /* Undocumented function.
6708 * @context is session context
6709 * @box_id is UTF-8 encoded box identifier
6710 * @token is UTF-8 encoded temporary password
6711 * @user_id outputs UTF-8 encoded reallocated user identifier
6712 * @password outpus UTF-8 encoded reallocated user password
6713 * Output arguments will be nulled in case of error */
6714 isds_error isds_activate(struct isds_ctx *context,
6715 const char *box_id, const char *token,
6716 char **user_id, char **password) {
6717 isds_error err = IE_SUCCESS;
6718 #if HAVE_LIBCURL
6719 xmlNsPtr isds_ns = NULL;
6720 xmlNodePtr request = NULL, node;
6721 xmlDocPtr response = NULL;
6722 xmlXPathContextPtr xpath_ctx = NULL;
6723 xmlXPathObjectPtr result = NULL;
6724 #endif
6727 if (!context) return IE_INVALID_CONTEXT;
6728 zfree(context->long_message);
6730 if (user_id) zfree(*user_id);
6731 if (password) zfree(*password);
6733 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6736 #if HAVE_LIBCURL
6737 /* Build Activate request */
6738 request = xmlNewNode(NULL, BAD_CAST "Activate");
6739 if (!request) {
6740 isds_log_message(context, _("Could build Activate request"));
6741 return IE_ERROR;
6743 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6744 if(!isds_ns) {
6745 isds_log_message(context, _("Could not create ISDS name space"));
6746 xmlFreeNode(request);
6747 return IE_ERROR;
6749 xmlSetNs(request, isds_ns);
6751 INSERT_STRING(request, "dbAccessDataId", token);
6752 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6753 INSERT_STRING(request, "dbID", box_id);
6756 /* Send request and check response*/
6757 err = send_destroy_request_check_response(context,
6758 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6759 &response, NULL, NULL);
6760 if (err) goto leave;
6763 /* Extract data */
6764 xpath_ctx = xmlXPathNewContext(response);
6765 if (!xpath_ctx) {
6766 err = IE_ERROR;
6767 goto leave;
6769 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6770 err = IE_ERROR;
6771 goto leave;
6773 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6774 xpath_ctx);
6775 if (!result) {
6776 err = IE_ERROR;
6777 goto leave;
6779 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6780 isds_log_message(context, _("Missing ActivateResponse element"));
6781 err = IE_ISDS;
6782 goto leave;
6784 if (result->nodesetval->nodeNr > 1) {
6785 isds_log_message(context, _("Multiple ActivateResponse element"));
6786 err = IE_ISDS;
6787 goto leave;
6789 xpath_ctx->node = result->nodesetval->nodeTab[0];
6790 xmlXPathFreeObject(result); result = NULL;
6792 EXTRACT_STRING("isds:userId", *user_id);
6793 if (!*user_id)
6794 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6795 "but did not return `userId' element.\n"));
6797 EXTRACT_STRING("isds:password", *password);
6798 if (!*password)
6799 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6800 "but did not return `password' element.\n"));
6802 leave:
6803 xmlXPathFreeObject(result);
6804 xmlXPathFreeContext(xpath_ctx);
6805 xmlFreeDoc(response);
6806 xmlFreeNode(request);
6808 if (!err)
6809 isds_log(ILF_ISDS, ILL_DEBUG,
6810 _("Activate request processed by server successfully.\n"));
6811 #else /* not HAVE_LIBCURL */
6812 err = IE_NOTSUP;
6813 #endif
6815 return err;
6819 /* Reset credentials of user assigned to given box.
6820 * @context is session context
6821 * @box is box identification
6822 * @user identifies user to reset password
6823 * @fee_paid is true if fee has been paid, false otherwise
6824 * @approval is optional external approval of box manipulation
6825 * @credentials_delivery is NULL if new password should be delivered off-line
6826 * to the user. It is valid pointer if user should obtain new password on-line
6827 * on dedicated web server. Then input @credentials_delivery.email value is
6828 * user's e-mail address user must provide to dedicated web server together
6829 * with @credentials_delivery.token. The output reallocated token user needs
6830 * to use to authorize on the web server to view his new password. Output
6831 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6832 * ISDS changed up on this call. (No reason why server could change the name
6833 * is known now.)
6834 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6835 * NULL, if you don't care.*/
6836 isds_error isds_reset_password(struct isds_ctx *context,
6837 const struct isds_DbOwnerInfo *box,
6838 const struct isds_DbUserInfo *user,
6839 const _Bool fee_paid, const struct isds_approval *approval,
6840 struct isds_credentials_delivery *credentials_delivery,
6841 char **refnumber) {
6842 isds_error err = IE_SUCCESS;
6843 #if HAVE_LIBCURL
6844 xmlNsPtr isds_ns = NULL;
6845 xmlNodePtr request = NULL, node;
6846 xmlDocPtr response = NULL;
6847 #endif
6850 if (!context) return IE_INVALID_CONTEXT;
6851 zfree(context->long_message);
6853 if (credentials_delivery) {
6854 zfree(credentials_delivery->token);
6855 zfree(credentials_delivery->new_user_name);
6857 if (!box || !user) return IE_INVAL;
6860 #if HAVE_LIBCURL
6861 /* Build NewAccessData request */
6862 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6863 if (!request) {
6864 isds_log_message(context,
6865 _("Could build NewAccessData request"));
6866 return IE_ERROR;
6868 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6869 if(!isds_ns) {
6870 isds_log_message(context, _("Could not create ISDS name space"));
6871 xmlFreeNode(request);
6872 return IE_ERROR;
6874 xmlSetNs(request, isds_ns);
6876 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6877 err = insert_DbOwnerInfo(context, box, node);
6878 if (err) goto leave;
6880 INSERT_ELEMENT(node, request, "dbUserInfo");
6881 err = insert_DbUserInfo(context, user, node);
6882 if (err) goto leave;
6884 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6886 err = insert_credentials_delivery(context, credentials_delivery, request);
6887 if (err) goto leave;
6889 err = insert_GExtApproval(context, approval, request);
6890 if (err) goto leave;
6892 /* Send request and check response*/
6893 err = send_destroy_request_check_response(context,
6894 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6895 &response, (xmlChar **) refnumber, NULL);
6896 if (err) goto leave;
6899 /* Extract optional token */
6900 err = extract_credentials_delivery(context, credentials_delivery,
6901 response, "NewAccessData");
6903 leave:
6904 xmlFreeDoc(response);
6905 xmlFreeNode(request);
6907 if (!err)
6908 isds_log(ILF_ISDS, ILL_DEBUG,
6909 _("NewAccessData request processed by server "
6910 "successfully.\n"));
6911 #else /* not HAVE_LIBCURL */
6912 err = IE_NOTSUP;
6913 #endif
6915 return err;
6919 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6920 * code, destroy response and log success.
6921 * @context is ISDS session context.
6922 * @service_name is name of SERVICE_DB_MANIPULATION service
6923 * @box is box identification
6924 * @user identifies user to remove
6925 * @credentials_delivery is NULL if new user's password should be delivered
6926 * off-line to the user. It is valid pointer if user should obtain new
6927 * password on-line on dedicated web server. Then input
6928 * @credentials_delivery.email value is user's e-mail address user must
6929 * provide to dedicated web server together with @credentials_delivery.token.
6930 * The output reallocated token user needs to use to authorize on the web
6931 * server to view his new password. Output reallocated
6932 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6933 * assingned or changed up on this call.
6934 * @approval is optional external approval of box manipulation
6935 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6936 * NULL, if you don't care. */
6937 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6938 struct isds_ctx *context, const xmlChar *service_name,
6939 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6940 struct isds_credentials_delivery *credentials_delivery,
6941 const struct isds_approval *approval, xmlChar **refnumber) {
6942 isds_error err = IE_SUCCESS;
6943 #if HAVE_LIBCURL
6944 xmlNsPtr isds_ns = NULL;
6945 xmlNodePtr request = NULL, node;
6946 xmlDocPtr response = NULL;
6947 #endif
6950 if (!context) return IE_INVALID_CONTEXT;
6951 zfree(context->long_message);
6952 if (credentials_delivery) {
6953 zfree(credentials_delivery->token);
6954 zfree(credentials_delivery->new_user_name);
6956 if (!service_name || service_name[0] == '\0' || !box || !user)
6957 return IE_INVAL;
6960 #if HAVE_LIBCURL
6961 /* Build NewAccessData or similar request */
6962 request = xmlNewNode(NULL, service_name);
6963 if (!request) {
6964 char *service_name_locale = _isds_utf82locale((char *) service_name);
6965 isds_printf_message(context, _("Could not build %s request"),
6966 service_name_locale);
6967 free(service_name_locale);
6968 return IE_ERROR;
6970 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6971 if(!isds_ns) {
6972 isds_log_message(context, _("Could not create ISDS name space"));
6973 xmlFreeNode(request);
6974 return IE_ERROR;
6976 xmlSetNs(request, isds_ns);
6978 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6979 err = insert_DbOwnerInfo(context, box, node);
6980 if (err) goto leave;
6982 INSERT_ELEMENT(node, request, "dbUserInfo");
6983 err = insert_DbUserInfo(context, user, node);
6984 if (err) goto leave;
6986 err = insert_credentials_delivery(context, credentials_delivery, request);
6987 if (err) goto leave;
6989 err = insert_GExtApproval(context, approval, request);
6990 if (err) goto leave;
6993 /* Send request and check response*/
6994 err = send_destroy_request_check_response(context,
6995 SERVICE_DB_MANIPULATION, service_name, &request, &response,
6996 refnumber, NULL);
6998 xmlFreeNode(request);
6999 request = NULL;
7001 /* Pick up credentials_delivery if requested */
7002 err = extract_credentials_delivery(context, credentials_delivery, response,
7003 (char *)service_name);
7005 leave:
7006 xmlFreeDoc(response);
7007 if (request) xmlFreeNode(request);
7009 if (!err) {
7010 char *service_name_locale = _isds_utf82locale((char *) service_name);
7011 isds_log(ILF_ISDS, ILL_DEBUG,
7012 _("%s request processed by server successfully.\n"),
7013 service_name_locale);
7014 free(service_name_locale);
7016 #else /* not HAVE_LIBCURL */
7017 err = IE_NOTSUP;
7018 #endif
7020 return err;
7024 /* Assign new user to given box.
7025 * @context is session context
7026 * @box is box identification
7027 * @user defines new user to add
7028 * @credentials_delivery is NULL if new user's password should be delivered
7029 * off-line to the user. It is valid pointer if user should obtain new
7030 * password on-line on dedicated web server. Then input
7031 * @credentials_delivery.email value is user's e-mail address user must
7032 * provide to dedicated web server together with @credentials_delivery.token.
7033 * The output reallocated token user needs to use to authorize on the web
7034 * server to view his new password. Output reallocated
7035 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7036 * assingned up on this call.
7037 * @approval is optional external approval of box manipulation
7038 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7039 * NULL, if you don't care.*/
7040 isds_error isds_add_user(struct isds_ctx *context,
7041 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7042 struct isds_credentials_delivery *credentials_delivery,
7043 const struct isds_approval *approval, char **refnumber) {
7044 return build_send_manipulationboxuser_request_check_drop_response(context,
7045 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
7046 approval, (xmlChar **) refnumber);
7050 /* Remove user assigned to given box.
7051 * @context is session context
7052 * @box is box identification
7053 * @user identifies user to remove
7054 * @approval is optional external approval of box manipulation
7055 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7056 * NULL, if you don't care.*/
7057 isds_error isds_delete_user(struct isds_ctx *context,
7058 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7059 const struct isds_approval *approval, char **refnumber) {
7060 return build_send_manipulationboxuser_request_check_drop_response(context,
7061 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
7062 (xmlChar **) refnumber);
7066 /* Get list of boxes in ZIP archive.
7067 * @context is session context
7068 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7069 * System recognizes following values currently: ALL (all boxes), UPG
7070 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7071 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7072 * commercial messages). This argument is a string because specification
7073 * states new values can appear in the future. Not all list types are
7074 * available to all users.
7075 * @buffer is automatically reallocated memory to store the list of boxes. The
7076 * list is zipped CSV file.
7077 * @buffer_length is size of @buffer data in bytes.
7078 * In case of error @buffer will be freed and @buffer_length will be
7079 * undefined.*/
7080 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7081 const char *list_identifier, void **buffer, size_t *buffer_length) {
7082 isds_error err = IE_SUCCESS;
7083 #if HAVE_LIBCURL
7084 xmlNsPtr isds_ns = NULL;
7085 xmlNodePtr request = NULL, node;
7086 xmlDocPtr response = NULL;
7087 xmlXPathContextPtr xpath_ctx = NULL;
7088 xmlXPathObjectPtr result = NULL;
7089 char *string = NULL;
7090 #endif
7093 if (!context) return IE_INVALID_CONTEXT;
7094 zfree(context->long_message);
7095 if (buffer) zfree(*buffer);
7096 if (!buffer || !buffer_length) return IE_INVAL;
7099 #if HAVE_LIBCURL
7100 /* Check if connection is established
7101 * TODO: This check should be done downstairs. */
7102 if (!context->curl) return IE_CONNECTION_CLOSED;
7105 /* Build AuthenticateMessage request */
7106 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7107 if (!request) {
7108 isds_log_message(context,
7109 _("Could not build GetDataBoxList request"));
7110 return IE_ERROR;
7112 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7113 if(!isds_ns) {
7114 isds_log_message(context, _("Could not create ISDS name space"));
7115 xmlFreeNode(request);
7116 return IE_ERROR;
7118 xmlSetNs(request, isds_ns);
7119 INSERT_STRING(request, "dblType", list_identifier);
7121 /* Send request to server and process response */
7122 err = send_destroy_request_check_response(context,
7123 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7124 &response, NULL, NULL);
7125 if (err) goto leave;
7128 /* Extract Base-64 encoded ZIP file */
7129 xpath_ctx = xmlXPathNewContext(response);
7130 if (!xpath_ctx) {
7131 err = IE_ERROR;
7132 goto leave;
7134 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7135 err = IE_ERROR;
7136 goto leave;
7138 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7140 /* Decode non-empty archive */
7141 if (string && string[0] != '\0') {
7142 *buffer_length = _isds_b64decode(string, buffer);
7143 if (*buffer_length == (size_t) -1) {
7144 isds_printf_message(context,
7145 _("Error while Base64-decoding box list archive"));
7146 err = IE_ERROR;
7147 goto leave;
7152 leave:
7153 free(string);
7154 xmlXPathFreeObject(result);
7155 xmlXPathFreeContext(xpath_ctx);
7156 xmlFreeDoc(response);
7157 xmlFreeNode(request);
7159 if (!err) {
7160 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7161 "processed by server successfully.\n"));
7163 #else /* not HAVE_LIBCURL */
7164 err = IE_NOTSUP;
7165 #endif
7167 return err;
7171 /* Find boxes suiting given criteria.
7172 * @criteria is filter. You should fill in at least some members.
7173 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7174 * possibly empty. Input NULL or valid old structure.
7175 * @return:
7176 * IE_SUCCESS if search succeeded, @boxes contains useful data
7177 * IE_NOEXIST if no such box exists, @boxes will be NULL
7178 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7179 * contains still valid data
7180 * other code if something bad happens. @boxes will be NULL. */
7181 isds_error isds_FindDataBox(struct isds_ctx *context,
7182 const struct isds_DbOwnerInfo *criteria,
7183 struct isds_list **boxes) {
7184 isds_error err = IE_SUCCESS;
7185 #if HAVE_LIBCURL
7186 _Bool truncated = 0;
7187 xmlNsPtr isds_ns = NULL;
7188 xmlNodePtr request = NULL;
7189 xmlDocPtr response = NULL;
7190 xmlChar *code = NULL, *message = NULL;
7191 xmlNodePtr db_owner_info;
7192 xmlXPathContextPtr xpath_ctx = NULL;
7193 xmlXPathObjectPtr result = NULL;
7194 xmlChar *string = NULL;
7195 #endif
7198 if (!context) return IE_INVALID_CONTEXT;
7199 zfree(context->long_message);
7200 if (!boxes) return IE_INVAL;
7201 isds_list_free(boxes);
7203 if (!criteria) {
7204 return IE_INVAL;
7207 #if HAVE_LIBCURL
7208 /* Check if connection is established
7209 * TODO: This check should be done downstairs. */
7210 if (!context->curl) return IE_CONNECTION_CLOSED;
7213 /* Build FindDataBox request */
7214 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
7215 if (!request) {
7216 isds_log_message(context,
7217 _("Could build FindDataBox request"));
7218 return IE_ERROR;
7220 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7221 if(!isds_ns) {
7222 isds_log_message(context, _("Could not create ISDS name space"));
7223 xmlFreeNode(request);
7224 return IE_ERROR;
7226 xmlSetNs(request, isds_ns);
7227 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7228 if (!db_owner_info) {
7229 isds_log_message(context, _("Could not add dbOwnerInfo child to "
7230 "FindDataBox element"));
7231 xmlFreeNode(request);
7232 return IE_ERROR;
7235 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
7236 if (err) goto leave;
7239 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
7241 /* Sent request */
7242 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7244 /* Destroy request */
7245 xmlFreeNode(request); request = NULL;
7247 if (err) {
7248 isds_log(ILF_ISDS, ILL_DEBUG,
7249 _("Processing ISDS response on FindDataBox "
7250 "request failed\n"));
7251 goto leave;
7254 /* Check for response status */
7255 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7256 &code, &message, NULL);
7257 if (err) {
7258 isds_log(ILF_ISDS, ILL_DEBUG,
7259 _("ISDS response on FindDataBox request is missing status\n"));
7260 goto leave;
7263 /* Request processed, but nothing found */
7264 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7265 !xmlStrcmp(code, BAD_CAST "5001")) {
7266 char *code_locale = _isds_utf82locale((char*)code);
7267 char *message_locale = _isds_utf82locale((char*)message);
7268 isds_log(ILF_ISDS, ILL_DEBUG,
7269 _("Server did not found any box on FindDataBox request "
7270 "(code=%s, message=%s)\n"), code_locale, message_locale);
7271 isds_log_message(context, message_locale);
7272 free(code_locale);
7273 free(message_locale);
7274 err = IE_NOEXIST;
7275 goto leave;
7278 /* Warning, not a error */
7279 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7280 char *code_locale = _isds_utf82locale((char*)code);
7281 char *message_locale = _isds_utf82locale((char*)message);
7282 isds_log(ILF_ISDS, ILL_DEBUG,
7283 _("Server truncated response on FindDataBox request "
7284 "(code=%s, message=%s)\n"), code_locale, message_locale);
7285 isds_log_message(context, message_locale);
7286 free(code_locale);
7287 free(message_locale);
7288 truncated = 1;
7291 /* Other error */
7292 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7293 char *code_locale = _isds_utf82locale((char*)code);
7294 char *message_locale = _isds_utf82locale((char*)message);
7295 isds_log(ILF_ISDS, ILL_DEBUG,
7296 _("Server refused FindDataBox request "
7297 "(code=%s, message=%s)\n"), code_locale, message_locale);
7298 isds_log_message(context, message_locale);
7299 free(code_locale);
7300 free(message_locale);
7301 err = IE_ISDS;
7302 goto leave;
7305 xpath_ctx = xmlXPathNewContext(response);
7306 if (!xpath_ctx) {
7307 err = IE_ERROR;
7308 goto leave;
7310 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7311 err = IE_ERROR;
7312 goto leave;
7315 /* Extract boxes if they present */
7316 result = xmlXPathEvalExpression(BAD_CAST
7317 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7318 xpath_ctx);
7319 if (!result) {
7320 err = IE_ERROR;
7321 goto leave;
7323 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7324 struct isds_list *item, *prev_item = NULL;
7325 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7326 item = calloc(1, sizeof(*item));
7327 if (!item) {
7328 err = IE_NOMEM;
7329 goto leave;
7332 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7333 if (i == 0) *boxes = item;
7334 else prev_item->next = item;
7335 prev_item = item;
7337 xpath_ctx->node = result->nodesetval->nodeTab[i];
7338 err = extract_DbOwnerInfo(context,
7339 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7340 if (err) goto leave;
7344 leave:
7345 if (err) {
7346 isds_list_free(boxes);
7347 } else {
7348 if (truncated) err = IE_2BIG;
7351 free(string);
7352 xmlFreeNode(request);
7353 xmlXPathFreeObject(result);
7354 xmlXPathFreeContext(xpath_ctx);
7356 free(code);
7357 free(message);
7358 xmlFreeDoc(response);
7360 if (!err)
7361 isds_log(ILF_ISDS, ILL_DEBUG,
7362 _("FindDataBox request processed by server successfully.\n"));
7363 #else /* not HAVE_LIBCURL */
7364 err = IE_NOTSUP;
7365 #endif
7367 return err;
7371 #if HAVE_LIBCURL
7372 /* Convert a string with match markers into a plain string with list of
7373 * pointers to the matches
7374 * @string is an UTF-8 encoded non-constant string with match markers
7375 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7376 * The markers will be removed from the string.
7377 * @starts is a reallocated list of static pointers into the @string pointing
7378 * to places where match start markers occured.
7379 * @ends is a reallocated list of static pointers into the @string pointing
7380 * to places where match end markers occured.
7381 * @return IE_SUCCESS in case of no failure. */
7382 static isds_error interpret_matches(xmlChar *string,
7383 struct isds_list **starts, struct isds_list **ends) {
7384 isds_error err = IE_SUCCESS;
7385 xmlChar *pointer, *destination, *source;
7386 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7388 isds_list_free(starts);
7389 isds_list_free(ends);
7390 if (NULL == starts || NULL == ends) return IE_INVAL;
7391 if (NULL == string) return IE_SUCCESS;
7393 for (pointer = string; *pointer != '\0';) {
7394 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7395 /* Remove the start marker */
7396 for (source = pointer + 14, destination = pointer;
7397 *source != '\0'; source++, destination++) {
7398 *destination = *source;
7400 *destination = '\0';
7401 /* Append the pointer into the list */
7402 item = calloc(1, sizeof(*item));
7403 if (!item) {
7404 err = IE_NOMEM;
7405 goto leave;
7407 item->destructor = (void (*)(void **))NULL;
7408 item->data = pointer;
7409 if (NULL == prev_start) *starts = item;
7410 else prev_start->next = item;
7411 prev_start = item;
7412 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7413 /* Remove the end marker */
7414 for (source = pointer + 12, destination = pointer;
7415 *source != '\0'; source++, destination++) {
7416 *destination = *source;
7418 *destination = '\0';
7419 /* Append the pointer into the list */
7420 item = calloc(1, sizeof(*item));
7421 if (!item) {
7422 err = IE_NOMEM;
7423 goto leave;
7425 item->destructor = (void (*)(void **))NULL;
7426 item->data = pointer;
7427 if (NULL == prev_end) *ends = item;
7428 else prev_end->next = item;
7429 prev_end = item;
7430 } else {
7431 pointer++;
7435 leave:
7436 if (err) {
7437 isds_list_free(starts);
7438 isds_list_free(ends);
7440 return err;
7444 /* Convert isds:dbResult XML tree into structure
7445 * @context is ISDS context.
7446 * @fulltext_result is automatically reallocated found box structure.
7447 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7448 * @collect_matches is true to interpret match markers.
7449 * In case of error @result will be freed. */
7450 static isds_error extract_dbResult(struct isds_ctx *context,
7451 struct isds_fulltext_result **fulltext_result,
7452 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7453 isds_error err = IE_SUCCESS;
7454 xmlXPathObjectPtr result = NULL;
7455 char *string = NULL;
7457 if (NULL == context) return IE_INVALID_CONTEXT;
7458 if (NULL == fulltext_result) return IE_INVAL;
7459 isds_fulltext_result_free(fulltext_result);
7460 if (!xpath_ctx) return IE_INVAL;
7463 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7464 if (NULL == *fulltext_result) {
7465 err = IE_NOMEM;
7466 goto leave;
7469 /* Extract data */
7470 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7472 EXTRACT_STRING("isds:dbType", string);
7473 if (NULL == string) {
7474 err = IE_ISDS;
7475 isds_log_message(context, _("Empty isds:dbType element"));
7476 goto leave;
7478 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7479 if (err) {
7480 if (err == IE_ENUM) {
7481 err = IE_ISDS;
7482 char *string_locale = _isds_utf82locale(string);
7483 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7484 string_locale);
7485 free(string_locale);
7487 goto leave;
7489 zfree(string);
7491 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7492 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7494 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7495 if (err) goto leave;
7497 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7498 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7499 (*fulltext_result)->dbEffectiveOVM);
7501 EXTRACT_STRING("isds:dbSendOptions", string);
7502 if (NULL == string) {
7503 err = IE_ISDS;
7504 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7505 goto leave;
7507 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7508 (*fulltext_result)->active = 1;
7509 (*fulltext_result)->public_sending = 1;
7510 (*fulltext_result)->commercial_sending = 0;
7511 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7512 (*fulltext_result)->active = 1;
7513 (*fulltext_result)->public_sending = 1;
7514 (*fulltext_result)->commercial_sending = 1;
7515 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7516 (*fulltext_result)->active = 1;
7517 (*fulltext_result)->public_sending = 0;
7518 (*fulltext_result)->commercial_sending = 1;
7519 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7520 (*fulltext_result)->active = 1;
7521 (*fulltext_result)->public_sending = 0;
7522 (*fulltext_result)->commercial_sending = 0;
7523 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7524 (*fulltext_result)->active = 0;
7525 (*fulltext_result)->public_sending = 0;
7526 (*fulltext_result)->commercial_sending = 0;
7527 } else {
7528 err = IE_ISDS;
7529 char *string_locale = _isds_utf82locale(string);
7530 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7531 string_locale);
7532 free(string_locale);
7533 goto leave;
7535 zfree(string);
7537 /* Interpret match marks */
7538 if (collect_matches) {
7539 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7540 &((*fulltext_result)->name_match_start),
7541 &((*fulltext_result)->name_match_end));
7542 if (err) goto leave;
7543 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7544 &((*fulltext_result)->address_match_start),
7545 &((*fulltext_result)->address_match_end));
7546 if (err) goto leave;
7549 leave:
7550 if (err) isds_fulltext_result_free(fulltext_result);
7551 free(string);
7552 xmlXPathFreeObject(result);
7553 return err;
7555 #endif /* HAVE_LIBCURL */
7558 /* Find boxes matching a given full-text criteria.
7559 * @context is a session context
7560 * @query is a non-empty string which consists of words to search
7561 * @target selects box attributes to search for @query words. Pass NULL if you
7562 * don't care.
7563 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7564 * to search in all box types. Pass NULL to let server to use default value
7565 * which is DBTYPE_SYSTEM.
7566 * @page_size defines count of boxes to constitute a response page. It counts
7567 * from zero. Pass NULL to let server to use a default value (50 now).
7568 * @page_number defines ordinar number of the response page to return. It
7569 * counts from zero. Pass NULL to let server to use a default value (0 now).
7570 * @track_matches points to true for marking @query words found in the box
7571 * attributes. It points to false for not marking. Pass NULL to let the server
7572 * to use default value (false now).
7573 * @total_matching_boxes outputs reallocated number of all boxes matching the
7574 * query. Will be pointer to NULL if server did not provide the value.
7575 * Pass NULL if you don't care.
7576 * @current_page_beginning outputs reallocated ordinar number of the first box
7577 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7578 * server did not provide the value. Pass NULL if you don't care.
7579 * @current_page_size outputs reallocated count of boxes in the this @boxes
7580 * page. It will be pointer to NULL if the server did not provide the value.
7581 * Pass NULL if you don't care.
7582 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7583 * is the last one, false if more boxes match, NULL if the server did not
7584 * provude the value. Pass NULL if you don't care.
7585 * @boxes outputs reallocated list of isds_fulltext_result structures,
7586 * possibly empty.
7587 * @return:
7588 * IE_SUCCESS if search succeeded
7589 * IE_2BIG if @page_size is too large
7590 * other code if something bad happens; output arguments will be NULL. */
7591 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7592 const char *query,
7593 const isds_fulltext_target *target,
7594 const isds_DbType *box_type,
7595 const unsigned long int *page_size,
7596 const unsigned long int *page_number,
7597 const _Bool *track_matches,
7598 unsigned long int **total_matching_boxes,
7599 unsigned long int **current_page_beginning,
7600 unsigned long int **current_page_size,
7601 _Bool **last_page,
7602 struct isds_list **boxes) {
7603 isds_error err = IE_SUCCESS;
7604 #if HAVE_LIBCURL
7605 xmlNsPtr isds_ns = NULL;
7606 xmlNodePtr request = NULL;
7607 xmlDocPtr response = NULL;
7608 xmlNodePtr node;
7609 xmlXPathContextPtr xpath_ctx = NULL;
7610 xmlXPathObjectPtr result = NULL;
7611 const xmlChar *static_string = NULL;
7612 xmlChar *string = NULL;
7614 const xmlChar *codes[] = {
7615 BAD_CAST "1004",
7616 BAD_CAST "1152",
7617 BAD_CAST "1153",
7618 BAD_CAST "1154",
7619 BAD_CAST "1155",
7620 BAD_CAST "1156",
7621 BAD_CAST "9002",
7622 NULL
7624 const char *meanings[] = {
7625 N_("You are not allowed to perform the search"),
7626 N_("The query string is empty"),
7627 N_("Searched box ID is malformed"),
7628 N_("Searched organization ID is malformed"),
7629 N_("Invalid input"),
7630 N_("Requested page size is too large"),
7631 N_("Search engine internal error")
7633 const isds_error errors[] = {
7634 IE_ISDS,
7635 IE_INVAL,
7636 IE_INVAL,
7637 IE_INVAL,
7638 IE_INVAL,
7639 IE_2BIG,
7640 IE_ISDS
7642 struct code_map_isds_error map = {
7643 .codes = codes,
7644 .meanings = meanings,
7645 .errors = errors
7647 #endif
7650 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7651 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7652 if (NULL != current_page_size) zfree(*current_page_size);
7653 if (NULL != last_page) zfree(*last_page);
7654 isds_list_free(boxes);
7656 if (NULL == context) return IE_INVALID_CONTEXT;
7657 zfree(context->long_message);
7659 if (NULL == boxes) return IE_INVAL;
7661 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
7662 isds_log_message(context, _("Query string must be non-empty"));
7663 return IE_INVAL;
7666 #if HAVE_LIBCURL
7667 /* Check if connection is established
7668 * TODO: This check should be done downstairs. */
7669 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7671 /* Build FindDataBox request */
7672 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
7673 if (NULL == request) {
7674 isds_log_message(context,
7675 _("Could not build ISDSSearch2 request"));
7676 return IE_ERROR;
7678 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7679 if(NULL == isds_ns) {
7680 isds_log_message(context, _("Could not create ISDS name space"));
7681 xmlFreeNode(request);
7682 return IE_ERROR;
7684 xmlSetNs(request, isds_ns);
7686 INSERT_STRING(request, "searchText", query);
7688 if (NULL != target) {
7689 static_string = isds_fulltext_target2string(*(target));
7690 if (NULL == static_string) {
7691 isds_printf_message(context, _("Invalid target value: %d"),
7692 *(target));
7693 err = IE_ENUM;
7694 goto leave;
7697 INSERT_STRING(request, "searchType", static_string);
7698 static_string = NULL;
7700 if (NULL != box_type) {
7701 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7702 if (DBTYPE_SYSTEM == *box_type) {
7703 static_string = BAD_CAST "ALL";
7704 } else {
7705 static_string = isds_DbType2string(*(box_type));
7706 if (NULL == static_string) {
7707 isds_printf_message(context, _("Invalid box type value: %d"),
7708 *(box_type));
7709 err = IE_ENUM;
7710 goto leave;
7714 INSERT_STRING(request, "searchScope", static_string);
7715 static_string = NULL;
7717 INSERT_ULONGINT(request, "page", page_number, string);
7718 INSERT_ULONGINT(request, "pageSize", page_size, string);
7719 INSERT_BOOLEAN(request, "highlighting", track_matches);
7721 /* Send request and check response */
7722 err = send_destroy_request_check_response(context,
7723 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
7724 &request, &response, NULL, &map);
7725 if (err) goto leave;
7727 /* Parse response */
7728 xpath_ctx = xmlXPathNewContext(response);
7729 if (NULL == xpath_ctx) {
7730 err = IE_ERROR;
7731 goto leave;
7733 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7734 err = IE_ERROR;
7735 goto leave;
7737 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
7738 xpath_ctx);
7739 if (!result) {
7740 err = IE_ERROR;
7741 goto leave;
7743 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7744 isds_log_message(context, _("Missing ISDSSearch2 element"));
7745 err = IE_ISDS;
7746 goto leave;
7748 if (result->nodesetval->nodeNr > 1) {
7749 isds_log_message(context, _("Multiple ISDSSearch2 element"));
7750 err = IE_ISDS;
7751 goto leave;
7753 xpath_ctx->node = result->nodesetval->nodeTab[0];
7754 xmlXPathFreeObject(result); result = NULL;
7757 /* Extract counters */
7758 if (NULL != total_matching_boxes) {
7759 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
7761 if (NULL != current_page_size) {
7762 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
7764 if (NULL != current_page_beginning) {
7765 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
7767 if (NULL != last_page) {
7768 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
7770 xmlXPathFreeObject(result); result = NULL;
7772 /* Extract boxes if they present */
7773 result = xmlXPathEvalExpression(BAD_CAST
7774 "isds:dbResults/isds:dbResult", xpath_ctx);
7775 if (NULL == result) {
7776 err = IE_ERROR;
7777 goto leave;
7779 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7780 struct isds_list *item, *prev_item = NULL;
7781 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7782 item = calloc(1, sizeof(*item));
7783 if (!item) {
7784 err = IE_NOMEM;
7785 goto leave;
7788 item->destructor = (void (*)(void **))isds_fulltext_result_free;
7789 if (i == 0) *boxes = item;
7790 else prev_item->next = item;
7791 prev_item = item;
7793 xpath_ctx->node = result->nodesetval->nodeTab[i];
7794 err = extract_dbResult(context,
7795 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
7796 (NULL == track_matches) ? 0 : *track_matches);
7797 if (err) goto leave;
7801 leave:
7802 if (err) {
7803 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7804 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7805 if (NULL != current_page_size) zfree(*current_page_size);
7806 if (NULL != last_page) zfree(*last_page);
7807 isds_list_free(boxes);
7810 free(string);
7811 xmlFreeNode(request);
7812 xmlXPathFreeObject(result);
7813 xmlXPathFreeContext(xpath_ctx);
7814 xmlFreeDoc(response);
7816 if (!err)
7817 isds_log(ILF_ISDS, ILL_DEBUG,
7818 _("ISDSSearch2 request processed by server successfully.\n"));
7819 #else /* not HAVE_LIBCURL */
7820 err = IE_NOTSUP;
7821 #endif
7823 return err;
7827 /* Get status of a box.
7828 * @context is ISDS session context.
7829 * @box_id is UTF-8 encoded box identifier as zero terminated string
7830 * @box_status is return value of box status.
7831 * @return:
7832 * IE_SUCCESS if box has been found and its status retrieved
7833 * IE_NOEXIST if box is not known to ISDS server
7834 * or other appropriate error.
7835 * You can use isds_DbState to enumerate box status. However out of enum
7836 * range value can be returned too. This is feature because ISDS
7837 * specification leaves the set of values open.
7838 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7839 * the box has been deleted, but ISDS still lists its former existence. */
7840 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7841 long int *box_status) {
7842 isds_error err = IE_SUCCESS;
7843 #if HAVE_LIBCURL
7844 xmlNsPtr isds_ns = NULL;
7845 xmlNodePtr request = NULL, db_id;
7846 xmlDocPtr response = NULL;
7847 xmlXPathContextPtr xpath_ctx = NULL;
7848 xmlXPathObjectPtr result = NULL;
7849 xmlChar *string = NULL;
7851 const xmlChar *codes[] = {
7852 BAD_CAST "5001",
7853 BAD_CAST "1007",
7854 BAD_CAST "2011",
7855 NULL
7857 const char *meanings[] = {
7858 "The box does not exist",
7859 "Box ID is malformed",
7860 "Box ID malformed",
7862 const isds_error errors[] = {
7863 IE_NOEXIST,
7864 IE_INVAL,
7865 IE_INVAL,
7867 struct code_map_isds_error map = {
7868 .codes = codes,
7869 .meanings = meanings,
7870 .errors = errors
7872 #endif
7874 if (!context) return IE_INVALID_CONTEXT;
7875 zfree(context->long_message);
7876 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7878 #if HAVE_LIBCURL
7879 /* Check if connection is established
7880 * TODO: This check should be done downstairs. */
7881 if (!context->curl) return IE_CONNECTION_CLOSED;
7884 /* Build CheckDataBox request */
7885 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7886 if (!request) {
7887 isds_log_message(context,
7888 _("Could build CheckDataBox request"));
7889 return IE_ERROR;
7891 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7892 if(!isds_ns) {
7893 isds_log_message(context, _("Could not create ISDS name space"));
7894 xmlFreeNode(request);
7895 return IE_ERROR;
7897 xmlSetNs(request, isds_ns);
7898 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7899 if (!db_id) {
7900 isds_log_message(context, _("Could not add dbID child to "
7901 "CheckDataBox element"));
7902 xmlFreeNode(request);
7903 return IE_ERROR;
7907 /* Send request and check response*/
7908 err = send_destroy_request_check_response(context,
7909 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7910 &request, &response, NULL, &map);
7911 if (err) goto leave;
7914 /* Extract data */
7915 xpath_ctx = xmlXPathNewContext(response);
7916 if (!xpath_ctx) {
7917 err = IE_ERROR;
7918 goto leave;
7920 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7921 err = IE_ERROR;
7922 goto leave;
7924 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7925 xpath_ctx);
7926 if (!result) {
7927 err = IE_ERROR;
7928 goto leave;
7930 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7931 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7932 err = IE_ISDS;
7933 goto leave;
7935 if (result->nodesetval->nodeNr > 1) {
7936 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7937 err = IE_ISDS;
7938 goto leave;
7940 xpath_ctx->node = result->nodesetval->nodeTab[0];
7941 xmlXPathFreeObject(result); result = NULL;
7943 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7946 leave:
7947 free(string);
7948 xmlXPathFreeObject(result);
7949 xmlXPathFreeContext(xpath_ctx);
7951 xmlFreeDoc(response);
7953 if (!err)
7954 isds_log(ILF_ISDS, ILL_DEBUG,
7955 _("CheckDataBox request processed by server successfully.\n"));
7956 #else /* not HAVE_LIBCURL */
7957 err = IE_NOTSUP;
7958 #endif
7960 return err;
7964 /* Get list of permissions to send commercial messages.
7965 * @context is ISDS session context.
7966 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7967 * @permissions is a reallocated list of permissions (struct
7968 * isds_commercial_permission*) to send commercial messages from @box_id. The
7969 * order of permissions is significant as the server applies the permissions
7970 * and associated pre-paid credits in the order. Empty list means no
7971 * permission.
7972 * @return:
7973 * IE_SUCCESS if the list has been obtained correctly,
7974 * or other appropriate error. */
7975 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7976 const char *box_id, struct isds_list **permissions) {
7977 isds_error err = IE_SUCCESS;
7978 #if HAVE_LIBCURL
7979 xmlDocPtr response = NULL;
7980 xmlXPathContextPtr xpath_ctx = NULL;
7981 xmlXPathObjectPtr result = NULL;
7982 #endif
7984 if (!context) return IE_INVALID_CONTEXT;
7985 zfree(context->long_message);
7986 if (NULL == permissions) return IE_INVAL;
7987 isds_list_free(permissions);
7988 if (NULL == box_id) return IE_INVAL;
7990 #if HAVE_LIBCURL
7991 /* Check if connection is established */
7992 if (!context->curl) return IE_CONNECTION_CLOSED;
7994 /* Do request and check for success */
7995 err = build_send_dbid_request_check_response(context,
7996 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7997 BAD_CAST box_id, NULL, &response, NULL);
7998 if (!err) {
7999 isds_log(ILF_ISDS, ILL_DEBUG,
8000 _("PDZInfo request processed by server successfully.\n"));
8003 /* Extract data */
8004 /* Prepare structure */
8005 xpath_ctx = xmlXPathNewContext(response);
8006 if (!xpath_ctx) {
8007 err = IE_ERROR;
8008 goto leave;
8010 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8011 err = IE_ERROR;
8012 goto leave;
8015 /* Set context node */
8016 result = xmlXPathEvalExpression(BAD_CAST
8017 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8018 xpath_ctx);
8019 if (!result) {
8020 err = IE_ERROR;
8021 goto leave;
8023 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8024 struct isds_list *prev_item = NULL;
8026 /* Iterate over all permission records */
8027 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8028 struct isds_list *item;
8030 /* Prepare structure */
8031 item = calloc(1, sizeof(*item));
8032 if (!item) {
8033 err = IE_NOMEM;
8034 goto leave;
8036 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8037 if (i == 0) *permissions = item;
8038 else prev_item->next = item;
8039 prev_item = item;
8041 /* Extract it */
8042 xpath_ctx->node = result->nodesetval->nodeTab[i];
8043 err = extract_DbPDZRecord(context,
8044 (struct isds_commercial_permission **) (&item->data),
8045 xpath_ctx);
8046 if (err) goto leave;
8050 leave:
8051 if (err) {
8052 isds_list_free(permissions);
8055 xmlXPathFreeObject(result);
8056 xmlXPathFreeContext(xpath_ctx);
8057 xmlFreeDoc(response);
8059 #else /* not HAVE_LIBCURL */
8060 err = IE_NOTSUP;
8061 #endif
8063 return err;
8067 /* Get details about credit for sending pre-paid commercial messages.
8068 * @context is ISDS session context.
8069 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8070 * @from_date is first day of credit history to return in @history. Only
8071 * tm_year, tm_mon and tm_mday carry sane value.
8072 * @to_date is last day of credit history to return in @history. Only
8073 * tm_year, tm_mon and tm_mday carry sane value.
8074 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8075 * if you don't care. This and all other credit values are integers in
8076 * hundredths of Czech Crowns.
8077 * @email outputs notification e-mail address where notifications about credit
8078 * are sent. This is automatically reallocated string. Pass NULL if you don't
8079 * care. It can return NULL if no address is defined.
8080 * @history outputs auto-reallocated list of pointers to struct
8081 * isds_credit_event. Events in closed interval @from_time to @to_time are
8082 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8083 * are sorted by time.
8084 * @return:
8085 * IE_SUCCESS if the credit details have been obtained correctly,
8086 * or other appropriate error. Please note that server allows to retrieve
8087 * only limited history of events. */
8088 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8089 const char *box_id,
8090 const struct tm *from_date, const struct tm *to_date,
8091 long int *credit, char **email, struct isds_list **history) {
8092 isds_error err = IE_SUCCESS;
8093 #if HAVE_LIBCURL
8094 char *box_id_locale = NULL;
8095 xmlNodePtr request = NULL, node;
8096 xmlNsPtr isds_ns = NULL;
8097 xmlChar *string = NULL;
8099 xmlDocPtr response = NULL;
8100 xmlXPathContextPtr xpath_ctx = NULL;
8101 xmlXPathObjectPtr result = NULL;
8103 const xmlChar *codes[] = {
8104 BAD_CAST "1004",
8105 BAD_CAST "2011",
8106 BAD_CAST "1093",
8107 BAD_CAST "1137",
8108 BAD_CAST "1058",
8109 NULL
8111 const char *meanings[] = {
8112 "Insufficient priviledges for the box",
8113 "The box does not exist",
8114 "Date is too long (history is not available after 15 months)",
8115 "Interval is too long (limit is 3 months)",
8116 "Invalid date"
8118 const isds_error errors[] = {
8119 IE_ISDS,
8120 IE_NOEXIST,
8121 IE_DATE,
8122 IE_DATE,
8123 IE_DATE,
8125 struct code_map_isds_error map = {
8126 .codes = codes,
8127 .meanings = meanings,
8128 .errors = errors
8130 #endif
8132 if (!context) return IE_INVALID_CONTEXT;
8133 zfree(context->long_message);
8135 /* Free output argument */
8136 if (NULL != credit) *credit = 0;
8137 if (NULL != email) zfree(*email);
8138 isds_list_free(history);
8140 if (NULL == box_id) return IE_INVAL;
8142 #if HAVE_LIBCURL
8143 /* Check if connection is established */
8144 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8146 box_id_locale = _isds_utf82locale((char*)box_id);
8147 if (NULL == box_id_locale) {
8148 err = IE_NOMEM;
8149 goto leave;
8152 /* Build request */
8153 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8154 if (NULL == request) {
8155 isds_printf_message(context,
8156 _("Could not build DataBoxCreditInfo request for %s box"),
8157 box_id_locale);
8158 err = IE_ERROR;
8159 goto leave;
8161 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8162 if(!isds_ns) {
8163 isds_log_message(context, _("Could not create ISDS name space"));
8164 err = IE_ERROR;
8165 goto leave;
8167 xmlSetNs(request, isds_ns);
8169 /* Add mandatory XSD:tIdDbInput child */
8170 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8171 /* Add mandatory dates elements with optional values */
8172 if (from_date) {
8173 err = tm2datestring(from_date, &string);
8174 if (err) {
8175 isds_log_message(context,
8176 _("Could not convert `from_date' argument to ISO date "
8177 "string"));
8178 goto leave;
8180 INSERT_STRING(request, "ciFromDate", string);
8181 zfree(string);
8182 } else {
8183 INSERT_STRING(request, "ciFromDate", NULL);
8185 if (to_date) {
8186 err = tm2datestring(to_date, &string);
8187 if (err) {
8188 isds_log_message(context,
8189 _("Could not convert `to_date' argument to ISO date "
8190 "string"));
8191 goto leave;
8193 INSERT_STRING(request, "ciTodate", string);
8194 zfree(string);
8195 } else {
8196 INSERT_STRING(request, "ciTodate", NULL);
8199 /* Send request and check response*/
8200 err = send_destroy_request_check_response(context,
8201 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8202 &request, &response, NULL, &map);
8203 if (err) goto leave;
8206 /* Extract data */
8207 /* Set context to the root */
8208 xpath_ctx = xmlXPathNewContext(response);
8209 if (!xpath_ctx) {
8210 err = IE_ERROR;
8211 goto leave;
8213 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8214 err = IE_ERROR;
8215 goto leave;
8217 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8218 xpath_ctx);
8219 if (!result) {
8220 err = IE_ERROR;
8221 goto leave;
8223 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8224 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8225 err = IE_ISDS;
8226 goto leave;
8228 if (result->nodesetval->nodeNr > 1) {
8229 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8230 err = IE_ISDS;
8231 goto leave;
8233 xpath_ctx->node = result->nodesetval->nodeTab[0];
8234 xmlXPathFreeObject(result); result = NULL;
8236 /* Extract common data */
8237 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8238 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8240 /* Extract records */
8241 if (NULL == history) goto leave;
8242 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8243 xpath_ctx);
8244 if (!result) {
8245 err = IE_ERROR;
8246 goto leave;
8248 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8249 struct isds_list *prev_item = NULL;
8251 /* Iterate over all records */
8252 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8253 struct isds_list *item;
8255 /* Prepare structure */
8256 item = calloc(1, sizeof(*item));
8257 if (!item) {
8258 err = IE_NOMEM;
8259 goto leave;
8261 item->destructor = (void(*)(void**))isds_credit_event_free;
8262 if (i == 0) *history = item;
8263 else prev_item->next = item;
8264 prev_item = item;
8266 /* Extract it */
8267 xpath_ctx->node = result->nodesetval->nodeTab[i];
8268 err = extract_CiRecord(context,
8269 (struct isds_credit_event **) (&item->data),
8270 xpath_ctx);
8271 if (err) goto leave;
8275 leave:
8276 if (!err) {
8277 isds_log(ILF_ISDS, ILL_DEBUG,
8278 _("DataBoxCreditInfo request processed by server successfully.\n"));
8280 if (err) {
8281 isds_list_free(history);
8282 if (NULL != email) zfree(*email)
8285 free(box_id_locale);
8286 xmlXPathFreeObject(result);
8287 xmlXPathFreeContext(xpath_ctx);
8288 xmlFreeDoc(response);
8290 #else /* not HAVE_LIBCURL */
8291 err = IE_NOTSUP;
8292 #endif
8294 return err;
8298 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8299 * code, destroy response and log success.
8300 * @context is ISDS session context.
8301 * @service_name is name of SERVICE_DB_MANIPULATION service
8302 * @box_id is UTF-8 encoded box identifier as zero terminated string
8303 * @approval is optional external approval of box manipulation
8304 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8305 * NULL, if you don't care. */
8306 static isds_error build_send_manipulationdbid_request_check_drop_response(
8307 struct isds_ctx *context, const xmlChar *service_name,
8308 const xmlChar *box_id, const struct isds_approval *approval,
8309 xmlChar **refnumber) {
8310 isds_error err = IE_SUCCESS;
8311 #if HAVE_LIBCURL
8312 xmlDocPtr response = NULL;
8313 #endif
8315 if (!context) return IE_INVALID_CONTEXT;
8316 zfree(context->long_message);
8317 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8319 #if HAVE_LIBCURL
8320 /* Check if connection is established */
8321 if (!context->curl) return IE_CONNECTION_CLOSED;
8323 /* Do request and check for success */
8324 err = build_send_dbid_request_check_response(context,
8325 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8326 &response, refnumber);
8327 xmlFreeDoc(response);
8329 if (!err) {
8330 char *service_name_locale = _isds_utf82locale((char *) service_name);
8331 isds_log(ILF_ISDS, ILL_DEBUG,
8332 _("%s request processed by server successfully.\n"),
8333 service_name_locale);
8334 free(service_name_locale);
8336 #else /* not HAVE_LIBCURL */
8337 err = IE_NOTSUP;
8338 #endif
8340 return err;
8344 /* Switch box into state where box can receive commercial messages (off by
8345 * default)
8346 * @context is ISDS session context.
8347 * @box_id is UTF-8 encoded box identifier as zero terminated string
8348 * @allow is true for enable, false for disable commercial messages income
8349 * @approval is optional external approval of box manipulation
8350 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8351 * NULL, if you don't care. */
8352 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8353 const char *box_id, const _Bool allow,
8354 const struct isds_approval *approval, char **refnumber) {
8355 return build_send_manipulationdbid_request_check_drop_response(context,
8356 (allow) ? BAD_CAST "SetOpenAddressing" :
8357 BAD_CAST "ClearOpenAddressing",
8358 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8362 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8363 * message acceptance). This is just a box permission. Sender must apply
8364 * such role by sending each message.
8365 * @context is ISDS session context.
8366 * @box_id is UTF-8 encoded box identifier as zero terminated string
8367 * @allow is true for enable, false for disable OVM role permission
8368 * @approval is optional external approval of box manipulation
8369 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8370 * NULL, if you don't care. */
8371 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
8372 const char *box_id, const _Bool allow,
8373 const struct isds_approval *approval, char **refnumber) {
8374 return build_send_manipulationdbid_request_check_drop_response(context,
8375 (allow) ? BAD_CAST "SetEffectiveOVM" :
8376 BAD_CAST "ClearEffectiveOVM",
8377 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8381 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8382 * code, destroy response and log success.
8383 * @context is ISDS session context.
8384 * @service_name is name of SERVICE_DB_MANIPULATION service
8385 * @owner is structure describing box
8386 * @approval is optional external approval of box manipulation
8387 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8388 * NULL, if you don't care. */
8389 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8390 struct isds_ctx *context, const xmlChar *service_name,
8391 const struct isds_DbOwnerInfo *owner,
8392 const struct isds_approval *approval, xmlChar **refnumber) {
8393 isds_error err = IE_SUCCESS;
8394 #if HAVE_LIBCURL
8395 char *service_name_locale = NULL;
8396 xmlNodePtr request = NULL, db_owner_info;
8397 xmlNsPtr isds_ns = NULL;
8398 #endif
8401 if (!context) return IE_INVALID_CONTEXT;
8402 zfree(context->long_message);
8403 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8405 #if HAVE_LIBCURL
8406 service_name_locale = _isds_utf82locale((char*)service_name);
8407 if (!service_name_locale) {
8408 err = IE_NOMEM;
8409 goto leave;
8412 /* Build request */
8413 request = xmlNewNode(NULL, service_name);
8414 if (!request) {
8415 isds_printf_message(context,
8416 _("Could not build %s request"), service_name_locale);
8417 err = IE_ERROR;
8418 goto leave;
8420 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8421 if(!isds_ns) {
8422 isds_log_message(context, _("Could not create ISDS name space"));
8423 err = IE_ERROR;
8424 goto leave;
8426 xmlSetNs(request, isds_ns);
8429 /* Add XSD:tOwnerInfoInput child*/
8430 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8431 err = insert_DbOwnerInfo(context, owner, db_owner_info);
8432 if (err) goto leave;
8434 /* Add XSD:gExtApproval*/
8435 err = insert_GExtApproval(context, approval, request);
8436 if (err) goto leave;
8438 /* Send it to server and process response */
8439 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8440 service_name, &request, refnumber);
8442 leave:
8443 xmlFreeNode(request);
8444 free(service_name_locale);
8445 #else /* not HAVE_LIBCURL */
8446 err = IE_NOTSUP;
8447 #endif
8449 return err;
8453 /* Switch box accessibility state on request of box owner.
8454 * Despite the name, owner must do the request off-line. This function is
8455 * designed for such off-line meeting points (e.g. Czech POINT).
8456 * @context is ISDS session context.
8457 * @box identifies box to switch accessibility state.
8458 * @allow is true for making accessible, false to disallow access.
8459 * @approval is optional external approval of box manipulation
8460 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8461 * NULL, if you don't care. */
8462 isds_error isds_switch_box_accessibility_on_owner_request(
8463 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8464 const _Bool allow, const struct isds_approval *approval,
8465 char **refnumber) {
8466 return build_send_manipulationdbowner_request_check_drop_response(context,
8467 (allow) ? BAD_CAST "EnableOwnDataBox" :
8468 BAD_CAST "DisableOwnDataBox",
8469 box, approval, (xmlChar **) refnumber);
8473 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8474 * date.
8475 * @context is ISDS session context.
8476 * @box identifies box to switch accessibility state.
8477 * @since is date since accessibility has been denied. This can be past too.
8478 * Only tm_year, tm_mon and tm_mday carry sane value.
8479 * @approval is optional external approval of box manipulation
8480 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8481 * NULL, if you don't care. */
8482 isds_error isds_disable_box_accessibility_externaly(
8483 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8484 const struct tm *since, const struct isds_approval *approval,
8485 char **refnumber) {
8486 isds_error err = IE_SUCCESS;
8487 #if HAVE_LIBCURL
8488 char *service_name_locale = NULL;
8489 xmlNodePtr request = NULL, node;
8490 xmlNsPtr isds_ns = NULL;
8491 xmlChar *string = NULL;
8492 #endif
8495 if (!context) return IE_INVALID_CONTEXT;
8496 zfree(context->long_message);
8497 if (!box || !since) return IE_INVAL;
8499 #if HAVE_LIBCURL
8500 /* Build request */
8501 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
8502 if (!request) {
8503 isds_printf_message(context,
8504 _("Could not build %s request"), "DisableDataBoxExternally");
8505 err = IE_ERROR;
8506 goto leave;
8508 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8509 if(!isds_ns) {
8510 isds_log_message(context, _("Could not create ISDS name space"));
8511 err = IE_ERROR;
8512 goto leave;
8514 xmlSetNs(request, isds_ns);
8517 /* Add @box identification */
8518 INSERT_ELEMENT(node, request, "dbOwnerInfo");
8519 err = insert_DbOwnerInfo(context, box, node);
8520 if (err) goto leave;
8522 /* Add @since date */
8523 err = tm2datestring(since, &string);
8524 if(err) {
8525 isds_log_message(context,
8526 _("Could not convert `since' argument to ISO date string"));
8527 goto leave;
8529 INSERT_STRING(request, "dbOwnerDisableDate", string);
8530 zfree(string);
8532 /* Add @approval */
8533 err = insert_GExtApproval(context, approval, request);
8534 if (err) goto leave;
8536 /* Send it to server and process response */
8537 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8538 BAD_CAST "DisableDataBoxExternally", &request,
8539 (xmlChar **) refnumber);
8541 leave:
8542 free(string);
8543 xmlFreeNode(request);
8544 free(service_name_locale);
8545 #else /* not HAVE_LIBCURL */
8546 err = IE_NOTSUP;
8547 #endif
8549 return err;
8553 #if HAVE_LIBCURL
8554 /* Insert struct isds_message data (envelope (recipient data optional) and
8555 * documents into XML tree
8556 * @context is session context
8557 * @outgoing_message is libisds structure with message data
8558 * @create_message is XML CreateMessage or CreateMultipleMessage element
8559 * @process_recipient true for recipient data serialization, false for no
8560 * serialization */
8561 static isds_error insert_envelope_files(struct isds_ctx *context,
8562 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8563 const _Bool process_recipient) {
8565 isds_error err = IE_SUCCESS;
8566 xmlNodePtr envelope, dm_files, node;
8567 xmlChar *string = NULL;
8569 if (!context) return IE_INVALID_CONTEXT;
8570 if (!outgoing_message || !create_message) return IE_INVAL;
8573 /* Build envelope */
8574 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8575 if (!envelope) {
8576 isds_printf_message(context, _("Could not add dmEnvelope child to "
8577 "%s element"), create_message->name);
8578 return IE_ERROR;
8581 if (!outgoing_message->envelope) {
8582 isds_log_message(context, _("Outgoing message is missing envelope"));
8583 err = IE_INVAL;
8584 goto leave;
8587 /* Insert optional message type */
8588 err = insert_message_type(context, outgoing_message->envelope->dmType,
8589 envelope);
8590 if (err) goto leave;
8592 INSERT_STRING(envelope, "dmSenderOrgUnit",
8593 outgoing_message->envelope->dmSenderOrgUnit);
8594 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8595 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8597 if (process_recipient) {
8598 if (!outgoing_message->envelope->dbIDRecipient) {
8599 isds_log_message(context,
8600 _("Outgoing message is missing recipient box identifier"));
8601 err = IE_INVAL;
8602 goto leave;
8604 INSERT_STRING(envelope, "dbIDRecipient",
8605 outgoing_message->envelope->dbIDRecipient);
8607 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8608 outgoing_message->envelope->dmRecipientOrgUnit);
8609 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8610 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8611 INSERT_STRING(envelope, "dmToHands",
8612 outgoing_message->envelope->dmToHands);
8615 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8616 "dmAnnotation");
8617 INSERT_STRING(envelope, "dmAnnotation",
8618 outgoing_message->envelope->dmAnnotation);
8620 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8621 0, 50, "dmRecipientRefNumber");
8622 INSERT_STRING(envelope, "dmRecipientRefNumber",
8623 outgoing_message->envelope->dmRecipientRefNumber);
8625 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8626 0, 50, "dmSenderRefNumber");
8627 INSERT_STRING(envelope, "dmSenderRefNumber",
8628 outgoing_message->envelope->dmSenderRefNumber);
8630 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8631 0, 50, "dmRecipientIdent");
8632 INSERT_STRING(envelope, "dmRecipientIdent",
8633 outgoing_message->envelope->dmRecipientIdent);
8635 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8636 0, 50, "dmSenderIdent");
8637 INSERT_STRING(envelope, "dmSenderIdent",
8638 outgoing_message->envelope->dmSenderIdent);
8640 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8641 outgoing_message->envelope->dmLegalTitleLaw, string);
8642 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8643 outgoing_message->envelope->dmLegalTitleYear, string);
8644 INSERT_STRING(envelope, "dmLegalTitleSect",
8645 outgoing_message->envelope->dmLegalTitleSect);
8646 INSERT_STRING(envelope, "dmLegalTitlePar",
8647 outgoing_message->envelope->dmLegalTitlePar);
8648 INSERT_STRING(envelope, "dmLegalTitlePoint",
8649 outgoing_message->envelope->dmLegalTitlePoint);
8651 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8652 outgoing_message->envelope->dmPersonalDelivery);
8653 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8654 outgoing_message->envelope->dmAllowSubstDelivery);
8656 /* ???: Should we require value for dbEffectiveOVM sender?
8657 * ISDS has default as true */
8658 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8659 INSERT_BOOLEAN(envelope, "dmOVM",
8660 outgoing_message->envelope->dmPublishOwnID);
8663 /* Append dmFiles */
8664 if (!outgoing_message->documents) {
8665 isds_log_message(context,
8666 _("Outgoing message is missing list of documents"));
8667 err = IE_INVAL;
8668 goto leave;
8670 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8671 if (!dm_files) {
8672 isds_printf_message(context, _("Could not add dmFiles child to "
8673 "%s element"), create_message->name);
8674 err = IE_ERROR;
8675 goto leave;
8678 /* Check for document hierarchy */
8679 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8680 if (err) goto leave;
8682 /* Process each document */
8683 for (struct isds_list *item =
8684 (struct isds_list *) outgoing_message->documents;
8685 item; item = item->next) {
8686 if (!item->data) {
8687 isds_log_message(context,
8688 _("List of documents contains empty item"));
8689 err = IE_INVAL;
8690 goto leave;
8692 /* FIXME: Check for dmFileMetaType and for document references.
8693 * Only first document can be of MAIN type */
8694 err = insert_document(context, (struct isds_document*) item->data,
8695 dm_files);
8697 if (err) goto leave;
8700 leave:
8701 free(string);
8702 return err;
8704 #endif /* HAVE_LIBCURL */
8707 /* Send a message via ISDS to a recipient
8708 * @context is session context
8709 * @outgoing_message is message to send; Some members are mandatory (like
8710 * dbIDRecipient), some are optional and some are irrelevant (especially data
8711 * about sender). Included pointer to isds_list documents must contain at
8712 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8713 * members will be filled with valid data from ISDS. Exact list of write
8714 * members is subject to change. Currently dmID is changed.
8715 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8716 isds_error isds_send_message(struct isds_ctx *context,
8717 struct isds_message *outgoing_message) {
8719 isds_error err = IE_SUCCESS;
8720 #if HAVE_LIBCURL
8721 xmlNsPtr isds_ns = NULL;
8722 xmlNodePtr request = NULL;
8723 xmlDocPtr response = NULL;
8724 xmlChar *code = NULL, *message = NULL;
8725 xmlXPathContextPtr xpath_ctx = NULL;
8726 xmlXPathObjectPtr result = NULL;
8727 /*_Bool message_is_complete = 0;*/
8728 #endif
8730 if (!context) return IE_INVALID_CONTEXT;
8731 zfree(context->long_message);
8732 if (!outgoing_message) return IE_INVAL;
8734 #if HAVE_LIBCURL
8735 /* Check if connection is established
8736 * TODO: This check should be done downstairs. */
8737 if (!context->curl) return IE_CONNECTION_CLOSED;
8740 /* Build CreateMessage request */
8741 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8742 if (!request) {
8743 isds_log_message(context,
8744 _("Could not build CreateMessage request"));
8745 return IE_ERROR;
8747 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8748 if(!isds_ns) {
8749 isds_log_message(context, _("Could not create ISDS name space"));
8750 xmlFreeNode(request);
8751 return IE_ERROR;
8753 xmlSetNs(request, isds_ns);
8755 /* Append envelope and files */
8756 err = insert_envelope_files(context, outgoing_message, request, 1);
8757 if (err) goto leave;
8760 /* Signal we can serialize message since now */
8761 /*message_is_complete = 1;*/
8764 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8766 /* Sent request */
8767 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8769 /* Don't' destroy request, we want to provide it to application later */
8771 if (err) {
8772 isds_log(ILF_ISDS, ILL_DEBUG,
8773 _("Processing ISDS response on CreateMessage "
8774 "request failed\n"));
8775 goto leave;
8778 /* Check for response status */
8779 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8780 &code, &message, NULL);
8781 if (err) {
8782 isds_log(ILF_ISDS, ILL_DEBUG,
8783 _("ISDS response on CreateMessage request "
8784 "is missing status\n"));
8785 goto leave;
8788 /* Request processed, but refused by server or server failed */
8789 if (xmlStrcmp(code, BAD_CAST "0000")) {
8790 char *box_id_locale =
8791 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8792 char *code_locale = _isds_utf82locale((char*)code);
8793 char *message_locale = _isds_utf82locale((char*)message);
8794 isds_log(ILF_ISDS, ILL_DEBUG,
8795 _("Server did not accept message for %s on CreateMessage "
8796 "request (code=%s, message=%s)\n"),
8797 box_id_locale, code_locale, message_locale);
8798 isds_log_message(context, message_locale);
8799 free(box_id_locale);
8800 free(code_locale);
8801 free(message_locale);
8802 err = IE_ISDS;
8803 goto leave;
8807 /* Extract data */
8808 xpath_ctx = xmlXPathNewContext(response);
8809 if (!xpath_ctx) {
8810 err = IE_ERROR;
8811 goto leave;
8813 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8814 err = IE_ERROR;
8815 goto leave;
8817 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8818 xpath_ctx);
8819 if (!result) {
8820 err = IE_ERROR;
8821 goto leave;
8823 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8824 isds_log_message(context, _("Missing CreateMessageResponse element"));
8825 err = IE_ISDS;
8826 goto leave;
8828 if (result->nodesetval->nodeNr > 1) {
8829 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8830 err = IE_ISDS;
8831 goto leave;
8833 xpath_ctx->node = result->nodesetval->nodeTab[0];
8834 xmlXPathFreeObject(result); result = NULL;
8836 if (outgoing_message->envelope->dmID) {
8837 free(outgoing_message->envelope->dmID);
8838 outgoing_message->envelope->dmID = NULL;
8840 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8841 if (!outgoing_message->envelope->dmID) {
8842 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8843 "but did not return assigned message ID\n"));
8846 leave:
8847 /* TODO: Serialize message into structure member raw */
8848 /* XXX: Each web service transport message in different format.
8849 * Therefore it's not possible to save them directly.
8850 * To save them, one must figure out common format.
8851 * We can leave it on application, or we can implement the ESS format. */
8852 /*if (message_is_complete) {
8853 if (outgoing_message->envelope->dmID) {
8855 /* Add assigned message ID as first child*/
8856 /*xmlNodePtr dmid_text = xmlNewText(
8857 (xmlChar *) outgoing_message->envelope->dmID);
8858 if (!dmid_text) goto serialization_failed;
8860 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8861 BAD_CAST "dmID");
8862 if (!dmid_element) {
8863 xmlFreeNode(dmid_text);
8864 goto serialization_failed;
8867 xmlNodePtr dmid_element_with_text =
8868 xmlAddChild(dmid_element, dmid_text);
8869 if (!dmid_element_with_text) {
8870 xmlFreeNode(dmid_element);
8871 xmlFreeNode(dmid_text);
8872 goto serialization_failed;
8875 node = xmlAddPrevSibling(envelope->childern,
8876 dmid_element_with_text);
8877 if (!node) {
8878 xmlFreeNodeList(dmid_element_with_text);
8879 goto serialization_failed;
8883 /* Serialize message with ID into raw */
8884 /*buffer = serialize_element(envelope)*/
8885 /* }
8887 serialization_failed:
8891 /* Clean up */
8892 xmlXPathFreeObject(result);
8893 xmlXPathFreeContext(xpath_ctx);
8895 free(code);
8896 free(message);
8897 xmlFreeDoc(response);
8898 xmlFreeNode(request);
8900 if (!err)
8901 isds_log(ILF_ISDS, ILL_DEBUG,
8902 _("CreateMessage request processed by server "
8903 "successfully.\n"));
8904 #else /* not HAVE_LIBCURL */
8905 err = IE_NOTSUP;
8906 #endif
8908 return err;
8912 /* Send a message via ISDS to a multiple recipients
8913 * @context is session context
8914 * @outgoing_message is message to send; Some members are mandatory,
8915 * some are optional and some are irrelevant (especially data
8916 * about sender). Data about recipient will be substituted by ISDS from
8917 * @copies. Included pointer to isds_list documents must
8918 * contain at least one document of FILEMETATYPE_MAIN.
8919 * @copies is list of isds_message_copy structures addressing all desired
8920 * recipients. This is read-write structure, some members will be filled with
8921 * valid data from ISDS (message IDs, error codes, error descriptions).
8922 * @return
8923 * ISDS_SUCCESS if all messages have been sent
8924 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8925 * succeeded messages can be identified by copies->data->error),
8926 * or other error code if something other goes wrong. */
8927 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8928 const struct isds_message *outgoing_message,
8929 struct isds_list *copies) {
8931 isds_error err = IE_SUCCESS;
8932 #if HAVE_LIBCURL
8933 isds_error append_err;
8934 xmlNsPtr isds_ns = NULL;
8935 xmlNodePtr request = NULL, recipients, recipient, node;
8936 struct isds_list *item;
8937 struct isds_message_copy *copy;
8938 xmlDocPtr response = NULL;
8939 xmlChar *code = NULL, *message = NULL;
8940 xmlXPathContextPtr xpath_ctx = NULL;
8941 xmlXPathObjectPtr result = NULL;
8942 xmlChar *string = NULL;
8943 int i;
8944 #endif
8946 if (!context) return IE_INVALID_CONTEXT;
8947 zfree(context->long_message);
8948 if (!outgoing_message || !copies) return IE_INVAL;
8950 #if HAVE_LIBCURL
8951 /* Check if connection is established
8952 * TODO: This check should be done downstairs. */
8953 if (!context->curl) return IE_CONNECTION_CLOSED;
8956 /* Build CreateMultipleMessage request */
8957 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
8958 if (!request) {
8959 isds_log_message(context,
8960 _("Could not build CreateMultipleMessage request"));
8961 return IE_ERROR;
8963 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8964 if(!isds_ns) {
8965 isds_log_message(context, _("Could not create ISDS name space"));
8966 xmlFreeNode(request);
8967 return IE_ERROR;
8969 xmlSetNs(request, isds_ns);
8972 /* Build recipients */
8973 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8974 if (!recipients) {
8975 isds_log_message(context, _("Could not add dmRecipients child to "
8976 "CreateMultipleMessage element"));
8977 xmlFreeNode(request);
8978 return IE_ERROR;
8981 /* Insert each recipient */
8982 for (item = copies; item; item = item->next) {
8983 copy = (struct isds_message_copy *) item->data;
8984 if (!copy) {
8985 isds_log_message(context,
8986 _("`copies' list item contains empty data"));
8987 err = IE_INVAL;
8988 goto leave;
8991 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
8992 if (!recipient) {
8993 isds_log_message(context, _("Could not add dmRecipient child to "
8994 "dmRecipients element"));
8995 err = IE_ERROR;
8996 goto leave;
8999 if (!copy->dbIDRecipient) {
9000 isds_log_message(context,
9001 _("Message copy is missing recipient box identifier"));
9002 err = IE_INVAL;
9003 goto leave;
9005 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
9006 INSERT_STRING(recipient, "dmRecipientOrgUnit",
9007 copy->dmRecipientOrgUnit);
9008 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
9009 copy->dmRecipientOrgUnitNum, string);
9010 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
9013 /* Append envelope and files */
9014 err = insert_envelope_files(context, outgoing_message, request, 0);
9015 if (err) goto leave;
9018 isds_log(ILF_ISDS, ILL_DEBUG,
9019 _("Sending CreateMultipleMessage request to ISDS\n"));
9021 /* Sent request */
9022 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9023 if (err) {
9024 isds_log(ILF_ISDS, ILL_DEBUG,
9025 _("Processing ISDS response on CreateMultipleMessage "
9026 "request failed\n"));
9027 goto leave;
9030 /* Check for response status */
9031 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9032 &code, &message, NULL);
9033 if (err) {
9034 isds_log(ILF_ISDS, ILL_DEBUG,
9035 _("ISDS response on CreateMultipleMessage request "
9036 "is missing status\n"));
9037 goto leave;
9040 /* Request processed, but some copies failed */
9041 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9042 char *box_id_locale =
9043 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9044 char *code_locale = _isds_utf82locale((char*)code);
9045 char *message_locale = _isds_utf82locale((char*)message);
9046 isds_log(ILF_ISDS, ILL_DEBUG,
9047 _("Server did accept message for multiple recipients "
9048 "on CreateMultipleMessage request but delivery to "
9049 "some of them failed (code=%s, message=%s)\n"),
9050 box_id_locale, code_locale, message_locale);
9051 isds_log_message(context, message_locale);
9052 free(box_id_locale);
9053 free(code_locale);
9054 free(message_locale);
9055 err = IE_PARTIAL_SUCCESS;
9058 /* Request refused by server as whole */
9059 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9060 char *box_id_locale =
9061 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9062 char *code_locale = _isds_utf82locale((char*)code);
9063 char *message_locale = _isds_utf82locale((char*)message);
9064 isds_log(ILF_ISDS, ILL_DEBUG,
9065 _("Server did not accept message for multiple recipients "
9066 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9067 box_id_locale, code_locale, message_locale);
9068 isds_log_message(context, message_locale);
9069 free(box_id_locale);
9070 free(code_locale);
9071 free(message_locale);
9072 err = IE_ISDS;
9073 goto leave;
9077 /* Extract data */
9078 xpath_ctx = xmlXPathNewContext(response);
9079 if (!xpath_ctx) {
9080 err = IE_ERROR;
9081 goto leave;
9083 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9084 err = IE_ERROR;
9085 goto leave;
9087 result = xmlXPathEvalExpression(
9088 BAD_CAST "/isds:CreateMultipleMessageResponse"
9089 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9090 xpath_ctx);
9091 if (!result) {
9092 err = IE_ERROR;
9093 goto leave;
9095 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9096 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9097 err = IE_ISDS;
9098 goto leave;
9101 /* Extract message ID and delivery status for each copy */
9102 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9103 item = item->next, i++) {
9104 copy = (struct isds_message_copy *) item->data;
9105 xpath_ctx->node = result->nodesetval->nodeTab[i];
9107 append_err = append_TMStatus(context, copy, xpath_ctx);
9108 if (append_err) {
9109 err = append_err;
9110 goto leave;
9113 if (item || i < result->nodesetval->nodeNr) {
9114 isds_printf_message(context, _("ISDS returned unexpected number of "
9115 "message copy delivery states: %d"),
9116 result->nodesetval->nodeNr);
9117 err = IE_ISDS;
9118 goto leave;
9122 leave:
9123 /* Clean up */
9124 free(string);
9125 xmlXPathFreeObject(result);
9126 xmlXPathFreeContext(xpath_ctx);
9128 free(code);
9129 free(message);
9130 xmlFreeDoc(response);
9131 xmlFreeNode(request);
9133 if (!err)
9134 isds_log(ILF_ISDS, ILL_DEBUG,
9135 _("CreateMultipleMessageResponse request processed by server "
9136 "successfully.\n"));
9137 #else /* not HAVE_LIBCURL */
9138 err = IE_NOTSUP;
9139 #endif
9141 return err;
9145 /* Get list of messages. This is common core for getting sent or received
9146 * messages.
9147 * Any criterion argument can be NULL, if you don't care about it.
9148 * @context is session context. Must not be NULL.
9149 * @outgoing_direction is true if you want list of outgoing messages,
9150 * it's false if you want incoming messages.
9151 * @from_time is minimal time and date of message sending inclusive.
9152 * @to_time is maximal time and date of message sending inclusive
9153 * @organization_unit_number is number of sender/recipient respectively.
9154 * @status_filter is bit field of isds_message_status values. Use special
9155 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9156 * all values, you can use bit-wise arithmetic if you want.)
9157 * @offset is index of first message we are interested in. First message is 1.
9158 * Set to 0 (or 1) if you don't care.
9159 * @number is maximal length of list you want to get as input value, outputs
9160 * number of messages matching these criteria. Can be NULL if you don't care
9161 * (applies to output value either).
9162 * @messages is automatically reallocated list of isds_message's. Be ware that
9163 * it returns only brief overview (envelope and some other fields) about each
9164 * message, not the complete message. FIXME: Specify exact fields.
9165 * The list is sorted by delivery time in ascending order.
9166 * Use NULL if you don't care about don't need the data (useful if you want to
9167 * know only the @number). If you provide &NULL, list will be allocated on
9168 * heap, if you provide pointer to non-NULL, list will be freed automatically
9169 * at first. Also in case of error the list will be NULLed.
9170 * @return IE_SUCCESS or appropriate error code. */
9171 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9172 _Bool outgoing_direction,
9173 const struct timeval *from_time, const struct timeval *to_time,
9174 const long int *organization_unit_number,
9175 const unsigned int status_filter,
9176 const unsigned long int offset, unsigned long int *number,
9177 struct isds_list **messages) {
9179 isds_error err = IE_SUCCESS;
9180 #if HAVE_LIBCURL
9181 xmlNsPtr isds_ns = NULL;
9182 xmlNodePtr request = NULL, node;
9183 xmlDocPtr response = NULL;
9184 xmlChar *code = NULL, *message = NULL;
9185 xmlXPathContextPtr xpath_ctx = NULL;
9186 xmlXPathObjectPtr result = NULL;
9187 xmlChar *string = NULL;
9188 int count = 0;
9189 #endif
9191 if (!context) return IE_INVALID_CONTEXT;
9192 zfree(context->long_message);
9194 /* Free former message list if any */
9195 if (messages) isds_list_free(messages);
9197 #if HAVE_LIBCURL
9198 /* Check if connection is established
9199 * TODO: This check should be done downstairs. */
9200 if (!context->curl) return IE_CONNECTION_CLOSED;
9202 /* Build GetListOf*Messages request */
9203 request = xmlNewNode(NULL,
9204 (outgoing_direction) ?
9205 BAD_CAST "GetListOfSentMessages" :
9206 BAD_CAST "GetListOfReceivedMessages"
9208 if (!request) {
9209 isds_log_message(context,
9210 (outgoing_direction) ?
9211 _("Could not build GetListOfSentMessages request") :
9212 _("Could not build GetListOfReceivedMessages request")
9214 return IE_ERROR;
9216 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9217 if(!isds_ns) {
9218 isds_log_message(context, _("Could not create ISDS name space"));
9219 xmlFreeNode(request);
9220 return IE_ERROR;
9222 xmlSetNs(request, isds_ns);
9225 if (from_time) {
9226 err = timeval2timestring(from_time, &string);
9227 if (err) goto leave;
9229 INSERT_STRING(request, "dmFromTime", string);
9230 free(string); string = NULL;
9232 if (to_time) {
9233 err = timeval2timestring(to_time, &string);
9234 if (err) goto leave;
9236 INSERT_STRING(request, "dmToTime", string);
9237 free(string); string = NULL;
9239 if (outgoing_direction) {
9240 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9241 organization_unit_number, string);
9242 } else {
9243 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9244 organization_unit_number, string);
9247 if (status_filter > MESSAGESTATE_ANY) {
9248 isds_printf_message(context,
9249 _("Invalid message state filter value: %ld"), status_filter);
9250 err = IE_INVAL;
9251 goto leave;
9253 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9255 if (offset > 0 ) {
9256 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9257 } else {
9258 INSERT_STRING(request, "dmOffset", "1");
9261 /* number 0 means no limit */
9262 if (number && *number == 0) {
9263 INSERT_STRING(request, "dmLimit", NULL);
9264 } else {
9265 INSERT_ULONGINT(request, "dmLimit", number, string);
9269 isds_log(ILF_ISDS, ILL_DEBUG,
9270 (outgoing_direction) ?
9271 _("Sending GetListOfSentMessages request to ISDS\n") :
9272 _("Sending GetListOfReceivedMessages request to ISDS\n")
9275 /* Sent request */
9276 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9277 xmlFreeNode(request); request = NULL;
9279 if (err) {
9280 isds_log(ILF_ISDS, ILL_DEBUG,
9281 (outgoing_direction) ?
9282 _("Processing ISDS response on GetListOfSentMessages "
9283 "request failed\n") :
9284 _("Processing ISDS response on GetListOfReceivedMessages "
9285 "request failed\n")
9287 goto leave;
9290 /* Check for response status */
9291 err = isds_response_status(context, SERVICE_DM_INFO, response,
9292 &code, &message, NULL);
9293 if (err) {
9294 isds_log(ILF_ISDS, ILL_DEBUG,
9295 (outgoing_direction) ?
9296 _("ISDS response on GetListOfSentMessages request "
9297 "is missing status\n") :
9298 _("ISDS response on GetListOfReceivedMessages request "
9299 "is missing status\n")
9301 goto leave;
9304 /* Request processed, but nothing found */
9305 if (xmlStrcmp(code, BAD_CAST "0000")) {
9306 char *code_locale = _isds_utf82locale((char*)code);
9307 char *message_locale = _isds_utf82locale((char*)message);
9308 isds_log(ILF_ISDS, ILL_DEBUG,
9309 (outgoing_direction) ?
9310 _("Server refused GetListOfSentMessages request "
9311 "(code=%s, message=%s)\n") :
9312 _("Server refused GetListOfReceivedMessages request "
9313 "(code=%s, message=%s)\n"),
9314 code_locale, message_locale);
9315 isds_log_message(context, message_locale);
9316 free(code_locale);
9317 free(message_locale);
9318 err = IE_ISDS;
9319 goto leave;
9323 /* Extract data */
9324 xpath_ctx = xmlXPathNewContext(response);
9325 if (!xpath_ctx) {
9326 err = IE_ERROR;
9327 goto leave;
9329 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9330 err = IE_ERROR;
9331 goto leave;
9333 result = xmlXPathEvalExpression(
9334 (outgoing_direction) ?
9335 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9336 "isds:dmRecords/isds:dmRecord" :
9337 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9338 "isds:dmRecords/isds:dmRecord",
9339 xpath_ctx);
9340 if (!result) {
9341 err = IE_ERROR;
9342 goto leave;
9345 /* Fill output arguments in */
9346 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9347 struct isds_envelope *envelope;
9348 struct isds_list *item = NULL, *last_item = NULL;
9350 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9351 /* Create new message */
9352 item = calloc(1, sizeof(*item));
9353 if (!item) {
9354 err = IE_NOMEM;
9355 goto leave;
9357 item->destructor = (void(*)(void**)) &isds_message_free;
9358 item->data = calloc(1, sizeof(struct isds_message));
9359 if (!item->data) {
9360 isds_list_free(&item);
9361 err = IE_NOMEM;
9362 goto leave;
9365 /* Extract envelope data */
9366 xpath_ctx->node = result->nodesetval->nodeTab[count];
9367 envelope = NULL;
9368 err = extract_DmRecord(context, &envelope, xpath_ctx);
9369 if (err) {
9370 isds_list_free(&item);
9371 goto leave;
9374 /* Attach extracted envelope */
9375 ((struct isds_message *) item->data)->envelope = envelope;
9377 /* Append new message into the list */
9378 if (!*messages) {
9379 *messages = last_item = item;
9380 } else {
9381 last_item->next = item;
9382 last_item = item;
9386 if (number) *number = count;
9388 leave:
9389 if (err) {
9390 isds_list_free(messages);
9393 free(string);
9394 xmlXPathFreeObject(result);
9395 xmlXPathFreeContext(xpath_ctx);
9397 free(code);
9398 free(message);
9399 xmlFreeDoc(response);
9400 xmlFreeNode(request);
9402 if (!err)
9403 isds_log(ILF_ISDS, ILL_DEBUG,
9404 (outgoing_direction) ?
9405 _("GetListOfSentMessages request processed by server "
9406 "successfully.\n") :
9407 _("GetListOfReceivedMessages request processed by server "
9408 "successfully.\n")
9410 #else /* not HAVE_LIBCURL */
9411 err = IE_NOTSUP;
9412 #endif
9413 return err;
9417 /* Get list of outgoing (already sent) messages.
9418 * Any criterion argument can be NULL, if you don't care about it.
9419 * @context is session context. Must not be NULL.
9420 * @from_time is minimal time and date of message sending inclusive.
9421 * @to_time is maximal time and date of message sending inclusive
9422 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9423 * @status_filter is bit field of isds_message_status values. Use special
9424 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9425 * all values, you can use bit-wise arithmetic if you want.)
9426 * @offset is index of first message we are interested in. First message is 1.
9427 * Set to 0 (or 1) if you don't care.
9428 * @number is maximal length of list you want to get as input value, outputs
9429 * number of messages matching these criteria. Can be NULL if you don't care
9430 * (applies to output value either).
9431 * @messages is automatically reallocated list of isds_message's. Be ware that
9432 * it returns only brief overview (envelope and some other fields) about each
9433 * message, not the complete message. FIXME: Specify exact fields.
9434 * The list is sorted by delivery time in ascending order.
9435 * Use NULL if you don't care about the meta data (useful if you want to know
9436 * only the @number). If you provide &NULL, list will be allocated on heap,
9437 * if you provide pointer to non-NULL, list will be freed automatically at
9438 * first. Also in case of error the list will be NULLed.
9439 * @return IE_SUCCESS or appropriate error code. */
9440 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9441 const struct timeval *from_time, const struct timeval *to_time,
9442 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9443 const unsigned long int offset, unsigned long int *number,
9444 struct isds_list **messages) {
9446 return isds_get_list_of_messages(
9447 context, 1,
9448 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9449 offset, number,
9450 messages);
9454 /* Get list of incoming (addressed to you) messages.
9455 * Any criterion argument can be NULL, if you don't care about it.
9456 * @context is session context. Must not be NULL.
9457 * @from_time is minimal time and date of message sending inclusive.
9458 * @to_time is maximal time and date of message sending inclusive
9459 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9460 * @status_filter is bit field of isds_message_status values. Use special
9461 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9462 * all values, you can use bit-wise arithmetic if you want.)
9463 * @offset is index of first message we are interested in. First message is 1.
9464 * Set to 0 (or 1) if you don't care.
9465 * @number is maximal length of list you want to get as input value, outputs
9466 * number of messages matching these criteria. Can be NULL if you don't care
9467 * (applies to output value either).
9468 * @messages is automatically reallocated list of isds_message's. Be ware that
9469 * it returns only brief overview (envelope and some other fields) about each
9470 * message, not the complete message. FIXME: Specify exact fields.
9471 * Use NULL if you don't care about the meta data (useful if you want to know
9472 * only the @number). If you provide &NULL, list will be allocated on heap,
9473 * if you provide pointer to non-NULL, list will be freed automatically at
9474 * first. Also in case of error the list will be NULLed.
9475 * @return IE_SUCCESS or appropriate error code. */
9476 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9477 const struct timeval *from_time, const struct timeval *to_time,
9478 const long int *dmRecipientOrgUnitNum,
9479 const unsigned int status_filter,
9480 const unsigned long int offset, unsigned long int *number,
9481 struct isds_list **messages) {
9483 return isds_get_list_of_messages(
9484 context, 0,
9485 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9486 offset, number,
9487 messages);
9491 /* Get list of sent message state changes.
9492 * Any criterion argument can be NULL, if you don't care about it.
9493 * @context is session context. Must not be NULL.
9494 * @from_time is minimal time and date of status changes inclusive
9495 * @to_time is maximal time and date of status changes inclusive
9496 * @changed_states is automatically reallocated list of
9497 * isds_message_status_change's. If you provide &NULL, list will be allocated
9498 * on heap, if you provide pointer to non-NULL, list will be freed
9499 * automatically at first. Also in case of error the list will be NULLed.
9500 * XXX: The list item ordering is not specified.
9501 * XXX: Server provides only `recent' changes.
9502 * @return IE_SUCCESS or appropriate error code. */
9503 isds_error isds_get_list_of_sent_message_state_changes(
9504 struct isds_ctx *context,
9505 const struct timeval *from_time, const struct timeval *to_time,
9506 struct isds_list **changed_states) {
9508 isds_error err = IE_SUCCESS;
9509 #if HAVE_LIBCURL
9510 xmlNsPtr isds_ns = NULL;
9511 xmlNodePtr request = NULL, node;
9512 xmlDocPtr response = NULL;
9513 xmlXPathContextPtr xpath_ctx = NULL;
9514 xmlXPathObjectPtr result = NULL;
9515 xmlChar *string = NULL;
9516 int count = 0;
9517 #endif
9519 if (!context) return IE_INVALID_CONTEXT;
9520 zfree(context->long_message);
9522 /* Free former message list if any */
9523 isds_list_free(changed_states);
9525 #if HAVE_LIBCURL
9526 /* Check if connection is established
9527 * TODO: This check should be done downstairs. */
9528 if (!context->curl) return IE_CONNECTION_CLOSED;
9530 /* Build GetMessageStateChanges request */
9531 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
9532 if (!request) {
9533 isds_log_message(context,
9534 _("Could not build GetMessageStateChanges request"));
9535 return IE_ERROR;
9537 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9538 if(!isds_ns) {
9539 isds_log_message(context, _("Could not create ISDS name space"));
9540 xmlFreeNode(request);
9541 return IE_ERROR;
9543 xmlSetNs(request, isds_ns);
9546 if (from_time) {
9547 err = timeval2timestring(from_time, &string);
9548 if (err) goto leave;
9550 INSERT_STRING(request, "dmFromTime", string);
9551 zfree(string);
9553 if (to_time) {
9554 err = timeval2timestring(to_time, &string);
9555 if (err) goto leave;
9557 INSERT_STRING(request, "dmToTime", string);
9558 zfree(string);
9561 /* Sent request */
9562 err = send_destroy_request_check_response(context,
9563 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9564 &response, NULL, NULL);
9565 if (err) goto leave;
9568 /* Extract data */
9569 xpath_ctx = xmlXPathNewContext(response);
9570 if (!xpath_ctx) {
9571 err = IE_ERROR;
9572 goto leave;
9574 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9575 err = IE_ERROR;
9576 goto leave;
9578 result = xmlXPathEvalExpression(
9579 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9580 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9581 if (!result) {
9582 err = IE_ERROR;
9583 goto leave;
9586 /* Fill output arguments in */
9587 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9588 struct isds_list *item = NULL, *last_item = NULL;
9590 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9591 /* Create new status change */
9592 item = calloc(1, sizeof(*item));
9593 if (!item) {
9594 err = IE_NOMEM;
9595 goto leave;
9597 item->destructor =
9598 (void(*)(void**)) &isds_message_status_change_free;
9600 /* Extract message status change */
9601 xpath_ctx->node = result->nodesetval->nodeTab[count];
9602 err = extract_StateChangesRecord(context,
9603 (struct isds_message_status_change **) &item->data,
9604 xpath_ctx);
9605 if (err) {
9606 isds_list_free(&item);
9607 goto leave;
9610 /* Append new message status change into the list */
9611 if (!*changed_states) {
9612 *changed_states = last_item = item;
9613 } else {
9614 last_item->next = item;
9615 last_item = item;
9620 leave:
9621 if (err) {
9622 isds_list_free(changed_states);
9625 free(string);
9626 xmlXPathFreeObject(result);
9627 xmlXPathFreeContext(xpath_ctx);
9628 xmlFreeDoc(response);
9629 xmlFreeNode(request);
9631 if (!err)
9632 isds_log(ILF_ISDS, ILL_DEBUG,
9633 _("GetMessageStateChanges request processed by server "
9634 "successfully.\n"));
9635 #else /* not HAVE_LIBCURL */
9636 err = IE_NOTSUP;
9637 #endif
9638 return err;
9642 #if HAVE_LIBCURL
9643 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9644 * code
9645 * @context is session context
9646 * @service is ISDS WS service handler
9647 * @service_name is name of SERVICE_DM_OPERATIONS
9648 * @message_id is message ID to send as service argument to ISDS
9649 * @response is reallocated server SOAP body response as XML document
9650 * @raw_response is reallocated bit stream with response body. Use
9651 * NULL if you don't care
9652 * @raw_response_length is size of @raw_response in bytes
9653 * @code is reallocated ISDS status code
9654 * @status_message is reallocated ISDS status message
9655 * @return error coded from lower layer, context message will be set up
9656 * appropriately. */
9657 static isds_error build_send_check_message_request(struct isds_ctx *context,
9658 const isds_service service, const xmlChar *service_name,
9659 const char *message_id,
9660 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9661 xmlChar **code, xmlChar **status_message) {
9663 isds_error err = IE_SUCCESS;
9664 char *service_name_locale = NULL, *message_id_locale = NULL;
9665 xmlNodePtr request = NULL, node;
9666 xmlNsPtr isds_ns = NULL;
9668 if (!context) return IE_INVALID_CONTEXT;
9669 if (!service_name || !message_id) return IE_INVAL;
9670 if (!response || !code || !status_message) return IE_INVAL;
9671 if (!raw_response_length && raw_response) return IE_INVAL;
9673 /* Free output argument */
9674 xmlFreeDoc(*response); *response = NULL;
9675 if (raw_response) zfree(*raw_response);
9676 zfree(*code);
9677 zfree(*status_message);
9680 /* Check if connection is established
9681 * TODO: This check should be done downstairs. */
9682 if (!context->curl) return IE_CONNECTION_CLOSED;
9684 service_name_locale = _isds_utf82locale((char*)service_name);
9685 message_id_locale = _isds_utf82locale(message_id);
9686 if (!service_name_locale || !message_id_locale) {
9687 err = IE_NOMEM;
9688 goto leave;
9691 /* Build request */
9692 request = xmlNewNode(NULL, service_name);
9693 if (!request) {
9694 isds_printf_message(context,
9695 _("Could not build %s request for %s message ID"),
9696 service_name_locale, message_id_locale);
9697 err = IE_ERROR;
9698 goto leave;
9700 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9701 if(!isds_ns) {
9702 isds_log_message(context, _("Could not create ISDS name space"));
9703 err = IE_ERROR;
9704 goto leave;
9706 xmlSetNs(request, isds_ns);
9709 /* Add requested ID */
9710 err = validate_message_id_length(context, (xmlChar *) message_id);
9711 if (err) goto leave;
9712 INSERT_STRING(request, "dmID", message_id);
9715 isds_log(ILF_ISDS, ILL_DEBUG,
9716 _("Sending %s request for %s message ID to ISDS\n"),
9717 service_name_locale, message_id_locale);
9719 /* Send request */
9720 err = _isds(context, service, request, response,
9721 raw_response, raw_response_length);
9722 xmlFreeNode(request); request = NULL;
9724 if (err) {
9725 isds_log(ILF_ISDS, ILL_DEBUG,
9726 _("Processing ISDS response on %s request failed\n"),
9727 service_name_locale);
9728 goto leave;
9731 /* Check for response status */
9732 err = isds_response_status(context, service, *response,
9733 code, status_message, NULL);
9734 if (err) {
9735 isds_log(ILF_ISDS, ILL_DEBUG,
9736 _("ISDS response on %s request is missing status\n"),
9737 service_name_locale);
9738 goto leave;
9741 /* Request processed, but nothing found */
9742 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9743 char *code_locale = _isds_utf82locale((char*) *code);
9744 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9745 isds_log(ILF_ISDS, ILL_DEBUG,
9746 _("Server refused %s request for %s message ID "
9747 "(code=%s, message=%s)\n"),
9748 service_name_locale, message_id_locale,
9749 code_locale, status_message_locale);
9750 isds_log_message(context, status_message_locale);
9751 free(code_locale);
9752 free(status_message_locale);
9753 err = IE_ISDS;
9754 goto leave;
9757 leave:
9758 free(message_id_locale);
9759 free(service_name_locale);
9760 xmlFreeNode(request);
9761 return err;
9765 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9766 * signed data and free ISDS response.
9767 * @context is session context
9768 * @message_id is UTF-8 encoded message ID for logging purpose
9769 * @response is parsed XML document. It will be freed and NULLed in the middle
9770 * of function run to save memory. This is not guaranteed in case of error.
9771 * @request_name is name of ISDS request used to construct response root
9772 * element name and for logging purpose.
9773 * @raw is reallocated output buffer with DER encoded CMS data
9774 * @raw_length is size of @raw buffer in bytes
9775 * @returns standard error codes, in case of error, @raw will be freed and
9776 * NULLed, @response sometimes. */
9777 static isds_error find_extract_signed_data_free_response(
9778 struct isds_ctx *context, const xmlChar *message_id,
9779 xmlDocPtr *response, const xmlChar *request_name,
9780 void **raw, size_t *raw_length) {
9782 isds_error err = IE_SUCCESS;
9783 char *xpath_expression = NULL;
9784 xmlXPathContextPtr xpath_ctx = NULL;
9785 xmlXPathObjectPtr result = NULL;
9786 char *encoded_structure = NULL;
9788 if (!context) return IE_INVALID_CONTEXT;
9789 if (!raw) return IE_INVAL;
9790 zfree(*raw);
9791 if (!message_id || !response || !*response || !request_name || !raw_length)
9792 return IE_INVAL;
9794 /* Build XPath expression */
9795 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9796 "Response/isds:dmSignature");
9797 if (!xpath_expression) return IE_NOMEM;
9799 /* Extract data */
9800 xpath_ctx = xmlXPathNewContext(*response);
9801 if (!xpath_ctx) {
9802 err = IE_ERROR;
9803 goto leave;
9805 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9806 err = IE_ERROR;
9807 goto leave;
9809 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9810 if (!result) {
9811 err = IE_ERROR;
9812 goto leave;
9814 /* Empty response */
9815 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9816 char *message_id_locale = _isds_utf82locale((char*) message_id);
9817 isds_printf_message(context,
9818 _("Server did not return any signed data for message ID `%s' "
9819 "on %s request"),
9820 message_id_locale, request_name);
9821 free(message_id_locale);
9822 err = IE_ISDS;
9823 goto leave;
9825 /* More responses */
9826 if (result->nodesetval->nodeNr > 1) {
9827 char *message_id_locale = _isds_utf82locale((char*) message_id);
9828 isds_printf_message(context,
9829 _("Server did return more signed data for message ID `%s' "
9830 "on %s request"),
9831 message_id_locale, request_name);
9832 free(message_id_locale);
9833 err = IE_ISDS;
9834 goto leave;
9836 /* One response */
9837 xpath_ctx->node = result->nodesetval->nodeTab[0];
9839 /* Extract PKCS#7 structure */
9840 EXTRACT_STRING(".", encoded_structure);
9841 if (!encoded_structure) {
9842 isds_log_message(context, _("dmSignature element is empty"));
9845 /* Here we have delivery info as standalone CMS in encoded_structure.
9846 * We don't need any other data, free them: */
9847 xmlXPathFreeObject(result); result = NULL;
9848 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9849 xmlFreeDoc(*response); *response = NULL;
9852 /* Decode PKCS#7 to DER format */
9853 *raw_length = _isds_b64decode(encoded_structure, raw);
9854 if (*raw_length == (size_t) -1) {
9855 isds_log_message(context,
9856 _("Error while Base64-decoding PKCS#7 structure"));
9857 err = IE_ERROR;
9858 goto leave;
9861 leave:
9862 if (err) {
9863 zfree(*raw);
9864 raw_length = 0;
9867 free(encoded_structure);
9868 xmlXPathFreeObject(result);
9869 xmlXPathFreeContext(xpath_ctx);
9870 free(xpath_expression);
9872 return err;
9874 #endif /* HAVE_LIBCURL */
9877 /* Download incoming message envelope identified by ID.
9878 * @context is session context
9879 * @message_id is message identifier (you can get them from
9880 * isds_get_list_of_received_messages())
9881 * @message is automatically reallocated message retrieved from ISDS.
9882 * It will miss documents per se. Use isds_get_received_message(), if you are
9883 * interested in documents (content) too.
9884 * Returned hash and timestamp require documents to be verifiable. */
9885 isds_error isds_get_received_envelope(struct isds_ctx *context,
9886 const char *message_id, struct isds_message **message) {
9888 isds_error err = IE_SUCCESS;
9889 #if HAVE_LIBCURL
9890 xmlDocPtr response = NULL;
9891 xmlChar *code = NULL, *status_message = NULL;
9892 xmlXPathContextPtr xpath_ctx = NULL;
9893 xmlXPathObjectPtr result = NULL;
9894 #endif
9896 if (!context) return IE_INVALID_CONTEXT;
9897 zfree(context->long_message);
9899 /* Free former message if any */
9900 if (!message) return IE_INVAL;
9901 isds_message_free(message);
9903 #if HAVE_LIBCURL
9904 /* Do request and check for success */
9905 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9906 BAD_CAST "MessageEnvelopeDownload", message_id,
9907 &response, NULL, NULL, &code, &status_message);
9908 if (err) goto leave;
9910 /* Extract data */
9911 xpath_ctx = xmlXPathNewContext(response);
9912 if (!xpath_ctx) {
9913 err = IE_ERROR;
9914 goto leave;
9916 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9917 err = IE_ERROR;
9918 goto leave;
9920 result = xmlXPathEvalExpression(
9921 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9922 "isds:dmReturnedMessageEnvelope",
9923 xpath_ctx);
9924 if (!result) {
9925 err = IE_ERROR;
9926 goto leave;
9928 /* Empty response */
9929 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9930 char *message_id_locale = _isds_utf82locale((char*) message_id);
9931 isds_printf_message(context,
9932 _("Server did not return any envelope for ID `%s' "
9933 "on MessageEnvelopeDownload request"), message_id_locale);
9934 free(message_id_locale);
9935 err = IE_ISDS;
9936 goto leave;
9938 /* More envelops */
9939 if (result->nodesetval->nodeNr > 1) {
9940 char *message_id_locale = _isds_utf82locale((char*) message_id);
9941 isds_printf_message(context,
9942 _("Server did return more envelopes for ID `%s' "
9943 "on MessageEnvelopeDownload request"), message_id_locale);
9944 free(message_id_locale);
9945 err = IE_ISDS;
9946 goto leave;
9948 /* One message */
9949 xpath_ctx->node = result->nodesetval->nodeTab[0];
9951 /* Extract the envelope (= message without documents, hence 0) */
9952 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9953 if (err) goto leave;
9955 /* Save XML blob */
9956 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9957 &(*message)->raw_length);
9959 leave:
9960 if (err) {
9961 isds_message_free(message);
9964 xmlXPathFreeObject(result);
9965 xmlXPathFreeContext(xpath_ctx);
9967 free(code);
9968 free(status_message);
9969 if (!*message || !(*message)->xml) {
9970 xmlFreeDoc(response);
9973 if (!err)
9974 isds_log(ILF_ISDS, ILL_DEBUG,
9975 _("MessageEnvelopeDownload request processed by server "
9976 "successfully.\n")
9978 #else /* not HAVE_LIBCURL */
9979 err = IE_NOTSUP;
9980 #endif
9981 return err;
9985 /* Load delivery info of any format from buffer.
9986 * @context is session context
9987 * @raw_type advertises format of @buffer content. Only delivery info types
9988 * are accepted.
9989 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9990 * retrieve such data from message->raw after calling
9991 * isds_get_signed_delivery_info().
9992 * @length is length of buffer in bytes.
9993 * @message is automatically reallocated message parsed from @buffer.
9994 * @strategy selects how buffer will be attached into raw isds_message member.
9995 * */
9996 isds_error isds_load_delivery_info(struct isds_ctx *context,
9997 const isds_raw_type raw_type,
9998 const void *buffer, const size_t length,
9999 struct isds_message **message, const isds_buffer_strategy strategy) {
10001 isds_error err = IE_SUCCESS;
10002 message_ns_type message_ns;
10003 xmlDocPtr message_doc = NULL;
10004 xmlXPathContextPtr xpath_ctx = NULL;
10005 xmlXPathObjectPtr result = NULL;
10006 void *xml_stream = NULL;
10007 size_t xml_stream_length = 0;
10009 if (!context) return IE_INVALID_CONTEXT;
10010 zfree(context->long_message);
10011 if (!message) return IE_INVAL;
10012 isds_message_free(message);
10013 if (!buffer) return IE_INVAL;
10016 /* Select buffer format and extract XML from CMS*/
10017 switch (raw_type) {
10018 case RAWTYPE_DELIVERYINFO:
10019 message_ns = MESSAGE_NS_UNSIGNED;
10020 xml_stream = (void *) buffer;
10021 xml_stream_length = length;
10022 break;
10024 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
10025 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10026 xml_stream = (void *) buffer;
10027 xml_stream_length = length;
10028 break;
10030 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10031 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10032 err = _isds_extract_cms_data(context, buffer, length,
10033 &xml_stream, &xml_stream_length);
10034 if (err) goto leave;
10035 break;
10037 default:
10038 isds_log_message(context, _("Bad raw delivery representation type"));
10039 return IE_INVAL;
10040 break;
10043 isds_log(ILF_ISDS, ILL_DEBUG,
10044 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10045 xml_stream_length, xml_stream);
10047 /* Convert delivery info XML stream into XPath context */
10048 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10049 if (!message_doc) {
10050 err = IE_XML;
10051 goto leave;
10053 xpath_ctx = xmlXPathNewContext(message_doc);
10054 if (!xpath_ctx) {
10055 err = IE_ERROR;
10056 goto leave;
10058 /* XXX: Name spaces mangled for signed delivery info:
10059 * http://isds.czechpoint.cz/v20/delivery:
10061 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10062 * <q:dmDelivery>
10063 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10064 * <p:dmID>170272</p:dmID>
10065 * ...
10066 * </p:dmDm>
10067 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10068 * ...
10069 * </q:dmEvents>...</q:dmEvents>
10070 * </q:dmDelivery>
10071 * </q:GetDeliveryInfoResponse>
10072 * */
10073 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10074 err = IE_ERROR;
10075 goto leave;
10077 result = xmlXPathEvalExpression(
10078 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10079 xpath_ctx);
10080 if (!result) {
10081 err = IE_ERROR;
10082 goto leave;
10084 /* Empty delivery info */
10085 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10086 isds_printf_message(context,
10087 _("XML document is not sisds:dmDelivery document"));
10088 err = IE_ISDS;
10089 goto leave;
10091 /* More delivery info's */
10092 if (result->nodesetval->nodeNr > 1) {
10093 isds_printf_message(context,
10094 _("XML document has more sisds:dmDelivery elements"));
10095 err = IE_ISDS;
10096 goto leave;
10098 /* One delivery info */
10099 xpath_ctx->node = result->nodesetval->nodeTab[0];
10101 /* Extract the envelope (= message without documents, hence 0).
10102 * XXX: extract_TReturnedMessage() can obtain attachments size,
10103 * but delivery info carries none. It's coded as option elements,
10104 * so it should work. */
10105 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10106 if (err) goto leave;
10108 /* Extract events */
10109 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10110 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10111 if (err) { err = IE_ERROR; goto leave; }
10112 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10113 if (err) goto leave;
10115 /* Append raw CMS structure into message */
10116 (*message)->raw_type = raw_type;
10117 switch (strategy) {
10118 case BUFFER_DONT_STORE:
10119 break;
10120 case BUFFER_COPY:
10121 (*message)->raw = malloc(length);
10122 if (!(*message)->raw) {
10123 err = IE_NOMEM;
10124 goto leave;
10126 memcpy((*message)->raw, buffer, length);
10127 (*message)->raw_length = length;
10128 break;
10129 case BUFFER_MOVE:
10130 (*message)->raw = (void *) buffer;
10131 (*message)->raw_length = length;
10132 break;
10133 default:
10134 err = IE_ENUM;
10135 goto leave;
10138 leave:
10139 if (err) {
10140 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10141 isds_message_free(message);
10144 xmlXPathFreeObject(result);
10145 xmlXPathFreeContext(xpath_ctx);
10146 if (!*message || !(*message)->xml) {
10147 xmlFreeDoc(message_doc);
10149 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10151 if (!err)
10152 isds_log(ILF_ISDS, ILL_DEBUG,
10153 _("Delivery info loaded successfully.\n"));
10154 return err;
10158 /* Download signed delivery info-sheet of given message identified by ID.
10159 * @context is session context
10160 * @message_id is message identifier (you can get them from
10161 * isds_get_list_of_{sent,received}_messages())
10162 * @message is automatically reallocated message retrieved from ISDS.
10163 * It will miss documents per se. Use isds_get_signed_received_message(),
10164 * if you are interested in documents (content). OTOH, only this function
10165 * can get list events message has gone through. */
10166 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10167 const char *message_id, struct isds_message **message) {
10169 isds_error err = IE_SUCCESS;
10170 #if HAVE_LIBCURL
10171 xmlDocPtr response = NULL;
10172 xmlChar *code = NULL, *status_message = 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 "GetSignedDeliveryInfo", message_id,
10188 &response, NULL, NULL, &code, &status_message);
10189 if (err) goto leave;
10191 /* Find signed delivery info, extract it into raw and maybe free
10192 * response */
10193 err = find_extract_signed_data_free_response(context,
10194 (xmlChar *)message_id, &response,
10195 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10196 if (err) goto leave;
10198 /* Parse delivery info */
10199 err = isds_load_delivery_info(context,
10200 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10201 message, BUFFER_MOVE);
10202 if (err) goto leave;
10204 raw = NULL;
10206 leave:
10207 if (err) {
10208 isds_message_free(message);
10211 free(raw);
10212 free(code);
10213 free(status_message);
10214 xmlFreeDoc(response);
10216 if (!err)
10217 isds_log(ILF_ISDS, ILL_DEBUG,
10218 _("GetSignedDeliveryInfo request processed by server "
10219 "successfully.\n")
10221 #else /* not HAVE_LIBCURL */
10222 err = IE_NOTSUP;
10223 #endif
10224 return err;
10228 /* Download delivery info-sheet of given message identified by ID.
10229 * @context is session context
10230 * @message_id is message identifier (you can get them from
10231 * isds_get_list_of_{sent,received}_messages())
10232 * @message is automatically reallocated message retrieved from ISDS.
10233 * It will miss documents per se. Use isds_get_received_message(), if you are
10234 * interested in documents (content). OTOH, only this function can get list
10235 * of events message has gone through. */
10236 isds_error isds_get_delivery_info(struct isds_ctx *context,
10237 const char *message_id, struct isds_message **message) {
10239 isds_error err = IE_SUCCESS;
10240 #if HAVE_LIBCURL
10241 xmlDocPtr response = NULL;
10242 xmlChar *code = NULL, *status_message = NULL;
10243 xmlNodePtr delivery_node = NULL;
10244 void *raw = NULL;
10245 size_t raw_length = 0;
10246 #endif
10248 if (!context) return IE_INVALID_CONTEXT;
10249 zfree(context->long_message);
10251 /* Free former message if any */
10252 if (!message) return IE_INVAL;
10253 isds_message_free(message);
10255 #if HAVE_LIBCURL
10256 /* Do request and check for success */
10257 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10258 BAD_CAST "GetDeliveryInfo", message_id,
10259 &response, NULL, NULL, &code, &status_message);
10260 if (err) goto leave;
10263 /* Serialize delivery info */
10264 delivery_node = xmlDocGetRootElement(response);
10265 if (!delivery_node) {
10266 char *message_id_locale = _isds_utf82locale((char*) message_id);
10267 isds_printf_message(context,
10268 _("Server did not return any delivery info for ID `%s' "
10269 "on GetDeliveryInfo request"), message_id_locale);
10270 free(message_id_locale);
10271 err = IE_ISDS;
10272 goto leave;
10274 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10275 if (err) goto leave;
10277 /* Parse delivery info */
10278 /* TODO: Here we parse the response second time. We could single delivery
10279 * parser from isds_load_delivery_info() to make things faster. */
10280 err = isds_load_delivery_info(context,
10281 RAWTYPE_DELIVERYINFO, raw, raw_length,
10282 message, BUFFER_MOVE);
10283 if (err) goto leave;
10285 raw = NULL;
10288 leave:
10289 if (err) {
10290 isds_message_free(message);
10293 free(raw);
10294 free(code);
10295 free(status_message);
10296 xmlFreeDoc(response);
10298 if (!err)
10299 isds_log(ILF_ISDS, ILL_DEBUG,
10300 _("GetDeliveryInfo request processed by server "
10301 "successfully.\n")
10303 #else /* not HAVE_LIBCURL */
10304 err = IE_NOTSUP;
10305 #endif
10306 return err;
10310 /* Download incoming message identified by ID.
10311 * @context is session context
10312 * @message_id is message identifier (you can get them from
10313 * isds_get_list_of_received_messages())
10314 * @message is automatically reallocated message retrieved from ISDS */
10315 isds_error isds_get_received_message(struct isds_ctx *context,
10316 const char *message_id, struct isds_message **message) {
10318 isds_error err = IE_SUCCESS;
10319 #if HAVE_LIBCURL
10320 xmlDocPtr response = NULL;
10321 void *xml_stream = NULL;
10322 size_t xml_stream_length;
10323 xmlChar *code = NULL, *status_message = NULL;
10324 xmlXPathContextPtr xpath_ctx = NULL;
10325 xmlXPathObjectPtr result = NULL;
10326 char *phys_path = NULL;
10327 size_t phys_start, phys_end;
10328 #endif
10330 if (!context) return IE_INVALID_CONTEXT;
10331 zfree(context->long_message);
10333 /* Free former message if any */
10334 if (NULL == message) return IE_INVAL;
10335 if (message) isds_message_free(message);
10337 #if HAVE_LIBCURL
10338 /* Do request and check for success */
10339 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10340 BAD_CAST "MessageDownload", message_id,
10341 &response, &xml_stream, &xml_stream_length,
10342 &code, &status_message);
10343 if (err) goto leave;
10345 /* Extract data */
10346 xpath_ctx = xmlXPathNewContext(response);
10347 if (!xpath_ctx) {
10348 err = IE_ERROR;
10349 goto leave;
10351 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10352 err = IE_ERROR;
10353 goto leave;
10355 result = xmlXPathEvalExpression(
10356 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10357 xpath_ctx);
10358 if (!result) {
10359 err = IE_ERROR;
10360 goto leave;
10362 /* Empty response */
10363 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10364 char *message_id_locale = _isds_utf82locale((char*) message_id);
10365 isds_printf_message(context,
10366 _("Server did not return any message for ID `%s' "
10367 "on MessageDownload request"), message_id_locale);
10368 free(message_id_locale);
10369 err = IE_ISDS;
10370 goto leave;
10372 /* More messages */
10373 if (result->nodesetval->nodeNr > 1) {
10374 char *message_id_locale = _isds_utf82locale((char*) message_id);
10375 isds_printf_message(context,
10376 _("Server did return more messages for ID `%s' "
10377 "on MessageDownload request"), message_id_locale);
10378 free(message_id_locale);
10379 err = IE_ISDS;
10380 goto leave;
10382 /* One message */
10383 xpath_ctx->node = result->nodesetval->nodeTab[0];
10385 /* Extract the message */
10386 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10387 if (err) goto leave;
10389 /* Locate raw XML blob */
10390 phys_path = strdup(
10391 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10392 PHYSXML_ELEMENT_SEPARATOR
10393 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10394 PHYSXML_ELEMENT_SEPARATOR
10395 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10397 if (!phys_path) {
10398 err = IE_NOMEM;
10399 goto leave;
10401 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10402 phys_path, &phys_start, &phys_end);
10403 zfree(phys_path);
10404 if (err) {
10405 isds_log_message(context,
10406 _("Substring with isds:MessageDownloadResponse element "
10407 "could not be located in raw SOAP message"));
10408 goto leave;
10410 /* Save XML blob */
10411 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10412 &(*message)->raw_length);*/
10413 /* TODO: Store name space declarations from ancestors */
10414 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10415 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10416 (*message)->raw_length = phys_end - phys_start + 1;
10417 (*message)->raw = malloc((*message)->raw_length);
10418 if (!(*message)->raw) {
10419 err = IE_NOMEM;
10420 goto leave;
10422 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10425 leave:
10426 if (err) {
10427 isds_message_free(message);
10430 free(phys_path);
10432 xmlXPathFreeObject(result);
10433 xmlXPathFreeContext(xpath_ctx);
10435 free(code);
10436 free(status_message);
10437 free(xml_stream);
10438 if (!*message || !(*message)->xml) {
10439 xmlFreeDoc(response);
10442 if (!err)
10443 isds_log(ILF_ISDS, ILL_DEBUG,
10444 _("MessageDownload request processed by server "
10445 "successfully.\n")
10447 #else /* not HAVE_LIBCURL */
10448 err = IE_NOTSUP;
10449 #endif
10450 return err;
10454 /* Load message of any type from buffer.
10455 * @context is session context
10456 * @raw_type defines content type of @buffer. Only message types are allowed.
10457 * @buffer is message raw representation. Format (CMS, plain signed,
10458 * message direction) is defined in @raw_type. You can retrieve such data
10459 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10460 * @length is length of buffer in bytes.
10461 * @message is automatically reallocated message parsed from @buffer.
10462 * @strategy selects how buffer will be attached into raw isds_message member.
10463 * */
10464 isds_error isds_load_message(struct isds_ctx *context,
10465 const isds_raw_type raw_type, const void *buffer, const size_t length,
10466 struct isds_message **message, const isds_buffer_strategy strategy) {
10468 isds_error err = IE_SUCCESS;
10469 void *xml_stream = NULL;
10470 size_t xml_stream_length = 0;
10471 message_ns_type message_ns;
10472 xmlDocPtr message_doc = NULL;
10473 xmlXPathContextPtr xpath_ctx = NULL;
10474 xmlXPathObjectPtr result = NULL;
10476 if (!context) return IE_INVALID_CONTEXT;
10477 zfree(context->long_message);
10478 if (!message) return IE_INVAL;
10479 isds_message_free(message);
10480 if (!buffer) return IE_INVAL;
10483 /* Select buffer format and extract XML from CMS*/
10484 switch (raw_type) {
10485 case RAWTYPE_INCOMING_MESSAGE:
10486 message_ns = MESSAGE_NS_UNSIGNED;
10487 xml_stream = (void *) buffer;
10488 xml_stream_length = length;
10489 break;
10491 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10492 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10493 xml_stream = (void *) buffer;
10494 xml_stream_length = length;
10495 break;
10497 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10498 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10499 err = _isds_extract_cms_data(context, buffer, length,
10500 &xml_stream, &xml_stream_length);
10501 if (err) goto leave;
10502 break;
10504 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10505 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10506 xml_stream = (void *) buffer;
10507 xml_stream_length = length;
10508 break;
10510 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10511 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10512 err = _isds_extract_cms_data(context, buffer, length,
10513 &xml_stream, &xml_stream_length);
10514 if (err) goto leave;
10515 break;
10517 default:
10518 isds_log_message(context, _("Bad raw message representation type"));
10519 return IE_INVAL;
10520 break;
10523 isds_log(ILF_ISDS, ILL_DEBUG,
10524 _("Loading message:\n%.*s\nEnd of message\n"),
10525 xml_stream_length, xml_stream);
10527 /* Convert messages XML stream into XPath context */
10528 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10529 if (!message_doc) {
10530 err = IE_XML;
10531 goto leave;
10533 xpath_ctx = xmlXPathNewContext(message_doc);
10534 if (!xpath_ctx) {
10535 err = IE_ERROR;
10536 goto leave;
10538 /* XXX: Standard name space for unsigned incoming direction:
10539 * http://isds.czechpoint.cz/v20/
10541 * XXX: Name spaces mangled for signed outgoing direction:
10542 * http://isds.czechpoint.cz/v20/SentMessage:
10544 * <q:MessageDownloadResponse
10545 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10546 * <q:dmReturnedMessage>
10547 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10548 * <p:dmID>151916</p:dmID>
10549 * ...
10550 * </p:dmDm>
10551 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10552 * ...
10553 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10554 * </q:dmReturnedMessage>
10555 * </q:MessageDownloadResponse>
10557 * XXX: Name spaces mangled for signed incoming direction:
10558 * http://isds.czechpoint.cz/v20/message:
10560 * <q:MessageDownloadResponse
10561 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10562 * <q:dmReturnedMessage>
10563 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10564 * <p:dmID>151916</p:dmID>
10565 * ...
10566 * </p:dmDm>
10567 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10568 * ...
10569 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10570 * </q:dmReturnedMessage>
10571 * </q:MessageDownloadResponse>
10573 * Stupidity of ISDS developers is unlimited */
10574 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10575 err = IE_ERROR;
10576 goto leave;
10578 result = xmlXPathEvalExpression(
10579 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10580 xpath_ctx);
10581 if (!result) {
10582 err = IE_ERROR;
10583 goto leave;
10585 /* Empty message */
10586 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10587 isds_printf_message(context,
10588 _("XML document does not contain "
10589 "sisds:dmReturnedMessage element"));
10590 err = IE_ISDS;
10591 goto leave;
10593 /* More messages */
10594 if (result->nodesetval->nodeNr > 1) {
10595 isds_printf_message(context,
10596 _("XML document has more sisds:dmReturnedMessage elements"));
10597 err = IE_ISDS;
10598 goto leave;
10600 /* One message */
10601 xpath_ctx->node = result->nodesetval->nodeTab[0];
10603 /* Extract the message */
10604 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10605 if (err) goto leave;
10607 /* Append raw buffer into message */
10608 (*message)->raw_type = raw_type;
10609 switch (strategy) {
10610 case BUFFER_DONT_STORE:
10611 break;
10612 case BUFFER_COPY:
10613 (*message)->raw = malloc(length);
10614 if (!(*message)->raw) {
10615 err = IE_NOMEM;
10616 goto leave;
10618 memcpy((*message)->raw, buffer, length);
10619 (*message)->raw_length = length;
10620 break;
10621 case BUFFER_MOVE:
10622 (*message)->raw = (void *) buffer;
10623 (*message)->raw_length = length;
10624 break;
10625 default:
10626 err = IE_ENUM;
10627 goto leave;
10631 leave:
10632 if (err) {
10633 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10634 isds_message_free(message);
10637 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10638 xmlXPathFreeObject(result);
10639 xmlXPathFreeContext(xpath_ctx);
10640 if (!*message || !(*message)->xml) {
10641 xmlFreeDoc(message_doc);
10644 if (!err)
10645 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10646 return err;
10650 /* Determine type of raw message or delivery info according some heuristics.
10651 * It does not validate the raw blob.
10652 * @context is session context
10653 * @raw_type returns content type of @buffer. Valid only if exit code of this
10654 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10655 * reallocated memory.
10656 * @buffer is message raw representation.
10657 * @length is length of buffer in bytes. */
10658 isds_error isds_guess_raw_type(struct isds_ctx *context,
10659 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10660 isds_error err;
10661 void *xml_stream = NULL;
10662 size_t xml_stream_length = 0;
10663 xmlDocPtr document = NULL;
10664 xmlNodePtr root = NULL;
10666 if (!context) return IE_INVALID_CONTEXT;
10667 zfree(context->long_message);
10668 if (length == 0 || !buffer) return IE_INVAL;
10669 if (!raw_type) return IE_INVAL;
10671 /* Try CMS */
10672 err = _isds_extract_cms_data(context, buffer, length,
10673 &xml_stream, &xml_stream_length);
10674 if (err) {
10675 xml_stream = (void *) buffer;
10676 xml_stream_length = (size_t) length;
10677 err = IE_SUCCESS;
10680 /* Try XML */
10681 document = xmlParseMemory(xml_stream, xml_stream_length);
10682 if (!document) {
10683 isds_printf_message(context,
10684 _("Could not parse data as XML document"));
10685 err = IE_NOTSUP;
10686 goto leave;
10689 /* Get root element */
10690 root = xmlDocGetRootElement(document);
10691 if (!root) {
10692 isds_printf_message(context,
10693 _("XML document is missing root element"));
10694 err = IE_XML;
10695 goto leave;
10698 if (!root->ns || !root->ns->href) {
10699 isds_printf_message(context,
10700 _("Root element does not belong to any name space"));
10701 err = IE_NOTSUP;
10702 goto leave;
10705 /* Test name space */
10706 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10707 if (xml_stream == buffer)
10708 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10709 else
10710 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10711 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10712 if (xml_stream == buffer)
10713 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10714 else
10715 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10716 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10717 if (xml_stream == buffer)
10718 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10719 else
10720 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10721 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10722 if (xml_stream != buffer) {
10723 isds_printf_message(context,
10724 _("Document in ISDS name space is encapsulated into CMS" ));
10725 err = IE_NOTSUP;
10726 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10727 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10728 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10729 *raw_type = RAWTYPE_DELIVERYINFO;
10730 else {
10731 isds_printf_message(context,
10732 _("Unknown root element in ISDS name space"));
10733 err = IE_NOTSUP;
10735 } else {
10736 isds_printf_message(context,
10737 _("Unknown name space"));
10738 err = IE_NOTSUP;
10741 leave:
10742 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10743 xmlFreeDoc(document);
10744 return err;
10748 /* Download signed incoming/outgoing message identified by ID.
10749 * @context is session context
10750 * @output is true for outgoing message, false for incoming message
10751 * @message_id is message identifier (you can get them from
10752 * isds_get_list_of_{sent,received}_messages())
10753 * @message is automatically reallocated message retrieved from ISDS. The raw
10754 * member will be filled with PKCS#7 structure in DER format. */
10755 static isds_error isds_get_signed_message(struct isds_ctx *context,
10756 const _Bool outgoing, const char *message_id,
10757 struct isds_message **message) {
10759 isds_error err = IE_SUCCESS;
10760 #if HAVE_LIBCURL
10761 xmlDocPtr response = NULL;
10762 xmlChar *code = NULL, *status_message = NULL;
10763 xmlXPathContextPtr xpath_ctx = NULL;
10764 xmlXPathObjectPtr result = NULL;
10765 char *encoded_structure = NULL;
10766 void *raw = NULL;
10767 size_t raw_length = 0;
10768 #endif
10770 if (!context) return IE_INVALID_CONTEXT;
10771 zfree(context->long_message);
10772 if (!message) return IE_INVAL;
10773 isds_message_free(message);
10775 #if HAVE_LIBCURL
10776 /* Do request and check for success */
10777 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10778 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10779 BAD_CAST "SignedMessageDownload",
10780 message_id, &response, NULL, NULL, &code, &status_message);
10781 if (err) goto leave;
10783 /* Find signed message, extract it into raw and maybe free
10784 * response */
10785 err = find_extract_signed_data_free_response(context,
10786 (xmlChar *)message_id, &response,
10787 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10788 BAD_CAST "SignedMessageDownload",
10789 &raw, &raw_length);
10790 if (err) goto leave;
10792 /* Parse message */
10793 err = isds_load_message(context,
10794 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10795 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10796 raw, raw_length, message, BUFFER_MOVE);
10797 if (err) goto leave;
10799 raw = NULL;
10801 leave:
10802 if (err) {
10803 isds_message_free(message);
10806 free(encoded_structure);
10807 xmlXPathFreeObject(result);
10808 xmlXPathFreeContext(xpath_ctx);
10809 free(raw);
10811 free(code);
10812 free(status_message);
10813 xmlFreeDoc(response);
10815 if (!err)
10816 isds_log(ILF_ISDS, ILL_DEBUG,
10817 (outgoing) ?
10818 _("SignedSentMessageDownload request processed by server "
10819 "successfully.\n") :
10820 _("SignedMessageDownload request processed by server "
10821 "successfully.\n")
10823 #else /* not HAVE_LIBCURL */
10824 err = IE_NOTSUP;
10825 #endif
10826 return err;
10830 /* Download signed incoming message identified by ID.
10831 * @context is session context
10832 * @message_id is message identifier (you can get them from
10833 * isds_get_list_of_received_messages())
10834 * @message is automatically reallocated message retrieved from ISDS. The raw
10835 * member will be filled with PKCS#7 structure in DER format. */
10836 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10837 const char *message_id, struct isds_message **message) {
10838 return isds_get_signed_message(context, 0, message_id, message);
10842 /* Download signed outgoing message identified by ID.
10843 * @context is session context
10844 * @message_id is message identifier (you can get them from
10845 * isds_get_list_of_sent_messages())
10846 * @message is automatically reallocated message retrieved from ISDS. The raw
10847 * member will be filled with PKCS#7 structure in DER format. */
10848 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10849 const char *message_id, struct isds_message **message) {
10850 return isds_get_signed_message(context, 1, message_id, message);
10854 /* Get type and name of user who sent a message identified by ID.
10855 * @context is session context
10856 * @message_id is message identifier
10857 * @sender_type is pointer to automatically allocated type of sender detected
10858 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10859 * library or to the server, NULL will be returned. Pass NULL if you don't
10860 * care about it.
10861 * @raw_sender_type is automatically reallocated UTF-8 string describing
10862 * sender type or NULL if not known to server. Pass NULL if you don't care.
10863 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10864 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10865 isds_error isds_get_message_sender(struct isds_ctx *context,
10866 const char *message_id, isds_sender_type **sender_type,
10867 char **raw_sender_type, char **sender_name) {
10868 isds_error err = IE_SUCCESS;
10869 #if HAVE_LIBCURL
10870 xmlDocPtr response = NULL;
10871 xmlChar *code = NULL, *status_message = NULL;
10872 xmlXPathContextPtr xpath_ctx = NULL;
10873 xmlXPathObjectPtr result = NULL;
10874 char *type_string = NULL;
10875 #endif
10877 if (!context) return IE_INVALID_CONTEXT;
10878 zfree(context->long_message);
10879 if (sender_type) zfree(*sender_type);
10880 if (raw_sender_type) zfree(*raw_sender_type);
10881 if (sender_name) zfree(*sender_name);
10882 if (!message_id) return IE_INVAL;
10884 #if HAVE_LIBCURL
10885 /* Do request and check for success */
10886 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10887 BAD_CAST "GetMessageAuthor",
10888 message_id, &response, NULL, NULL, &code, &status_message);
10889 if (err) goto leave;
10891 /* Extract data */
10892 xpath_ctx = xmlXPathNewContext(response);
10893 if (!xpath_ctx) {
10894 err = IE_ERROR;
10895 goto leave;
10897 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10898 err = IE_ERROR;
10899 goto leave;
10901 result = xmlXPathEvalExpression(
10902 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10903 if (!result) {
10904 err = IE_ERROR;
10905 goto leave;
10907 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10908 isds_log_message(context,
10909 _("Missing GetMessageAuthorResponse element"));
10910 err = IE_ISDS;
10911 goto leave;
10913 if (result->nodesetval->nodeNr > 1) {
10914 isds_log_message(context,
10915 _("Multiple GetMessageAuthorResponse element"));
10916 err = IE_ISDS;
10917 goto leave;
10919 xpath_ctx->node = result->nodesetval->nodeTab[0];
10920 xmlXPathFreeObject(result); result = NULL;
10922 /* Fill output arguments in */
10923 EXTRACT_STRING("isds:userType", type_string);
10924 if (NULL != type_string) {
10925 if (NULL != sender_type) {
10926 *sender_type = calloc(1, sizeof(**sender_type));
10927 if (NULL == *sender_type) {
10928 err = IE_NOMEM;
10929 goto leave;
10932 err = string2isds_sender_type((xmlChar *)type_string,
10933 *sender_type);
10934 if (err) {
10935 zfree(*sender_type);
10936 if (err == IE_ENUM) {
10937 err = IE_SUCCESS;
10938 char *type_string_locale = _isds_utf82locale(type_string);
10939 isds_log(ILF_ISDS, ILL_WARNING,
10940 _("Unknown isds:userType value: %s"),
10941 type_string_locale);
10942 free(type_string_locale);
10947 if (NULL != sender_name)
10948 EXTRACT_STRING("isds:authorName", *sender_name);
10950 leave:
10951 if (err) {
10952 if (NULL != sender_type) zfree(*sender_type);
10953 zfree(type_string);
10954 if (NULL != sender_name) zfree(*sender_name);
10956 if (NULL != raw_sender_type) *raw_sender_type = type_string;
10958 xmlXPathFreeObject(result);
10959 xmlXPathFreeContext(xpath_ctx);
10961 free(code);
10962 free(status_message);
10963 xmlFreeDoc(response);
10965 if (!err)
10966 isds_log(ILF_ISDS, ILL_DEBUG,
10967 _("GetMessageAuthor request processed by server "
10968 "successfully.\n"));
10969 #else /* not HAVE_LIBCURL */
10970 err = IE_NOTSUP;
10971 #endif
10972 return err;
10976 /* Retrieve hash of message identified by ID stored in ISDS.
10977 * @context is session context
10978 * @message_id is message identifier
10979 * @hash is automatically reallocated message hash downloaded from ISDS.
10980 * Message must exist in system and must not be deleted. */
10981 isds_error isds_download_message_hash(struct isds_ctx *context,
10982 const char *message_id, struct isds_hash **hash) {
10984 isds_error err = IE_SUCCESS;
10985 #if HAVE_LIBCURL
10986 xmlDocPtr response = NULL;
10987 xmlChar *code = NULL, *status_message = NULL;
10988 xmlXPathContextPtr xpath_ctx = NULL;
10989 xmlXPathObjectPtr result = NULL;
10990 #endif
10992 if (!context) return IE_INVALID_CONTEXT;
10993 zfree(context->long_message);
10995 isds_hash_free(hash);
10997 #if HAVE_LIBCURL
10998 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10999 BAD_CAST "VerifyMessage", message_id,
11000 &response, NULL, NULL, &code, &status_message);
11001 if (err) goto leave;
11004 /* Extract data */
11005 xpath_ctx = xmlXPathNewContext(response);
11006 if (!xpath_ctx) {
11007 err = IE_ERROR;
11008 goto leave;
11010 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11011 err = IE_ERROR;
11012 goto leave;
11014 result = xmlXPathEvalExpression(
11015 BAD_CAST "/isds:VerifyMessageResponse",
11016 xpath_ctx);
11017 if (!result) {
11018 err = IE_ERROR;
11019 goto leave;
11021 /* Empty response */
11022 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11023 char *message_id_locale = _isds_utf82locale((char*) message_id);
11024 isds_printf_message(context,
11025 _("Server did not return any response for ID `%s' "
11026 "on VerifyMessage request"), message_id_locale);
11027 free(message_id_locale);
11028 err = IE_ISDS;
11029 goto leave;
11031 /* More responses */
11032 if (result->nodesetval->nodeNr > 1) {
11033 char *message_id_locale = _isds_utf82locale((char*) message_id);
11034 isds_printf_message(context,
11035 _("Server did return more responses for ID `%s' "
11036 "on VerifyMessage request"), message_id_locale);
11037 free(message_id_locale);
11038 err = IE_ISDS;
11039 goto leave;
11041 /* One response */
11042 xpath_ctx->node = result->nodesetval->nodeTab[0];
11044 /* Extract the hash */
11045 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11047 leave:
11048 if (err) {
11049 isds_hash_free(hash);
11052 xmlXPathFreeObject(result);
11053 xmlXPathFreeContext(xpath_ctx);
11055 free(code);
11056 free(status_message);
11057 xmlFreeDoc(response);
11059 if (!err)
11060 isds_log(ILF_ISDS, ILL_DEBUG,
11061 _("VerifyMessage request processed by server "
11062 "successfully.\n")
11064 #else /* not HAVE_LIBCURL */
11065 err = IE_NOTSUP;
11066 #endif
11067 return err;
11071 /* Erase message specified by @message_id from long term storage. Other
11072 * message cannot be erased on user request.
11073 * @context is session context
11074 * @message_id is message identifier.
11075 * @incoming is true for incoming message, false for outgoing message.
11076 * @return
11077 * IE_SUCCESS if message has ben removed
11078 * IE_INVAL if message does not exist in long term storage or message
11079 * belongs to different box
11080 * TODO: IE_NOEPRM if user has no permission to erase a message */
11081 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11082 const char *message_id, _Bool incoming) {
11083 isds_error err = IE_SUCCESS;
11084 #if HAVE_LIBCURL
11085 xmlNodePtr request = NULL, node;
11086 xmlNsPtr isds_ns = NULL;
11087 xmlDocPtr response = NULL;
11088 xmlChar *code = NULL, *status_message = NULL;
11089 #endif
11091 if (!context) return IE_INVALID_CONTEXT;
11092 zfree(context->long_message);
11093 if (NULL == message_id) return IE_INVAL;
11095 /* Check if connection is established
11096 * TODO: This check should be done downstairs. */
11097 if (!context->curl) return IE_CONNECTION_CLOSED;
11099 #if HAVE_LIBCURL
11100 /* Build request */
11101 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11102 if (!request) {
11103 isds_log_message(context,
11104 _("Could build EraseMessage request"));
11105 return IE_ERROR;
11107 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11108 if(!isds_ns) {
11109 isds_log_message(context, _("Could not create ISDS name space"));
11110 xmlFreeNode(request);
11111 return IE_ERROR;
11113 xmlSetNs(request, isds_ns);
11115 err = validate_message_id_length(context, (xmlChar *) message_id);
11116 if (err) goto leave;
11117 INSERT_STRING(request, "dmID", message_id);
11119 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11122 /* Send request */
11123 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11124 "message ID %s to ISDS\n"), message_id);
11125 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11126 xmlFreeNode(request); request = NULL;
11128 if (err) {
11129 isds_log(ILF_ISDS, ILL_DEBUG,
11130 _("Processing ISDS response on EraseMessage request "
11131 "failed\n"));
11132 goto leave;
11135 /* Check for response status */
11136 err = isds_response_status(context, SERVICE_DM_INFO, response,
11137 &code, &status_message, NULL);
11138 if (err) {
11139 isds_log(ILF_ISDS, ILL_DEBUG,
11140 _("ISDS response on EraseMessage request is missing "
11141 "status\n"));
11142 goto leave;
11145 /* Check server status code */
11146 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11147 isds_log_message(context, _("Message to erase belongs to other box"));
11148 err = IE_INVAL;
11149 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11150 isds_log_message(context, _("Message to erase is not saved in "
11151 "long term storage or the direction does not match"));
11152 err = IE_INVAL;
11153 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11154 char *code_locale = _isds_utf82locale((char*) code);
11155 char *message_locale = _isds_utf82locale((char*) status_message);
11156 isds_log(ILF_ISDS, ILL_DEBUG,
11157 _("Server refused EraseMessage request "
11158 "(code=%s, message=%s)\n"),
11159 code_locale, message_locale);
11160 isds_log_message(context, message_locale);
11161 free(code_locale);
11162 free(message_locale);
11163 err = IE_ISDS;
11164 goto leave;
11167 leave:
11168 free(code);
11169 free(status_message);
11170 xmlFreeDoc(response);
11171 xmlFreeNode(request);
11173 if (!err)
11174 isds_log(ILF_ISDS, ILL_DEBUG,
11175 _("EraseMessage request processed by server "
11176 "successfully.\n")
11178 #else /* not HAVE_LIBCURL */
11179 err = IE_NOTSUP;
11180 #endif
11181 return err;
11185 /* Mark message as read. This is a transactional commit function to acknowledge
11186 * to ISDS the message has been downloaded and processed by client properly.
11187 * @context is session context
11188 * @message_id is message identifier. */
11189 isds_error isds_mark_message_read(struct isds_ctx *context,
11190 const char *message_id) {
11192 isds_error err = IE_SUCCESS;
11193 #if HAVE_LIBCURL
11194 xmlDocPtr response = NULL;
11195 xmlChar *code = NULL, *status_message = NULL;
11196 #endif
11198 if (!context) return IE_INVALID_CONTEXT;
11199 zfree(context->long_message);
11201 #if HAVE_LIBCURL
11202 /* Do request and check for success */
11203 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11204 BAD_CAST "MarkMessageAsDownloaded", message_id,
11205 &response, NULL, NULL, &code, &status_message);
11207 free(code);
11208 free(status_message);
11209 xmlFreeDoc(response);
11211 if (!err)
11212 isds_log(ILF_ISDS, ILL_DEBUG,
11213 _("MarkMessageAsDownloaded request processed by server "
11214 "successfully.\n")
11216 #else /* not HAVE_LIBCURL */
11217 err = IE_NOTSUP;
11218 #endif
11219 return err;
11223 /* Mark message as received by recipient. This is applicable only to
11224 * commercial message. Use envelope->dmType message member to distinguish
11225 * commercial message from government message. Government message is
11226 * received automatically (by law), commercial message on recipient request.
11227 * @context is session context
11228 * @message_id is message identifier. */
11229 isds_error isds_mark_message_received(struct isds_ctx *context,
11230 const char *message_id) {
11232 isds_error err = IE_SUCCESS;
11233 #if HAVE_LIBCURL
11234 xmlDocPtr response = NULL;
11235 xmlChar *code = NULL, *status_message = NULL;
11236 #endif
11238 if (!context) return IE_INVALID_CONTEXT;
11239 zfree(context->long_message);
11241 #if HAVE_LIBCURL
11242 /* Do request and check for success */
11243 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11244 BAD_CAST "ConfirmDelivery", message_id,
11245 &response, NULL, NULL, &code, &status_message);
11247 free(code);
11248 free(status_message);
11249 xmlFreeDoc(response);
11251 if (!err)
11252 isds_log(ILF_ISDS, ILL_DEBUG,
11253 _("ConfirmDelivery request processed by server "
11254 "successfully.\n")
11256 #else /* not HAVE_LIBCURL */
11257 err = IE_NOTSUP;
11258 #endif
11259 return err;
11263 /* Send document for authorized conversion into Czech POINT system.
11264 * This is public anonymous service, no log-in necessary. Special context is
11265 * used to reuse keep-a-live HTTPS connection.
11266 * @context is Czech POINT session context. DO NOT use context connected to
11267 * ISDS server. Use new context or context used by this function previously.
11268 * @document is document to convert. Only data, data_length, dmFileDescr and
11269 * is_xml members are significant. Be ware that not all document formats can be
11270 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11271 * @id is reallocated identifier assigned by Czech POINT system to
11272 * your document on submit. Use is to tell it to Czech POINT officer.
11273 * @date is reallocated document submit date (submitted documents
11274 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11275 * value. */
11276 isds_error czp_convert_document(struct isds_ctx *context,
11277 const struct isds_document *document,
11278 char **id, struct tm **date) {
11279 isds_error err = IE_SUCCESS;
11280 #if HAVE_LIBCURL
11281 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11282 xmlNodePtr request = NULL, node;
11283 xmlDocPtr response = NULL;
11285 xmlXPathContextPtr xpath_ctx = NULL;
11286 xmlXPathObjectPtr result = NULL;
11287 long int status = -1;
11288 long int *status_ptr = &status;
11289 char *string = NULL;
11290 #endif
11293 if (!context) return IE_INVALID_CONTEXT;
11294 zfree(context->long_message);
11295 if (!document || !id || !date) return IE_INVAL;
11297 if (document->is_xml) {
11298 isds_log_message(context,
11299 _("XML documents cannot be submitted to conversion"));
11300 return IE_NOTSUP;
11303 /* Free output arguments */
11304 zfree(*id);
11305 zfree(*date);
11307 #if HAVE_LIBCURL
11308 /* Store configuration */
11309 context->type = CTX_TYPE_CZP;
11310 free(context->url);
11311 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11312 if (!(context->url))
11313 return IE_NOMEM;
11315 /* Prepare CURL handle if not yet connected */
11316 if (!context->curl) {
11317 context->curl = curl_easy_init();
11318 if (!(context->curl))
11319 return IE_ERROR;
11322 /* Build conversion request */
11323 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11324 if (!request) {
11325 isds_log_message(context,
11326 _("Could not build Czech POINT conversion request"));
11327 return IE_ERROR;
11329 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11330 if(!deposit_ns) {
11331 isds_log_message(context,
11332 _("Could not create Czech POINT deposit name space"));
11333 xmlFreeNode(request);
11334 return IE_ERROR;
11336 xmlSetNs(request, deposit_ns);
11338 /* Insert children. They are in empty namespace! */
11339 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11340 if(!empty_ns) {
11341 isds_log_message(context, _("Could not create empty name space"));
11342 err = IE_ERROR;
11343 goto leave;
11345 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11346 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11347 document->dmFileDescr);
11349 /* Document encoded in Base64 */
11350 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11351 document->data, document->data_length);
11352 if (err) goto leave;
11354 isds_log(ILF_ISDS, ILL_DEBUG,
11355 _("Submitting document for conversion into Czech POINT deposit"));
11357 /* Send conversion request */
11358 err = _czp_czpdeposit(context, request, &response);
11359 xmlFreeNode(request); request = NULL;
11361 if (err) {
11362 czp_do_close_connection(context);
11363 goto leave;
11367 /* Extract response */
11368 xpath_ctx = xmlXPathNewContext(response);
11369 if (!xpath_ctx) {
11370 err = IE_ERROR;
11371 goto leave;
11373 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11374 err = IE_ERROR;
11375 goto leave;
11377 result = xmlXPathEvalExpression(
11378 BAD_CAST "/deposit:saveDocumentResponse/return",
11379 xpath_ctx);
11380 if (!result) {
11381 err = IE_ERROR;
11382 goto leave;
11384 /* Empty response */
11385 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11386 isds_printf_message(context,
11387 _("Missing `return' element in Czech POINT deposit response"));
11388 err = IE_ISDS;
11389 goto leave;
11391 /* More responses */
11392 if (result->nodesetval->nodeNr > 1) {
11393 isds_printf_message(context,
11394 _("Multiple `return' element in Czech POINT deposit response"));
11395 err = IE_ISDS;
11396 goto leave;
11398 /* One response */
11399 xpath_ctx->node = result->nodesetval->nodeTab[0];
11401 /* Get status */
11402 EXTRACT_LONGINT("status", status_ptr, 1);
11403 if (status) {
11404 EXTRACT_STRING("statusMsg", string);
11405 char *string_locale = _isds_utf82locale(string);
11406 isds_printf_message(context,
11407 _("Czech POINT deposit refused document for conversion "
11408 "(code=%ld, message=%s)"),
11409 status, string_locale);
11410 free(string_locale);
11411 err = IE_ISDS;
11412 goto leave;
11415 /* Get document ID */
11416 EXTRACT_STRING("documentID", *id);
11418 /* Get submit date */
11419 EXTRACT_STRING("dateInserted", string);
11420 if (string) {
11421 *date = calloc(1, sizeof(**date));
11422 if (!*date) {
11423 err = IE_NOMEM;
11424 goto leave;
11426 err = _isds_datestring2tm((xmlChar *)string, *date);
11427 if (err) {
11428 if (err == IE_NOTSUP) {
11429 err = IE_ISDS;
11430 char *string_locale = _isds_utf82locale(string);
11431 isds_printf_message(context,
11432 _("Invalid dateInserted value: %s"), string_locale);
11433 free(string_locale);
11435 goto leave;
11439 leave:
11440 free(string);
11441 xmlXPathFreeObject(result);
11442 xmlXPathFreeContext(xpath_ctx);
11444 xmlFreeDoc(response);
11445 xmlFreeNode(request);
11447 if (!err) {
11448 char *id_locale = _isds_utf82locale((char *) *id);
11449 isds_log(ILF_ISDS, ILL_DEBUG,
11450 _("Document %s has been submitted for conversion "
11451 "to server successfully\n"), id_locale);
11452 free(id_locale);
11454 #else /* not HAVE_LIBCURL */
11455 err = IE_NOTSUP;
11456 #endif
11457 return err;
11461 /* Close possibly opened connection to Czech POINT document deposit.
11462 * @context is Czech POINT session context. */
11463 isds_error czp_close_connection(struct isds_ctx *context) {
11464 if (!context) return IE_INVALID_CONTEXT;
11465 zfree(context->long_message);
11466 #if HAVE_LIBCURL
11467 return czp_do_close_connection(context);
11468 #else
11469 return IE_NOTSUP;
11470 #endif
11474 /* Send request for new box creation in testing ISDS instance.
11475 * It's not possible to request for a production box currently, as it
11476 * communicates via e-mail.
11477 * XXX: This function does not work either. Server complains about invalid
11478 * e-mail address.
11479 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11480 * this function
11481 * @context is special session context for box creation request. DO NOT use
11482 * standard context as it could reveal your password. Use fresh new context or
11483 * context previously used by this function.
11484 * @box is box description to create including single primary user (in case of
11485 * FO box type). It outputs box ID assigned by ISDS in dbID element.
11486 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11487 * box, or contact address of PFO box owner). The email member is mandatory as
11488 * it will be used to deliver credentials.
11489 * @former_names is former name of box owner. Pass NULL if you don't care.
11490 * @approval is optional external approval of box manipulation
11491 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11492 * NULL, if you don't care.*/
11493 isds_error isds_request_new_testing_box(struct isds_ctx *context,
11494 struct isds_DbOwnerInfo *box, const struct isds_list *users,
11495 const char *former_names, const struct isds_approval *approval,
11496 char **refnumber) {
11497 isds_error err = IE_SUCCESS;
11498 #if HAVE_LIBCURL
11499 xmlNodePtr request = NULL;
11500 xmlDocPtr response = NULL;
11501 xmlXPathContextPtr xpath_ctx = NULL;
11502 xmlXPathObjectPtr result = NULL;
11503 #endif
11506 if (!context) return IE_INVALID_CONTEXT;
11507 zfree(context->long_message);
11508 if (!box) return IE_INVAL;
11510 #if HAVE_LIBCURL
11511 if (!box->email || box->email[0] == '\0') {
11512 isds_log_message(context, _("E-mail field is mandatory"));
11513 return IE_INVAL;
11516 /* Scratch box ID */
11517 zfree(box->dbID);
11519 /* Store configuration */
11520 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
11521 free(context->url);
11522 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
11523 if (!(context->url))
11524 return IE_NOMEM;
11526 /* Prepare CURL handle if not yet connected */
11527 if (!context->curl) {
11528 context->curl = curl_easy_init();
11529 if (!(context->curl))
11530 return IE_ERROR;
11533 /* Build CreateDataBox request */
11534 err = build_CreateDBInput_request(context,
11535 &request, BAD_CAST "CreateDataBox",
11536 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
11537 if (err) goto leave;
11539 /* Send it to server and process response */
11540 err = send_destroy_request_check_response(context,
11541 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
11542 &response, (xmlChar **) refnumber, NULL);
11543 if (err) goto leave;
11545 /* Extract box ID */
11546 xpath_ctx = xmlXPathNewContext(response);
11547 if (!xpath_ctx) {
11548 err = IE_ERROR;
11549 goto leave;
11551 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11552 err = IE_ERROR;
11553 goto leave;
11555 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
11557 leave:
11558 xmlXPathFreeObject(result);
11559 xmlXPathFreeContext(xpath_ctx);
11560 xmlFreeDoc(response);
11561 xmlFreeNode(request);
11563 if (!err) {
11564 isds_log(ILF_ISDS, ILL_DEBUG,
11565 _("CreateDataBox request processed by server successfully.\n"));
11567 #else /* not HAVE_LIBCURL */
11568 err = IE_NOTSUP;
11569 #endif
11571 return err;
11575 /* Submit CMS signed message to ISDS to verify its originality. This is
11576 * stronger form of isds_verify_message_hash() because ISDS does more checks
11577 * than simple one (potentialy old weak) hash comparison.
11578 * @context is session context
11579 * @message is memory with raw CMS signed message bit stream
11580 * @length is @message size in bytes
11581 * @return
11582 * IE_SUCCESS if message originates in ISDS
11583 * IE_NOTEQUAL if message is unknown to ISDS
11584 * other code for other errors */
11585 isds_error isds_authenticate_message(struct isds_ctx *context,
11586 const void *message, size_t length) {
11587 isds_error err = IE_SUCCESS;
11588 #if HAVE_LIBCURL
11589 xmlNsPtr isds_ns = NULL;
11590 xmlNodePtr request = NULL;
11591 xmlDocPtr response = NULL;
11592 xmlXPathContextPtr xpath_ctx = NULL;
11593 xmlXPathObjectPtr result = NULL;
11594 _Bool *authentic = NULL;
11595 #endif
11597 if (!context) return IE_INVALID_CONTEXT;
11598 zfree(context->long_message);
11599 if (!message || length == 0) return IE_INVAL;
11601 #if HAVE_LIBCURL
11602 /* Check if connection is established
11603 * TODO: This check should be done downstairs. */
11604 if (!context->curl) return IE_CONNECTION_CLOSED;
11607 /* Build AuthenticateMessage request */
11608 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11609 if (!request) {
11610 isds_log_message(context,
11611 _("Could not build AuthenticateMessage request"));
11612 return IE_ERROR;
11614 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11615 if(!isds_ns) {
11616 isds_log_message(context, _("Could not create ISDS name space"));
11617 xmlFreeNode(request);
11618 return IE_ERROR;
11620 xmlSetNs(request, isds_ns);
11622 /* Insert Base64 encoded message */
11623 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11624 message, length);
11625 if (err) goto leave;
11627 /* Send request to server and process response */
11628 err = send_destroy_request_check_response(context,
11629 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11630 &response, NULL, NULL);
11631 if (err) goto leave;
11634 /* ISDS has decided */
11635 xpath_ctx = xmlXPathNewContext(response);
11636 if (!xpath_ctx) {
11637 err = IE_ERROR;
11638 goto leave;
11640 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11641 err = IE_ERROR;
11642 goto leave;
11645 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11647 if (!authentic) {
11648 isds_log_message(context,
11649 _("Server did not return any response on "
11650 "AuthenticateMessage request"));
11651 err = IE_ISDS;
11652 goto leave;
11654 if (*authentic) {
11655 isds_log(ILF_ISDS, ILL_DEBUG,
11656 _("ISDS authenticated the message successfully\n"));
11657 } else {
11658 isds_log_message(context, _("ISDS does not know the message"));
11659 err = IE_NOTEQUAL;
11663 leave:
11664 free(authentic);
11665 xmlXPathFreeObject(result);
11666 xmlXPathFreeContext(xpath_ctx);
11668 xmlFreeDoc(response);
11669 xmlFreeNode(request);
11670 #else /* not HAVE_LIBCURL */
11671 err = IE_NOTSUP;
11672 #endif
11674 return err;
11678 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11679 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11680 * be re-signed.
11681 * @context is session context
11682 * @input_data is memory with raw CMS signed message or delivery info bit
11683 * stream to re-sign
11684 * @input_length is @input_data size in bytes
11685 * @output_data is pointer to auto-allocated memory where to store re-signed
11686 * input data blob. Caller must free it.
11687 * @output_data is pointer where to store @output_data size in bytes
11688 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11689 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11690 * @return
11691 * IE_SUCCESS if CMS blob has been re-signed successfully
11692 * other code for other errors */
11693 isds_error isds_resign_message(struct isds_ctx *context,
11694 const void *input_data, size_t input_length,
11695 void **output_data, size_t *output_length, struct tm **valid_to) {
11696 isds_error err = IE_SUCCESS;
11697 #if HAVE_LIBCURL
11698 xmlNsPtr isds_ns = NULL;
11699 xmlNodePtr request = NULL;
11700 xmlDocPtr response = NULL;
11701 xmlXPathContextPtr xpath_ctx = NULL;
11702 xmlXPathObjectPtr result = NULL;
11703 char *string = NULL;
11704 const xmlChar *codes[] = {
11705 BAD_CAST "2200",
11706 BAD_CAST "2201",
11707 BAD_CAST "2204",
11708 BAD_CAST "2207",
11709 NULL
11711 const char *meanings[] = {
11712 "Message is bad",
11713 "Message is not original",
11714 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11715 "Time stamp could not been generated in time"
11717 const isds_error errors[] = {
11718 IE_INVAL,
11719 IE_NOTUNIQ,
11720 IE_INVAL,
11721 IE_ISDS,
11723 struct code_map_isds_error map = {
11724 .codes = codes,
11725 .meanings = meanings,
11726 .errors = errors
11728 #endif
11730 if (NULL != output_data) *output_data = NULL;
11731 if (NULL != output_length) *output_length = 0;
11732 if (NULL != valid_to) *valid_to = NULL;
11734 if (NULL == context) return IE_INVALID_CONTEXT;
11735 zfree(context->long_message);
11736 if (NULL == input_data || 0 == input_length) {
11737 isds_log_message(context, _("Empty CMS blob on input"));
11738 return IE_INVAL;
11740 if (NULL == output_data || NULL == output_length) {
11741 isds_log_message(context,
11742 _("NULL pointer provided for output CMS blob"));
11743 return IE_INVAL;
11746 #if HAVE_LIBCURL
11747 /* Check if connection is established
11748 * TODO: This check should be done downstairs. */
11749 if (!context->curl) return IE_CONNECTION_CLOSED;
11752 /* Build Re-signISDSDocument request */
11753 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11754 if (!request) {
11755 isds_log_message(context,
11756 _("Could not build Re-signISDSDocument request"));
11757 return IE_ERROR;
11759 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11760 if(!isds_ns) {
11761 isds_log_message(context, _("Could not create ISDS name space"));
11762 xmlFreeNode(request);
11763 return IE_ERROR;
11765 xmlSetNs(request, isds_ns);
11767 /* Insert Base64 encoded CMS blob */
11768 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11769 input_data, input_length);
11770 if (err) goto leave;
11772 /* Send request to server and process response */
11773 err = send_destroy_request_check_response(context,
11774 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11775 &response, NULL, &map);
11776 if (err) goto leave;
11779 /* Extract re-signed data */
11780 xpath_ctx = xmlXPathNewContext(response);
11781 if (!xpath_ctx) {
11782 err = IE_ERROR;
11783 goto leave;
11785 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11786 err = IE_ERROR;
11787 goto leave;
11789 result = xmlXPathEvalExpression(
11790 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11791 if (!result) {
11792 err = IE_ERROR;
11793 goto leave;
11795 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11796 isds_log_message(context,
11797 _("Missing Re-signISDSDocumentResponse element"));
11798 err = IE_ISDS;
11799 goto leave;
11801 if (result->nodesetval->nodeNr > 1) {
11802 isds_log_message(context,
11803 _("Multiple Re-signISDSDocumentResponse element"));
11804 err = IE_ISDS;
11805 goto leave;
11807 xpath_ctx->node = result->nodesetval->nodeTab[0];
11808 xmlXPathFreeObject(result); result = NULL;
11810 EXTRACT_STRING("isds:dmResultDoc", string);
11811 /* Decode non-empty data */
11812 if (NULL != string && string[0] != '\0') {
11813 *output_length = _isds_b64decode(string, output_data);
11814 if (*output_length == (size_t) -1) {
11815 isds_log_message(context,
11816 _("Error while Base64-decoding re-signed data"));
11817 err = IE_ERROR;
11818 goto leave;
11820 } else {
11821 isds_log_message(context, _("Server did not send re-signed data"));
11822 err = IE_ISDS;
11823 goto leave;
11825 zfree(string);
11827 if (NULL != valid_to) {
11828 /* Get time stamp expiration date */
11829 EXTRACT_STRING("isds:dmValidTo", string);
11830 if (NULL != string) {
11831 *valid_to = calloc(1, sizeof(**valid_to));
11832 if (!*valid_to) {
11833 err = IE_NOMEM;
11834 goto leave;
11836 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11837 if (err) {
11838 if (err == IE_NOTSUP) {
11839 err = IE_ISDS;
11840 char *string_locale = _isds_utf82locale(string);
11841 isds_printf_message(context,
11842 _("Invalid dmValidTo value: %s"), string_locale);
11843 free(string_locale);
11845 goto leave;
11850 leave:
11851 free(string);
11853 xmlXPathFreeObject(result);
11854 xmlXPathFreeContext(xpath_ctx);
11856 xmlFreeDoc(response);
11857 xmlFreeNode(request);
11858 #else /* not HAVE_LIBCURL */
11859 err = IE_NOTSUP;
11860 #endif
11862 return err;
11865 #undef INSERT_ELEMENT
11866 #undef CHECK_FOR_STRING_LENGTH
11867 #undef INSERT_STRING_ATTRIBUTE
11868 #undef INSERT_ULONGINTNOPTR
11869 #undef INSERT_ULONGINT
11870 #undef INSERT_LONGINT
11871 #undef INSERT_BOOLEAN
11872 #undef INSERT_SCALAR_BOOLEAN
11873 #undef INSERT_STRING
11874 #undef INSERT_STRING_WITH_NS
11875 #undef EXTRACT_STRING_ATTRIBUTE
11876 #undef EXTRACT_ULONGINT
11877 #undef EXTRACT_LONGINT
11878 #undef EXTRACT_BOOLEAN
11879 #undef EXTRACT_STRING
11882 /* Compute hash of message from raw representation and store it into envelope.
11883 * Original hash structure will be destroyed in envelope.
11884 * @context is session context
11885 * @message is message carrying raw XML message blob
11886 * @algorithm is desired hash algorithm to use */
11887 isds_error isds_compute_message_hash(struct isds_ctx *context,
11888 struct isds_message *message, const isds_hash_algorithm algorithm) {
11889 isds_error err = IE_SUCCESS;
11890 const char *nsuri;
11891 void *xml_stream = NULL;
11892 size_t xml_stream_length;
11893 size_t phys_start, phys_end;
11894 char *phys_path = NULL;
11895 struct isds_hash *new_hash = NULL;
11898 if (!context) return IE_INVALID_CONTEXT;
11899 zfree(context->long_message);
11900 if (!message) return IE_INVAL;
11902 if (!message->raw) {
11903 isds_log_message(context,
11904 _("Message does not carry raw representation"));
11905 return IE_INVAL;
11908 switch (message->raw_type) {
11909 case RAWTYPE_INCOMING_MESSAGE:
11910 nsuri = ISDS_NS;
11911 xml_stream = message->raw;
11912 xml_stream_length = message->raw_length;
11913 break;
11915 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11916 nsuri = SISDS_INCOMING_NS;
11917 xml_stream = message->raw;
11918 xml_stream_length = message->raw_length;
11919 break;
11921 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11922 nsuri = SISDS_INCOMING_NS;
11923 err = _isds_extract_cms_data(context,
11924 message->raw, message->raw_length,
11925 &xml_stream, &xml_stream_length);
11926 if (err) goto leave;
11927 break;
11929 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11930 nsuri = SISDS_OUTGOING_NS;
11931 xml_stream = message->raw;
11932 xml_stream_length = message->raw_length;
11933 break;
11935 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11936 nsuri = SISDS_OUTGOING_NS;
11937 err = _isds_extract_cms_data(context,
11938 message->raw, message->raw_length,
11939 &xml_stream, &xml_stream_length);
11940 if (err) goto leave;
11941 break;
11943 default:
11944 isds_log_message(context, _("Bad raw representation type"));
11945 return IE_INVAL;
11946 break;
11950 /* XXX: Hash is computed from original string representing isds:dmDm
11951 * subtree. That means no encoding, white space, xmlns attributes changes.
11952 * In other words, input for hash can be invalid XML stream. */
11953 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
11954 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11955 PHYSXML_ELEMENT_SEPARATOR,
11956 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
11957 PHYSXML_ELEMENT_SEPARATOR
11958 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
11959 err = IE_NOMEM;
11960 goto leave;
11962 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11963 phys_path, &phys_start, &phys_end);
11964 zfree(phys_path);
11965 if (err) {
11966 isds_log_message(context,
11967 _("Substring with isds:dmDM element could not be located "
11968 "in raw message"));
11969 goto leave;
11973 /* Compute hash */
11974 new_hash = calloc(1, sizeof(*new_hash));
11975 if (!new_hash) {
11976 err = IE_NOMEM;
11977 goto leave;
11979 new_hash->algorithm = algorithm;
11980 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
11981 new_hash);
11982 if (err) {
11983 isds_log_message(context, _("Could not compute message hash"));
11984 goto leave;
11987 /* Save computed hash */
11988 if (!message->envelope) {
11989 message->envelope = calloc(1, sizeof(*message->envelope));
11990 if (!message->envelope) {
11991 err = IE_NOMEM;
11992 goto leave;
11995 isds_hash_free(&message->envelope->hash);
11996 message->envelope->hash = new_hash;
11998 leave:
11999 if (err) {
12000 isds_hash_free(&new_hash);
12003 free(phys_path);
12004 if (xml_stream != message->raw) free(xml_stream);
12005 return err;
12009 /* Compare two hashes.
12010 * @h1 is first hash
12011 * @h2 is another hash
12012 * @return
12013 * IE_SUCCESS if hashes equal
12014 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12015 * IE_ENUM if not comparable, but both structures defined
12016 * IE_INVAL if some of the structures are undefined (NULL)
12017 * IE_ERROR if internal error occurs */
12018 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
12019 if (h1 == NULL || h2 == NULL) return IE_INVAL;
12020 if (h1->algorithm != h2->algorithm) return IE_ENUM;
12021 if (h1->length != h2->length) return IE_ERROR;
12022 if (h1->length > 0 && !h1->value) return IE_ERROR;
12023 if (h2->length > 0 && !h2->value) return IE_ERROR;
12025 for (size_t i = 0; i < h1->length; i++) {
12026 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12027 return IE_NOTEQUAL;
12029 return IE_SUCCESS;
12033 /* Check message has gone through ISDS by comparing message hash stored in
12034 * ISDS and locally computed hash. You must provide message with valid raw
12035 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12036 * This is convenient wrapper for isds_download_message_hash(),
12037 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12038 * @context is session context
12039 * @message is message with valid raw and envelope member; envelope->hash
12040 * member will be changed during function run. Use envelope on heap only.
12041 * @return
12042 * IE_SUCCESS if message originates in ISDS
12043 * IE_NOTEQUAL if message is unknown to ISDS
12044 * other code for other errors */
12045 isds_error isds_verify_message_hash(struct isds_ctx *context,
12046 struct isds_message *message) {
12047 isds_error err = IE_SUCCESS;
12048 struct isds_hash *downloaded_hash = NULL;
12050 if (!context) return IE_INVALID_CONTEXT;
12051 zfree(context->long_message);
12052 if (!message) return IE_INVAL;
12054 if (!message->envelope) {
12055 isds_log_message(context,
12056 _("Given message structure is missing envelope"));
12057 return IE_INVAL;
12059 if (!message->raw) {
12060 isds_log_message(context,
12061 _("Given message structure is missing raw representation"));
12062 return IE_INVAL;
12065 err = isds_download_message_hash(context, message->envelope->dmID,
12066 &downloaded_hash);
12067 if (err) goto leave;
12069 err = isds_compute_message_hash(context, message,
12070 downloaded_hash->algorithm);
12071 if (err) goto leave;
12073 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12075 leave:
12076 isds_hash_free(&downloaded_hash);
12077 return err;
12081 /* Search for document by document ID in list of documents. IDs are compared
12082 * as UTF-8 string.
12083 * @documents is list of isds_documents
12084 * @id is document identifier
12085 * @return first matching document or NULL. */
12086 const struct isds_document *isds_find_document_by_id(
12087 const struct isds_list *documents, const char *id) {
12088 const struct isds_list *item;
12089 const struct isds_document *document;
12091 for (item = documents; item; item = item->next) {
12092 document = (struct isds_document *) item->data;
12093 if (!document) continue;
12095 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12096 return document;
12099 return NULL;
12103 /* Normalize @mime_type to be proper MIME type.
12104 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12105 * guess regular MIME type (e.g. "application/pdf").
12106 * @mime_type is UTF-8 encoded MIME type to fix
12107 * @return original @mime_type if no better interpretation exists, or
12108 * constant static UTF-8 encoded string with proper MIME type. */
12109 const char *isds_normalize_mime_type(const char *mime_type) {
12110 if (!mime_type) return NULL;
12112 for (size_t offset = 0;
12113 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12114 offset += 2) {
12115 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12116 extension_map_mime[offset]))
12117 return (const char *) extension_map_mime[offset + 1];
12120 return mime_type;
12124 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12125 struct isds_message **message);
12126 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12127 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12128 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12129 struct isds_address **address);
12131 int isds_message_free(struct isds_message **message);
12132 int isds_address_free(struct isds_address **address);
12136 /* Makes known all relevant namespaces to given XPath context
12137 * @xpath_ctx is XPath context
12138 * @message_ns selects proper message name space. Unsigned and signed
12139 * messages and delivery info's differ in prefix and URI. */
12140 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12141 const message_ns_type message_ns) {
12142 const xmlChar *message_namespace = NULL;
12144 if (!xpath_ctx) return IE_ERROR;
12146 switch(message_ns) {
12147 case MESSAGE_NS_1:
12148 message_namespace = BAD_CAST ISDS1_NS; break;
12149 case MESSAGE_NS_UNSIGNED:
12150 message_namespace = BAD_CAST ISDS_NS; break;
12151 case MESSAGE_NS_SIGNED_INCOMING:
12152 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12153 case MESSAGE_NS_SIGNED_OUTGOING:
12154 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12155 case MESSAGE_NS_SIGNED_DELIVERY:
12156 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12157 default:
12158 return IE_ENUM;
12161 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12162 return IE_ERROR;
12163 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12164 return IE_ERROR;
12165 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12166 return IE_ERROR;
12167 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12168 return IE_ERROR;
12169 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12170 return IE_ERROR;
12171 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12172 return IE_ERROR;
12173 return IE_SUCCESS;