Fix date parsing on Windows during daylight saving period
[libisds.git] / src / isds.c
blobc6c027929759d471adb0525aa81de642d7a2efb1
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 broken.tm_isdst = -1;
2304 offset = (char*)string + i;
2305 #else
2306 /* Parse date and time without subseconds and offset */
2307 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2308 if (!offset) {
2309 zfree(*time);
2310 return IE_DATE;
2312 #endif
2314 /* Get subseconds */
2315 if (*offset == '.' ) {
2316 offset++;
2318 /* Copy first 6 digits, pad it with zeros.
2319 * Current server implementation uses only millisecond resolution. */
2320 /* TODO: isdigit() is locale sensitive */
2321 for (i = 0;
2322 i < subsecond_resolution && isdigit(*offset);
2323 i++, offset++) {
2324 subseconds[i] = *offset;
2326 if (subsecond_resolution == i && isdigit(*offset)) {
2327 /* Check 7th digit for rounding */
2328 if (*offset >= '5') round_up = 1;
2329 offset++;
2331 for (; i < subsecond_resolution; i++) {
2332 subseconds[i] = '0';
2334 subseconds[subsecond_resolution] = '\0';
2336 /* Convert it into integer */
2337 long_number = strtol(subseconds, &endptr, 10);
2338 if (*endptr != '\0' || long_number == LONG_MIN ||
2339 long_number == LONG_MAX) {
2340 zfree(*time);
2341 return IE_DATE;
2343 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2344 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2345 * microseconds" and "the type shall be a signed integer capable of
2346 * storing values at least in the range [-1, 1000000]. */
2347 if (long_number < -1 || long_number >= 1000000) {
2348 zfree(*time);
2349 return IE_DATE;
2351 (*time)->tv_usec = long_number;
2353 /* Round the subseconds */
2354 if (round_up) {
2355 if (999999 == (*time)->tv_usec) {
2356 (*time)->tv_usec = 0;
2357 broken.tm_sec++;
2358 } else {
2359 (*time)->tv_usec++;
2363 /* move to the zone offset delimiter or signal NULL*/
2364 delim = strchr(offset, '-');
2365 if (!delim)
2366 delim = strchr(offset, '+');
2367 if (!delim)
2368 delim = strchr(offset, 'Z');
2369 offset = delim;
2372 /* Get zone offset */
2373 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2374 * "" equals to "Z" and it means UTC zone. */
2375 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2376 * colon separator */
2377 if (offset && (*offset == '-' || *offset == '+')) {
2378 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2379 zfree(*time);
2380 return IE_DATE;
2382 if (*offset == '+') {
2383 broken.tm_hour -= offset_hours;
2384 broken.tm_min -= offset_minutes;
2385 } else {
2386 broken.tm_hour += offset_hours;
2387 broken.tm_min += offset_minutes;
2391 /* Convert to time_t */
2392 (*time)->tv_sec = _isds_timegm(&broken);
2393 if ((*time)->tv_sec == (time_t) -1) {
2394 zfree(*time);
2395 return IE_DATE;
2398 return IE_SUCCESS;
2402 /* Convert unsigned int into isds_message_status.
2403 * @context is session context
2404 * @number is pointer to number value. NULL will be treated as invalid value.
2405 * @status is automatically reallocated status
2406 * @return IE_SUCCESS, or error code and free status */
2407 static isds_error uint2isds_message_status(struct isds_ctx *context,
2408 const unsigned long int *number, isds_message_status **status) {
2409 if (!context) return IE_INVALID_CONTEXT;
2410 if (!status) return IE_INVAL;
2412 free(*status); *status = NULL;
2413 if (!number) return IE_INVAL;
2415 if (*number < 1 || *number > 10) {
2416 isds_printf_message(context, _("Invalid message status value: %lu"),
2417 *number);
2418 return IE_ENUM;
2421 *status = malloc(sizeof(**status));
2422 if (!*status) return IE_NOMEM;
2424 **status = 1 << *number;
2425 return IE_SUCCESS;
2429 /* Convert event description string into isds_event members type and
2430 * description
2431 * @string is raw event description starting with event prefix
2432 * @event is structure where to store type and stripped description to
2433 * @return standard error code, unknown prefix is not classified as an error.
2434 * */
2435 static isds_error eventstring2event(const xmlChar *string,
2436 struct isds_event* event) {
2437 const xmlChar *known_prefixes[] = {
2438 BAD_CAST "EV0:",
2439 BAD_CAST "EV1:",
2440 BAD_CAST "EV2:",
2441 BAD_CAST "EV3:",
2442 BAD_CAST "EV4:",
2443 BAD_CAST "EV5:",
2444 BAD_CAST "EV11:",
2445 BAD_CAST "EV12:",
2446 BAD_CAST "EV13:"
2448 const isds_event_type types[] = {
2449 EVENT_ENTERED_SYSTEM,
2450 EVENT_ACCEPTED_BY_RECIPIENT,
2451 EVENT_ACCEPTED_BY_FICTION,
2452 EVENT_UNDELIVERABLE,
2453 EVENT_COMMERCIAL_ACCEPTED,
2454 EVENT_DELIVERED,
2455 EVENT_PRIMARY_LOGIN,
2456 EVENT_ENTRUSTED_LOGIN,
2457 EVENT_SYSCERT_LOGIN
2459 unsigned int index;
2460 size_t length;
2462 if (!string || !event) return IE_INVAL;
2464 if (!event->type) {
2465 event->type = malloc(sizeof(*event->type));
2466 if (!(event->type)) return IE_NOMEM;
2468 zfree(event->description);
2470 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2471 index++) {
2472 length = xmlUTF8Strlen(known_prefixes[index]);
2474 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2475 /* Prefix is known */
2476 *event->type = types[index];
2478 /* Strip prefix from description and spaces */
2479 /* TODO: Recognize all white spaces from UCS blank class and
2480 * operate on UTF-8 chars. */
2481 for (; string[length] != '\0' && string[length] == ' '; length++);
2482 event->description = strdup((char *) (string + length));
2483 if (!(event->description)) return IE_NOMEM;
2485 return IE_SUCCESS;
2489 /* Unknown event prefix.
2490 * XSD allows any string */
2491 char *string_locale = _isds_utf82locale((char *) string);
2492 isds_log(ILF_ISDS, ILL_WARNING,
2493 _("Unknown delivery info event prefix: %s\n"), string_locale);
2494 free(string_locale);
2496 *event->type = EVENT_UKNOWN;
2497 event->description = strdup((char *) string);
2498 if (!(event->description)) return IE_NOMEM;
2500 return IE_SUCCESS;
2504 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2505 * and leave label */
2506 #define EXTRACT_STRING(element, string) { \
2507 xmlXPathFreeObject(result); \
2508 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2509 if (NULL == (result)) { \
2510 err = IE_ERROR; \
2511 goto leave; \
2513 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2514 if (result->nodesetval->nodeNr > 1) { \
2515 isds_printf_message(context, _("Multiple %s element"), element); \
2516 err = IE_ERROR; \
2517 goto leave; \
2519 (string) = (char *) \
2520 xmlXPathCastNodeSetToString(result->nodesetval); \
2521 if (NULL == (string)) { \
2522 err = IE_ERROR; \
2523 goto leave; \
2528 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2530 char *string = NULL; \
2531 EXTRACT_STRING(element, string); \
2533 if (string) { \
2534 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2535 if (!(booleanPtr)) { \
2536 free(string); \
2537 err = IE_NOMEM; \
2538 goto leave; \
2541 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2542 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2543 *(booleanPtr) = 1; \
2544 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2545 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2546 *(booleanPtr) = 0; \
2547 else { \
2548 char *string_locale = _isds_utf82locale((char*)string); \
2549 isds_printf_message(context, \
2550 _("%s value is not valid boolean: %s"), \
2551 element, string_locale); \
2552 free(string_locale); \
2553 free(string); \
2554 err = IE_ERROR; \
2555 goto leave; \
2558 free(string); \
2562 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2564 char *string = NULL; \
2565 EXTRACT_STRING(element, string); \
2567 if (NULL == string) { \
2568 isds_printf_message(context, _("%s element is empty"), element); \
2569 err = IE_ERROR; \
2570 goto leave; \
2572 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2573 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2574 (boolean) = 1; \
2575 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2576 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2577 (boolean) = 0; \
2578 else { \
2579 char *string_locale = _isds_utf82locale((char*)string); \
2580 isds_printf_message(context, \
2581 _("%s value is not valid boolean: %s"), \
2582 element, string_locale); \
2583 free(string_locale); \
2584 free(string); \
2585 err = IE_ERROR; \
2586 goto leave; \
2589 free(string); \
2592 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2594 char *string = NULL; \
2595 EXTRACT_STRING(element, string); \
2596 if (string) { \
2597 long int number; \
2598 char *endptr; \
2600 number = strtol((char*)string, &endptr, 10); \
2602 if (*endptr != '\0') { \
2603 char *string_locale = _isds_utf82locale((char *)string); \
2604 isds_printf_message(context, \
2605 _("%s is not valid integer: %s"), \
2606 element, string_locale); \
2607 free(string_locale); \
2608 free(string); \
2609 err = IE_ISDS; \
2610 goto leave; \
2613 if (number == LONG_MIN || number == LONG_MAX) { \
2614 char *string_locale = _isds_utf82locale((char *)string); \
2615 isds_printf_message(context, \
2616 _("%s value out of range of long int: %s"), \
2617 element, string_locale); \
2618 free(string_locale); \
2619 free(string); \
2620 err = IE_ERROR; \
2621 goto leave; \
2624 free(string); string = NULL; \
2626 if (!(preallocated)) { \
2627 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2628 if (!(longintPtr)) { \
2629 err = IE_NOMEM; \
2630 goto leave; \
2633 *(longintPtr) = number; \
2637 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2639 char *string = NULL; \
2640 EXTRACT_STRING(element, string); \
2641 if (string) { \
2642 long int number; \
2643 char *endptr; \
2645 number = strtol((char*)string, &endptr, 10); \
2647 if (*endptr != '\0') { \
2648 char *string_locale = _isds_utf82locale((char *)string); \
2649 isds_printf_message(context, \
2650 _("%s is not valid integer: %s"), \
2651 element, string_locale); \
2652 free(string_locale); \
2653 free(string); \
2654 err = IE_ISDS; \
2655 goto leave; \
2658 if (number == LONG_MIN || number == LONG_MAX) { \
2659 char *string_locale = _isds_utf82locale((char *)string); \
2660 isds_printf_message(context, \
2661 _("%s value out of range of long int: %s"), \
2662 element, string_locale); \
2663 free(string_locale); \
2664 free(string); \
2665 err = IE_ERROR; \
2666 goto leave; \
2669 free(string); string = NULL; \
2670 if (number < 0) { \
2671 isds_printf_message(context, \
2672 _("%s value is negative: %ld"), element, number); \
2673 err = IE_ERROR; \
2674 goto leave; \
2677 if (!(preallocated)) { \
2678 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2679 if (!(ulongintPtr)) { \
2680 err = IE_NOMEM; \
2681 goto leave; \
2684 *(ulongintPtr) = number; \
2688 #define EXTRACT_DATE(element, tmPtr) { \
2689 char *string = NULL; \
2690 EXTRACT_STRING(element, string); \
2691 if (NULL != string) { \
2692 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2693 if (NULL == (tmPtr)) { \
2694 free(string); \
2695 err = IE_NOMEM; \
2696 goto leave; \
2698 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2699 if (err) { \
2700 if (err == IE_NOTSUP) { \
2701 err = IE_ISDS; \
2702 char *string_locale = _isds_utf82locale(string); \
2703 char *element_locale = _isds_utf82locale(element); \
2704 isds_printf_message(context, _("Invalid %s value: %s"), \
2705 element_locale, string_locale); \
2706 free(string_locale); \
2707 free(element_locale); \
2709 free(string); \
2710 goto leave; \
2712 free(string); \
2716 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2717 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2718 NULL); \
2719 if ((required) && (!string)) { \
2720 char *attribute_locale = _isds_utf82locale(attribute); \
2721 char *element_locale = \
2722 _isds_utf82locale((char *)xpath_ctx->node->name); \
2723 isds_printf_message(context, \
2724 _("Could not extract required %s attribute value from " \
2725 "%s element"), attribute_locale, element_locale); \
2726 free(element_locale); \
2727 free(attribute_locale); \
2728 err = IE_ERROR; \
2729 goto leave; \
2734 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2736 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2737 (xmlChar *) (string)); \
2738 if (!node) { \
2739 isds_printf_message(context, \
2740 _("Could not add %s child to %s element"), \
2741 element, (parent)->name); \
2742 err = IE_ERROR; \
2743 goto leave; \
2747 #define INSERT_STRING(parent, element, string) \
2748 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2750 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2752 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2753 else { INSERT_STRING(parent, element, "false"); } \
2756 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2758 if (booleanPtr) { \
2759 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2760 } else { \
2761 INSERT_STRING(parent, element, NULL); \
2765 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2766 if ((longintPtr)) { \
2767 /* FIXME: locale sensitive */ \
2768 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2769 err = IE_NOMEM; \
2770 goto leave; \
2772 INSERT_STRING(parent, element, buffer) \
2773 free(buffer); (buffer) = NULL; \
2774 } else { INSERT_STRING(parent, element, NULL) } \
2777 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2778 if ((ulongintPtr)) { \
2779 /* FIXME: locale sensitive */ \
2780 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2781 err = IE_NOMEM; \
2782 goto leave; \
2784 INSERT_STRING(parent, element, buffer) \
2785 free(buffer); (buffer) = NULL; \
2786 } else { INSERT_STRING(parent, element, NULL) } \
2789 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2791 /* FIXME: locale sensitive */ \
2792 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2793 err = IE_NOMEM; \
2794 goto leave; \
2796 INSERT_STRING(parent, element, buffer) \
2797 free(buffer); (buffer) = NULL; \
2800 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2801 * new attribute. */
2802 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2804 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2805 (xmlChar *) (string)); \
2806 if (!attribute_node) { \
2807 isds_printf_message(context, _("Could not add %s " \
2808 "attribute to %s element"), \
2809 (attribute), (parent)->name); \
2810 err = IE_ERROR; \
2811 goto leave; \
2815 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2816 if (string) { \
2817 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2818 if (length > (maximum)) { \
2819 isds_printf_message(context, \
2820 ngettext("%s has more than %d characters", \
2821 "%s has more than %d characters", (maximum)), \
2822 (name), (maximum)); \
2823 err = IE_2BIG; \
2824 goto leave; \
2826 if (length < (minimum)) { \
2827 isds_printf_message(context, \
2828 ngettext("%s has less than %d characters", \
2829 "%s has less than %d characters", (minimum)), \
2830 (name), (minimum)); \
2831 err = IE_2SMALL; \
2832 goto leave; \
2837 #define INSERT_ELEMENT(child, parent, element) \
2839 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2840 if (!(child)) { \
2841 isds_printf_message(context, \
2842 _("Could not add %s child to %s element"), \
2843 (element), (parent)->name); \
2844 err = IE_ERROR; \
2845 goto leave; \
2850 /* Find child element by name in given XPath context and switch context onto
2851 * it. The child must be uniq and must exist. Otherwise fails.
2852 * @context is ISDS context
2853 * @child is child element name
2854 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2855 * into it child. In error case, the @xpath_ctx keeps original value. */
2856 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2857 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2858 isds_error err = IE_SUCCESS;
2859 xmlXPathObjectPtr result = NULL;
2861 if (!context) return IE_INVALID_CONTEXT;
2862 if (!child || !xpath_ctx) return IE_INVAL;
2864 /* Find child */
2865 result = xmlXPathEvalExpression(child, xpath_ctx);
2866 if (!result) {
2867 err = IE_XML;
2868 goto leave;
2871 /* No match */
2872 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2873 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2874 char *child_locale = _isds_utf82locale((char*) child);
2875 isds_printf_message(context,
2876 _("%s element does not contain %s child"),
2877 parent_locale, child_locale);
2878 free(child_locale);
2879 free(parent_locale);
2880 err = IE_NOEXIST;
2881 goto leave;
2884 /* More matches */
2885 if (result->nodesetval->nodeNr > 1) {
2886 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2887 char *child_locale = _isds_utf82locale((char*) child);
2888 isds_printf_message(context,
2889 _("%s element contains multiple %s children"),
2890 parent_locale, child_locale);
2891 free(child_locale);
2892 free(parent_locale);
2893 err = IE_NOTUNIQ;
2894 goto leave;
2897 /* Switch context */
2898 xpath_ctx->node = result->nodesetval->nodeTab[0];
2900 leave:
2901 xmlXPathFreeObject(result);
2902 return err;
2907 #if HAVE_LIBCURL
2908 /* Find and convert XSD:gPersonName group in current node into structure
2909 * @context is ISDS context
2910 * @personName is automatically reallocated person name structure. If no member
2911 * value is found, will be freed.
2912 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2913 * elements
2914 * In case of error @personName will be freed. */
2915 static isds_error extract_gPersonName(struct isds_ctx *context,
2916 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2917 isds_error err = IE_SUCCESS;
2918 xmlXPathObjectPtr result = NULL;
2920 if (!context) return IE_INVALID_CONTEXT;
2921 if (!personName) return IE_INVAL;
2922 isds_PersonName_free(personName);
2923 if (!xpath_ctx) return IE_INVAL;
2926 *personName = calloc(1, sizeof(**personName));
2927 if (!*personName) {
2928 err = IE_NOMEM;
2929 goto leave;
2932 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2933 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2934 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2935 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2937 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2938 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2939 isds_PersonName_free(personName);
2941 leave:
2942 if (err) isds_PersonName_free(personName);
2943 xmlXPathFreeObject(result);
2944 return err;
2948 /* Find and convert XSD:gAddress group in current node into structure
2949 * @context is ISDS context
2950 * @address is automatically reallocated address structure. If no member
2951 * value is found, will be freed.
2952 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2953 * elements
2954 * In case of error @address will be freed. */
2955 static isds_error extract_gAddress(struct isds_ctx *context,
2956 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2957 isds_error err = IE_SUCCESS;
2958 xmlXPathObjectPtr result = NULL;
2960 if (!context) return IE_INVALID_CONTEXT;
2961 if (!address) return IE_INVAL;
2962 isds_Address_free(address);
2963 if (!xpath_ctx) return IE_INVAL;
2966 *address = calloc(1, sizeof(**address));
2967 if (!*address) {
2968 err = IE_NOMEM;
2969 goto leave;
2972 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2973 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2974 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2975 EXTRACT_STRING("isds:adNumberInMunicipality",
2976 (*address)->adNumberInMunicipality);
2977 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2978 EXTRACT_STRING("isds:adState", (*address)->adState);
2980 if (!(*address)->adCity && !(*address)->adStreet &&
2981 !(*address)->adNumberInStreet &&
2982 !(*address)->adNumberInMunicipality &&
2983 !(*address)->adZipCode && !(*address)->adState)
2984 isds_Address_free(address);
2986 leave:
2987 if (err) isds_Address_free(address);
2988 xmlXPathFreeObject(result);
2989 return err;
2993 /* Find and convert isds:biDate element in current node into structure
2994 * @context is ISDS context
2995 * @biDate is automatically reallocated birth date structure. If no member
2996 * value is found, will be freed.
2997 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2998 * element
2999 * In case of error @biDate will be freed. */
3000 static isds_error extract_BiDate(struct isds_ctx *context,
3001 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
3002 isds_error err = IE_SUCCESS;
3003 xmlXPathObjectPtr result = NULL;
3004 char *string = NULL;
3006 if (!context) return IE_INVALID_CONTEXT;
3007 if (!biDate) return IE_INVAL;
3008 zfree(*biDate);
3009 if (!xpath_ctx) return IE_INVAL;
3011 EXTRACT_STRING("isds:biDate", string);
3012 if (string) {
3013 *biDate = calloc(1, sizeof(**biDate));
3014 if (!*biDate) {
3015 err = IE_NOMEM;
3016 goto leave;
3018 err = _isds_datestring2tm((xmlChar *)string, *biDate);
3019 if (err) {
3020 if (err == IE_NOTSUP) {
3021 err = IE_ISDS;
3022 char *string_locale = _isds_utf82locale(string);
3023 isds_printf_message(context,
3024 _("Invalid isds:biDate value: %s"), string_locale);
3025 free(string_locale);
3027 goto leave;
3031 leave:
3032 if (err) zfree(*biDate);
3033 free(string);
3034 xmlXPathFreeObject(result);
3035 return err;
3039 /* Convert isds:dBOwnerInfo XML tree into structure
3040 * @context is ISDS context
3041 * @db_owner_info is automatically reallocated box owner info structure
3042 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
3043 * In case of error @db_owner_info will be freed. */
3044 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
3045 struct isds_DbOwnerInfo **db_owner_info,
3046 xmlXPathContextPtr xpath_ctx) {
3047 isds_error err = IE_SUCCESS;
3048 xmlXPathObjectPtr result = NULL;
3049 char *string = NULL;
3051 if (!context) return IE_INVALID_CONTEXT;
3052 if (!db_owner_info) return IE_INVAL;
3053 isds_DbOwnerInfo_free(db_owner_info);
3054 if (!xpath_ctx) return IE_INVAL;
3057 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3058 if (!*db_owner_info) {
3059 err = IE_NOMEM;
3060 goto leave;
3063 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
3065 EXTRACT_STRING("isds:dbType", string);
3066 if (string) {
3067 (*db_owner_info)->dbType =
3068 calloc(1, sizeof(*((*db_owner_info)->dbType)));
3069 if (!(*db_owner_info)->dbType) {
3070 err = IE_NOMEM;
3071 goto leave;
3073 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
3074 if (err) {
3075 zfree((*db_owner_info)->dbType);
3076 if (err == IE_ENUM) {
3077 err = IE_ISDS;
3078 char *string_locale = _isds_utf82locale(string);
3079 isds_printf_message(context, _("Unknown isds:dbType: %s"),
3080 string_locale);
3081 free(string_locale);
3083 goto leave;
3085 zfree(string);
3088 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
3090 err = extract_gPersonName(context, &(*db_owner_info)->personName,
3091 xpath_ctx);
3092 if (err) goto leave;
3094 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
3096 (*db_owner_info)->birthInfo =
3097 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
3098 if (!(*db_owner_info)->birthInfo) {
3099 err = IE_NOMEM;
3100 goto leave;
3102 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
3103 xpath_ctx);
3104 if (err) goto leave;
3105 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3106 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3107 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3108 if (!(*db_owner_info)->birthInfo->biDate &&
3109 !(*db_owner_info)->birthInfo->biCity &&
3110 !(*db_owner_info)->birthInfo->biCounty &&
3111 !(*db_owner_info)->birthInfo->biState)
3112 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3114 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3115 if (err) goto leave;
3117 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3118 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3119 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3120 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3121 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3123 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3125 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3126 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3127 (*db_owner_info)->dbOpenAddressing);
3129 leave:
3130 if (err) isds_DbOwnerInfo_free(db_owner_info);
3131 free(string);
3132 xmlXPathFreeObject(result);
3133 return err;
3137 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3138 * @context is session context
3139 * @owner is libisds structure with box description
3140 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3141 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3142 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
3144 isds_error err = IE_SUCCESS;
3145 xmlNodePtr node;
3146 xmlChar *string = NULL;
3148 if (!context) return IE_INVALID_CONTEXT;
3149 if (!owner || !db_owner_info) return IE_INVAL;
3152 /* Build XSD:tDbOwnerInfo */
3153 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3154 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3156 /* dbType */
3157 if (owner->dbType) {
3158 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
3159 if (!type_string) {
3160 isds_printf_message(context, _("Invalid dbType value: %d"),
3161 *(owner->dbType));
3162 err = IE_ENUM;
3163 goto leave;
3165 INSERT_STRING(db_owner_info, "dbType", type_string);
3167 INSERT_STRING(db_owner_info, "ic", owner->ic);
3168 if (owner->personName) {
3169 INSERT_STRING(db_owner_info, "pnFirstName",
3170 owner->personName->pnFirstName);
3171 INSERT_STRING(db_owner_info, "pnMiddleName",
3172 owner->personName->pnMiddleName);
3173 INSERT_STRING(db_owner_info, "pnLastName",
3174 owner->personName->pnLastName);
3175 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3176 owner->personName->pnLastNameAtBirth);
3178 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3179 if (owner->birthInfo) {
3180 if (owner->birthInfo->biDate) {
3181 if (!tm2datestring(owner->birthInfo->biDate, &string))
3182 INSERT_STRING(db_owner_info, "biDate", string);
3183 free(string); string = NULL;
3185 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
3186 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
3187 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
3189 if (owner->address) {
3190 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
3191 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
3192 INSERT_STRING(db_owner_info, "adNumberInStreet",
3193 owner->address->adNumberInStreet);
3194 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3195 owner->address->adNumberInMunicipality);
3196 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
3197 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
3199 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3200 INSERT_STRING(db_owner_info, "email", owner->email);
3201 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3203 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3204 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3206 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3207 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3209 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3211 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3212 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3213 owner->dbOpenAddressing);
3215 leave:
3216 free(string);
3217 return err;
3221 /* Convert XSD:tDbUserInfo XML tree into structure
3222 * @context is ISDS context
3223 * @db_user_info is automatically reallocated user info structure
3224 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3225 * In case of error @db_user_info will be freed. */
3226 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3227 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3228 isds_error err = IE_SUCCESS;
3229 xmlXPathObjectPtr result = NULL;
3230 char *string = NULL;
3232 if (!context) return IE_INVALID_CONTEXT;
3233 if (!db_user_info) return IE_INVAL;
3234 isds_DbUserInfo_free(db_user_info);
3235 if (!xpath_ctx) return IE_INVAL;
3238 *db_user_info = calloc(1, sizeof(**db_user_info));
3239 if (!*db_user_info) {
3240 err = IE_NOMEM;
3241 goto leave;
3244 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3246 EXTRACT_STRING("isds:userType", string);
3247 if (string) {
3248 (*db_user_info)->userType =
3249 calloc(1, sizeof(*((*db_user_info)->userType)));
3250 if (!(*db_user_info)->userType) {
3251 err = IE_NOMEM;
3252 goto leave;
3254 err = string2isds_UserType((xmlChar *)string,
3255 (*db_user_info)->userType);
3256 if (err) {
3257 zfree((*db_user_info)->userType);
3258 if (err == IE_ENUM) {
3259 err = IE_ISDS;
3260 char *string_locale = _isds_utf82locale(string);
3261 isds_printf_message(context,
3262 _("Unknown isds:userType value: %s"), string_locale);
3263 free(string_locale);
3265 goto leave;
3267 zfree(string);
3270 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3272 (*db_user_info)->personName =
3273 calloc(1, sizeof(*((*db_user_info)->personName)));
3274 if (!(*db_user_info)->personName) {
3275 err = IE_NOMEM;
3276 goto leave;
3279 err = extract_gPersonName(context, &(*db_user_info)->personName,
3280 xpath_ctx);
3281 if (err) goto leave;
3283 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3284 if (err) goto leave;
3286 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3287 if (err) goto leave;
3289 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3290 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3292 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3293 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3294 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3296 /* ???: Default value is "CZ" according specification. Should we provide
3297 * it? */
3298 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3300 leave:
3301 if (err) isds_DbUserInfo_free(db_user_info);
3302 free(string);
3303 xmlXPathFreeObject(result);
3304 return err;
3308 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3309 * @context is session context
3310 * @user is libisds structure with user description
3311 * @db_user_info is XML element of XSD:tDbUserInfo */
3312 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3313 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3315 isds_error err = IE_SUCCESS;
3316 xmlNodePtr node;
3317 xmlChar *string = NULL;
3319 if (!context) return IE_INVALID_CONTEXT;
3320 if (!user || !db_user_info) return IE_INVAL;
3322 /* Build XSD:tDbUserInfo */
3323 if (user->personName) {
3324 INSERT_STRING(db_user_info, "pnFirstName",
3325 user->personName->pnFirstName);
3326 INSERT_STRING(db_user_info, "pnMiddleName",
3327 user->personName->pnMiddleName);
3328 INSERT_STRING(db_user_info, "pnLastName",
3329 user->personName->pnLastName);
3330 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3331 user->personName->pnLastNameAtBirth);
3333 if (user->address) {
3334 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3335 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3336 INSERT_STRING(db_user_info, "adNumberInStreet",
3337 user->address->adNumberInStreet);
3338 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3339 user->address->adNumberInMunicipality);
3340 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3341 INSERT_STRING(db_user_info, "adState", user->address->adState);
3343 if (user->biDate) {
3344 if (!tm2datestring(user->biDate, &string))
3345 INSERT_STRING(db_user_info, "biDate", string);
3346 zfree(string);
3348 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3349 INSERT_STRING(db_user_info, "userID", user->userID);
3351 /* userType */
3352 if (user->userType) {
3353 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3354 if (!type_string) {
3355 isds_printf_message(context, _("Invalid userType value: %d"),
3356 *(user->userType));
3357 err = IE_ENUM;
3358 goto leave;
3360 INSERT_STRING(db_user_info, "userType", type_string);
3363 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3364 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3365 INSERT_STRING(db_user_info, "ic", user->ic);
3366 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3367 INSERT_STRING(db_user_info, "firmName", user->firmName);
3368 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3369 INSERT_STRING(db_user_info, "caCity", user->caCity);
3370 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3371 INSERT_STRING(db_user_info, "caState", user->caState);
3373 leave:
3374 free(string);
3375 return err;
3379 /* Convert XSD:tPDZRec XML tree into structure
3380 * @context is ISDS context
3381 * @permission is automatically reallocated commercial permission structure
3382 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3383 * In case of error @permission will be freed. */
3384 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3385 struct isds_commercial_permission **permission,
3386 xmlXPathContextPtr xpath_ctx) {
3387 isds_error err = IE_SUCCESS;
3388 xmlXPathObjectPtr result = NULL;
3389 char *string = NULL;
3391 if (!context) return IE_INVALID_CONTEXT;
3392 if (!permission) return IE_INVAL;
3393 isds_commercial_permission_free(permission);
3394 if (!xpath_ctx) return IE_INVAL;
3397 *permission = calloc(1, sizeof(**permission));
3398 if (!*permission) {
3399 err = IE_NOMEM;
3400 goto leave;
3403 EXTRACT_STRING("isds:PDZType", string);
3404 if (string) {
3405 err = string2isds_payment_type((xmlChar *)string,
3406 &(*permission)->type);
3407 if (err) {
3408 if (err == IE_ENUM) {
3409 err = IE_ISDS;
3410 char *string_locale = _isds_utf82locale(string);
3411 isds_printf_message(context,
3412 _("Unknown isds:PDZType value: %s"), string_locale);
3413 free(string_locale);
3415 goto leave;
3417 zfree(string);
3420 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3421 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3423 EXTRACT_STRING("isds:PDZExpire", string);
3424 if (string) {
3425 err = timestring2timeval((xmlChar *) string,
3426 &((*permission)->expiration));
3427 if (err) {
3428 char *string_locale = _isds_utf82locale(string);
3429 if (err == IE_DATE) err = IE_ISDS;
3430 isds_printf_message(context,
3431 _("Could not convert PDZExpire as ISO time: %s"),
3432 string_locale);
3433 free(string_locale);
3434 goto leave;
3436 zfree(string);
3439 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3440 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3442 leave:
3443 if (err) isds_commercial_permission_free(permission);
3444 free(string);
3445 xmlXPathFreeObject(result);
3446 return err;
3450 /* Convert XSD:tCiRecord XML tree into structure
3451 * @context is ISDS context
3452 * @event is automatically reallocated commercial credit event structure
3453 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3454 * In case of error @event will be freed. */
3455 static isds_error extract_CiRecord(struct isds_ctx *context,
3456 struct isds_credit_event **event,
3457 xmlXPathContextPtr xpath_ctx) {
3458 isds_error err = IE_SUCCESS;
3459 xmlXPathObjectPtr result = NULL;
3460 char *string = NULL;
3461 long int *number_ptr;
3463 if (!context) return IE_INVALID_CONTEXT;
3464 if (!event) return IE_INVAL;
3465 isds_credit_event_free(event);
3466 if (!xpath_ctx) return IE_INVAL;
3469 *event = calloc(1, sizeof(**event));
3470 if (!*event) {
3471 err = IE_NOMEM;
3472 goto leave;
3475 EXTRACT_STRING("isds:ciEventTime", string);
3476 if (string) {
3477 err = timestring2timeval((xmlChar *) string,
3478 &(*event)->time);
3479 if (err) {
3480 char *string_locale = _isds_utf82locale(string);
3481 if (err == IE_DATE) err = IE_ISDS;
3482 isds_printf_message(context,
3483 _("Could not convert ciEventTime as ISO time: %s"),
3484 string_locale);
3485 free(string_locale);
3486 goto leave;
3488 zfree(string);
3491 EXTRACT_STRING("isds:ciEventType", string);
3492 if (string) {
3493 err = string2isds_credit_event_type((xmlChar *)string,
3494 &(*event)->type);
3495 if (err) {
3496 if (err == IE_ENUM) {
3497 err = IE_ISDS;
3498 char *string_locale = _isds_utf82locale(string);
3499 isds_printf_message(context,
3500 _("Unknown isds:ciEventType value: %s"), string_locale);
3501 free(string_locale);
3503 goto leave;
3505 zfree(string);
3508 number_ptr = &((*event)->credit_change);
3509 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3510 number_ptr = &(*event)->new_credit;
3511 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3513 switch((*event)->type) {
3514 case ISDS_CREDIT_CHARGED:
3515 EXTRACT_STRING("isds:ciTransID",
3516 (*event)->details.charged.transaction);
3517 break;
3518 case ISDS_CREDIT_DISCHARGED:
3519 EXTRACT_STRING("isds:ciTransID",
3520 (*event)->details.discharged.transaction);
3521 break;
3522 case ISDS_CREDIT_MESSAGE_SENT:
3523 EXTRACT_STRING("isds:ciRecipientID",
3524 (*event)->details.message_sent.recipient);
3525 EXTRACT_STRING("isds:ciPDZID",
3526 (*event)->details.message_sent.message_id);
3527 break;
3528 case ISDS_CREDIT_STORAGE_SET:
3529 number_ptr = &((*event)->details.storage_set.new_capacity);
3530 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3531 EXTRACT_DATE("isds:ciNewFrom",
3532 (*event)->details.storage_set.new_valid_from);
3533 EXTRACT_DATE("isds:ciNewTo",
3534 (*event)->details.storage_set.new_valid_to);
3535 EXTRACT_LONGINT("isds:ciOldCapacity",
3536 (*event)->details.storage_set.old_capacity, 0);
3537 EXTRACT_DATE("isds:ciOldFrom",
3538 (*event)->details.storage_set.old_valid_from);
3539 EXTRACT_DATE("isds:ciOldTo",
3540 (*event)->details.storage_set.old_valid_to);
3541 EXTRACT_STRING("isds:ciDoneBy",
3542 (*event)->details.storage_set.initiator);
3543 break;
3544 case ISDS_CREDIT_EXPIRED:
3545 break;
3548 leave:
3549 if (err) isds_credit_event_free(event);
3550 free(string);
3551 xmlXPathFreeObject(result);
3552 return err;
3556 #endif /* HAVE_LIBCURL */
3559 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3560 * isds_envelope structure. The envelope is automatically allocated but not
3561 * reallocated. The date are just appended into envelope structure.
3562 * @context is ISDS context
3563 * @envelope is automatically allocated message envelope structure
3564 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3565 * In case of error @envelope will be freed. */
3566 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3567 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3568 isds_error err = IE_SUCCESS;
3569 xmlXPathObjectPtr result = NULL;
3571 if (!context) return IE_INVALID_CONTEXT;
3572 if (!envelope) return IE_INVAL;
3573 if (!xpath_ctx) return IE_INVAL;
3576 if (!*envelope) {
3577 /* Allocate envelope */
3578 *envelope = calloc(1, sizeof(**envelope));
3579 if (!*envelope) {
3580 err = IE_NOMEM;
3581 goto leave;
3583 } else {
3584 /* Else free former data */
3585 zfree((*envelope)->dmSenderOrgUnit);
3586 zfree((*envelope)->dmSenderOrgUnitNum);
3587 zfree((*envelope)->dbIDRecipient);
3588 zfree((*envelope)->dmRecipientOrgUnit);
3589 zfree((*envelope)->dmRecipientOrgUnitNum);
3590 zfree((*envelope)->dmToHands);
3591 zfree((*envelope)->dmAnnotation);
3592 zfree((*envelope)->dmRecipientRefNumber);
3593 zfree((*envelope)->dmSenderRefNumber);
3594 zfree((*envelope)->dmRecipientIdent);
3595 zfree((*envelope)->dmSenderIdent);
3596 zfree((*envelope)->dmLegalTitleLaw);
3597 zfree((*envelope)->dmLegalTitleYear);
3598 zfree((*envelope)->dmLegalTitleSect);
3599 zfree((*envelope)->dmLegalTitlePar);
3600 zfree((*envelope)->dmLegalTitlePoint);
3601 zfree((*envelope)->dmPersonalDelivery);
3602 zfree((*envelope)->dmAllowSubstDelivery);
3605 /* Extract envelope elements added by sender or ISDS
3606 * (XSD: gMessageEnvelopeSub type) */
3607 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3608 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3609 (*envelope)->dmSenderOrgUnitNum, 0);
3610 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3611 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3612 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3613 (*envelope)->dmRecipientOrgUnitNum, 0);
3614 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3615 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3616 EXTRACT_STRING("isds:dmRecipientRefNumber",
3617 (*envelope)->dmRecipientRefNumber);
3618 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3619 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3620 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3622 /* Extract envelope elements regarding law reference */
3623 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3624 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3625 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3626 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3627 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3629 /* Extract envelope other elements */
3630 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3631 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3632 (*envelope)->dmAllowSubstDelivery);
3634 leave:
3635 if (err) isds_envelope_free(envelope);
3636 xmlXPathFreeObject(result);
3637 return err;
3642 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3643 * isds_envelope structure. The envelope is automatically allocated but not
3644 * reallocated. The date are just appended into envelope structure.
3645 * @context is ISDS context
3646 * @envelope is automatically allocated message envelope structure
3647 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3648 * In case of error @envelope will be freed. */
3649 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3650 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3651 isds_error err = IE_SUCCESS;
3652 xmlXPathObjectPtr result = NULL;
3654 if (!context) return IE_INVALID_CONTEXT;
3655 if (!envelope) return IE_INVAL;
3656 if (!xpath_ctx) return IE_INVAL;
3659 if (!*envelope) {
3660 /* Allocate envelope */
3661 *envelope = calloc(1, sizeof(**envelope));
3662 if (!*envelope) {
3663 err = IE_NOMEM;
3664 goto leave;
3666 } else {
3667 /* Else free former data */
3668 zfree((*envelope)->dmID);
3669 zfree((*envelope)->dbIDSender);
3670 zfree((*envelope)->dmSender);
3671 zfree((*envelope)->dmSenderAddress);
3672 zfree((*envelope)->dmSenderType);
3673 zfree((*envelope)->dmRecipient);
3674 zfree((*envelope)->dmRecipientAddress);
3675 zfree((*envelope)->dmAmbiguousRecipient);
3678 /* Extract envelope elements added by ISDS
3679 * (XSD: gMessageEnvelope type) */
3680 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3681 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3682 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3683 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3684 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3685 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3686 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3687 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3688 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3689 (*envelope)->dmAmbiguousRecipient);
3691 /* Extract envelope elements added by sender and ISDS
3692 * (XSD: gMessageEnvelope type) */
3693 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3694 if (err) goto leave;
3696 leave:
3697 if (err) isds_envelope_free(envelope);
3698 xmlXPathFreeObject(result);
3699 return err;
3703 /* Convert other envelope elements from XML tree into isds_envelope structure:
3704 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3705 * The envelope is automatically allocated but not reallocated.
3706 * The data are just appended into envelope structure.
3707 * @context is ISDS context
3708 * @envelope is automatically allocated message envelope structure
3709 * @xpath_ctx is XPath context with current node as parent desired elements
3710 * In case of error @envelope will be freed. */
3711 static isds_error append_status_size_times(struct isds_ctx *context,
3712 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3713 isds_error err = IE_SUCCESS;
3714 xmlXPathObjectPtr result = NULL;
3715 char *string = NULL;
3716 unsigned long int *unumber = NULL;
3718 if (!context) return IE_INVALID_CONTEXT;
3719 if (!envelope) return IE_INVAL;
3720 if (!xpath_ctx) return IE_INVAL;
3723 if (!*envelope) {
3724 /* Allocate new */
3725 *envelope = calloc(1, sizeof(**envelope));
3726 if (!*envelope) {
3727 err = IE_NOMEM;
3728 goto leave;
3730 } else {
3731 /* Free old data */
3732 zfree((*envelope)->dmMessageStatus);
3733 zfree((*envelope)->dmAttachmentSize);
3734 zfree((*envelope)->dmDeliveryTime);
3735 zfree((*envelope)->dmAcceptanceTime);
3739 /* dmMessageStatus element is mandatory */
3740 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3741 if (!unumber) {
3742 isds_log_message(context,
3743 _("Missing mandatory sisds:dmMessageStatus integer"));
3744 err = IE_ISDS;
3745 goto leave;
3747 err = uint2isds_message_status(context, unumber,
3748 &((*envelope)->dmMessageStatus));
3749 if (err) {
3750 if (err == IE_ENUM) err = IE_ISDS;
3751 goto leave;
3753 free(unumber); unumber = NULL;
3755 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3758 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3759 if (string) {
3760 err = timestring2timeval((xmlChar *) string,
3761 &((*envelope)->dmDeliveryTime));
3762 if (err) {
3763 char *string_locale = _isds_utf82locale(string);
3764 if (err == IE_DATE) err = IE_ISDS;
3765 isds_printf_message(context,
3766 _("Could not convert dmDeliveryTime as ISO time: %s"),
3767 string_locale);
3768 free(string_locale);
3769 goto leave;
3771 zfree(string);
3774 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3775 if (string) {
3776 err = timestring2timeval((xmlChar *) string,
3777 &((*envelope)->dmAcceptanceTime));
3778 if (err) {
3779 char *string_locale = _isds_utf82locale(string);
3780 if (err == IE_DATE) err = IE_ISDS;
3781 isds_printf_message(context,
3782 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3783 string_locale);
3784 free(string_locale);
3785 goto leave;
3787 zfree(string);
3790 leave:
3791 if (err) isds_envelope_free(envelope);
3792 free(unumber);
3793 free(string);
3794 xmlXPathFreeObject(result);
3795 return err;
3799 /* Convert message type attribute of current element into isds_envelope
3800 * structure.
3801 * TODO: This function can be incorporated into append_status_size_times() as
3802 * they are called always together.
3803 * The envelope is automatically allocated but not reallocated.
3804 * The data are just appended into envelope structure.
3805 * @context is ISDS context
3806 * @envelope is automatically allocated message envelope structure
3807 * @xpath_ctx is XPath context with current node as parent of attribute
3808 * carrying message type
3809 * In case of error @envelope will be freed. */
3810 static isds_error append_message_type(struct isds_ctx *context,
3811 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3812 isds_error err = IE_SUCCESS;
3814 if (!context) return IE_INVALID_CONTEXT;
3815 if (!envelope) return IE_INVAL;
3816 if (!xpath_ctx) return IE_INVAL;
3819 if (!*envelope) {
3820 /* Allocate new */
3821 *envelope = calloc(1, sizeof(**envelope));
3822 if (!*envelope) {
3823 err = IE_NOMEM;
3824 goto leave;
3826 } else {
3827 /* Free old data */
3828 zfree((*envelope)->dmType);
3832 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3834 if (!(*envelope)->dmType) {
3835 /* Use default value */
3836 (*envelope)->dmType = strdup("V");
3837 if (!(*envelope)->dmType) {
3838 err = IE_NOMEM;
3839 goto leave;
3841 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3842 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3843 isds_printf_message(context,
3844 _("Message type in dmType attribute is not 1 character long: "
3845 "%s"),
3846 type_locale);
3847 free(type_locale);
3848 err = IE_ISDS;
3849 goto leave;
3852 leave:
3853 if (err) isds_envelope_free(envelope);
3854 return err;
3858 #if HAVE_LIBCURL
3859 /* Convert dmType isds_envelope member into XML attribute and append it to
3860 * current node.
3861 * @context is ISDS context
3862 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3863 * @dm_envelope is XML element the resulting attribute will be appended to.
3864 * @return error code, in case of error context' message is filled. */
3865 static isds_error insert_message_type(struct isds_ctx *context,
3866 const char *type, xmlNodePtr dm_envelope) {
3867 isds_error err = IE_SUCCESS;
3868 xmlAttrPtr attribute_node;
3870 if (!context) return IE_INVALID_CONTEXT;
3871 if (!dm_envelope) return IE_INVAL;
3873 /* Insert optional message type */
3874 if (type) {
3875 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3876 char *type_locale = _isds_utf82locale(type);
3877 isds_printf_message(context,
3878 _("Message type in envelope is not 1 character long: %s"),
3879 type_locale);
3880 free(type_locale);
3881 err = IE_INVAL;
3882 goto leave;
3884 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3887 leave:
3888 return err;
3890 #endif /* HAVE_LIBCURL */
3893 /* Extract message document into reallocated document structure
3894 * @context is ISDS context
3895 * @document is automatically reallocated message documents structure
3896 * @xpath_ctx is XPath context with current node as isds:dmFile
3897 * In case of error @document will be freed. */
3898 static isds_error extract_document(struct isds_ctx *context,
3899 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3900 isds_error err = IE_SUCCESS;
3901 xmlXPathObjectPtr result = NULL;
3902 xmlNodePtr file_node;
3903 char *string = NULL;
3905 if (!context) return IE_INVALID_CONTEXT;
3906 if (!document) return IE_INVAL;
3907 isds_document_free(document);
3908 if (!xpath_ctx) return IE_INVAL;
3909 file_node = xpath_ctx->node;
3911 *document = calloc(1, sizeof(**document));
3912 if (!*document) {
3913 err = IE_NOMEM;
3914 goto leave;
3917 /* Extract document meta data */
3918 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3919 if (context->normalize_mime_type) {
3920 const char *normalized_type =
3921 isds_normalize_mime_type((*document)->dmMimeType);
3922 if (NULL != normalized_type &&
3923 normalized_type != (*document)->dmMimeType) {
3924 char *new_type = strdup(normalized_type);
3925 if (NULL == new_type) {
3926 isds_printf_message(context,
3927 _("Not enough memory to normalize document MIME type"));
3928 err = IE_NOMEM;
3929 goto leave;
3931 free((*document)->dmMimeType);
3932 (*document)->dmMimeType = new_type;
3936 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3937 err = string2isds_FileMetaType((xmlChar*)string,
3938 &((*document)->dmFileMetaType));
3939 if (err) {
3940 char *meta_type_locale = _isds_utf82locale(string);
3941 isds_printf_message(context,
3942 _("Document has invalid dmFileMetaType attribute value: %s"),
3943 meta_type_locale);
3944 free(meta_type_locale);
3945 err = IE_ISDS;
3946 goto leave;
3948 zfree(string);
3950 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3951 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3952 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3953 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3956 /* Extract document data.
3957 * Base64 encoded blob or XML subtree must be presented. */
3959 /* Check for dmEncodedContent */
3960 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3961 xpath_ctx);
3962 if (!result) {
3963 err = IE_XML;
3964 goto leave;
3967 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3968 /* Here we have Base64 blob */
3969 (*document)->is_xml = 0;
3971 if (result->nodesetval->nodeNr > 1) {
3972 isds_printf_message(context,
3973 _("Document has more dmEncodedContent elements"));
3974 err = IE_ISDS;
3975 goto leave;
3978 xmlXPathFreeObject(result); result = NULL;
3979 EXTRACT_STRING("isds:dmEncodedContent", string);
3981 /* Decode non-empty document */
3982 if (string && string[0] != '\0') {
3983 (*document)->data_length =
3984 _isds_b64decode(string, &((*document)->data));
3985 if ((*document)->data_length == (size_t) -1) {
3986 isds_printf_message(context,
3987 _("Error while Base64-decoding document content"));
3988 err = IE_ERROR;
3989 goto leave;
3992 } else {
3993 /* No Base64 blob, try XML document */
3994 xmlXPathFreeObject(result); result = NULL;
3995 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3996 xpath_ctx);
3997 if (!result) {
3998 err = IE_XML;
3999 goto leave;
4002 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4003 /* Here we have XML document */
4004 (*document)->is_xml = 1;
4006 if (result->nodesetval->nodeNr > 1) {
4007 isds_printf_message(context,
4008 _("Document has more dmXMLContent elements"));
4009 err = IE_ISDS;
4010 goto leave;
4013 /* XXX: We cannot serialize the content simply because:
4014 * - XML document may point out of its scope (e.g. to message
4015 * envelope)
4016 * - isds:dmXMLContent can contain more elements, no element,
4017 * a text node only
4018 * - it's not the XML way
4019 * Thus we provide the only right solution: XML DOM. Let's
4020 * application to cope with this hot potato :) */
4021 (*document)->xml_node_list =
4022 result->nodesetval->nodeTab[0]->children;
4023 } else {
4024 /* No base64 blob, nor XML document */
4025 isds_printf_message(context,
4026 _("Document has no dmEncodedContent, nor dmXMLContent "
4027 "element"));
4028 err = IE_ISDS;
4029 goto leave;
4034 leave:
4035 if (err) isds_document_free(document);
4036 free(string);
4037 xmlXPathFreeObject(result);
4038 xpath_ctx->node = file_node;
4039 return err;
4044 /* Extract message documents into reallocated list of documents
4045 * @context is ISDS context
4046 * @documents is automatically reallocated message documents list structure
4047 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4048 * In case of error @documents will be freed. */
4049 static isds_error extract_documents(struct isds_ctx *context,
4050 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
4051 isds_error err = IE_SUCCESS;
4052 xmlXPathObjectPtr result = NULL;
4053 xmlNodePtr files_node;
4054 struct isds_list *document, *prev_document = NULL;
4056 if (!context) return IE_INVALID_CONTEXT;
4057 if (!documents) return IE_INVAL;
4058 isds_list_free(documents);
4059 if (!xpath_ctx) return IE_INVAL;
4060 files_node = xpath_ctx->node;
4062 /* Find documents */
4063 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
4064 if (!result) {
4065 err = IE_XML;
4066 goto leave;
4069 /* No match */
4070 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4071 isds_printf_message(context,
4072 _("Message does not contain any document"));
4073 err = IE_ISDS;
4074 goto leave;
4078 /* Iterate over documents */
4079 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4081 /* Allocate and append list item */
4082 document = calloc(1, sizeof(*document));
4083 if (!document) {
4084 err = IE_NOMEM;
4085 goto leave;
4087 document->destructor = (void (*)(void **))isds_document_free;
4088 if (i == 0) *documents = document;
4089 else prev_document->next = document;
4090 prev_document = document;
4092 /* Extract document */
4093 xpath_ctx->node = result->nodesetval->nodeTab[i];
4094 err = extract_document(context,
4095 (struct isds_document **) &(document->data), xpath_ctx);
4096 if (err) goto leave;
4100 leave:
4101 if (err) isds_list_free(documents);
4102 xmlXPathFreeObject(result);
4103 xpath_ctx->node = files_node;
4104 return err;
4108 #if HAVE_LIBCURL
4109 /* Convert isds:dmRecord XML tree into structure
4110 * @context is ISDS context
4111 * @envelope is automatically reallocated message envelope structure
4112 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4113 * In case of error @envelope will be freed. */
4114 static isds_error extract_DmRecord(struct isds_ctx *context,
4115 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4116 isds_error err = IE_SUCCESS;
4117 xmlXPathObjectPtr result = NULL;
4119 if (!context) return IE_INVALID_CONTEXT;
4120 if (!envelope) return IE_INVAL;
4121 isds_envelope_free(envelope);
4122 if (!xpath_ctx) return IE_INVAL;
4125 *envelope = calloc(1, sizeof(**envelope));
4126 if (!*envelope) {
4127 err = IE_NOMEM;
4128 goto leave;
4132 /* Extract tRecord data */
4133 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4135 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4136 * dmAcceptanceTime. */
4137 err = append_status_size_times(context, envelope, xpath_ctx);
4138 if (err) goto leave;
4140 /* Extract envelope elements added by sender and ISDS
4141 * (XSD: gMessageEnvelope type) */
4142 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4143 if (err) goto leave;
4145 /* Get message type */
4146 err = append_message_type(context, envelope, xpath_ctx);
4147 if (err) goto leave;
4150 leave:
4151 if (err) isds_envelope_free(envelope);
4152 xmlXPathFreeObject(result);
4153 return err;
4157 /* Convert XSD:tStateChangesRecord type XML tree into structure
4158 * @context is ISDS context
4159 * @changed_status is automatically reallocated message state change structure
4160 * @xpath_ctx is XPath context with current node as element of
4161 * XSD:tStateChangesRecord type
4162 * In case of error @changed_status will be freed. */
4163 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4164 struct isds_message_status_change **changed_status,
4165 xmlXPathContextPtr xpath_ctx) {
4166 isds_error err = IE_SUCCESS;
4167 xmlXPathObjectPtr result = NULL;
4168 unsigned long int *unumber = NULL;
4169 char *string = NULL;
4171 if (!context) return IE_INVALID_CONTEXT;
4172 if (!changed_status) return IE_INVAL;
4173 isds_message_status_change_free(changed_status);
4174 if (!xpath_ctx) return IE_INVAL;
4177 *changed_status = calloc(1, sizeof(**changed_status));
4178 if (!*changed_status) {
4179 err = IE_NOMEM;
4180 goto leave;
4184 /* Extract tGetStateChangesInput data */
4185 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4187 /* dmEventTime is mandatory */
4188 EXTRACT_STRING("isds:dmEventTime", string);
4189 if (string) {
4190 err = timestring2timeval((xmlChar *) string,
4191 &((*changed_status)->time));
4192 if (err) {
4193 char *string_locale = _isds_utf82locale(string);
4194 if (err == IE_DATE) err = IE_ISDS;
4195 isds_printf_message(context,
4196 _("Could not convert dmEventTime as ISO time: %s"),
4197 string_locale);
4198 free(string_locale);
4199 goto leave;
4201 zfree(string);
4204 /* dmMessageStatus element is mandatory */
4205 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4206 if (!unumber) {
4207 isds_log_message(context,
4208 _("Missing mandatory isds:dmMessageStatus integer"));
4209 err = IE_ISDS;
4210 goto leave;
4212 err = uint2isds_message_status(context, unumber,
4213 &((*changed_status)->dmMessageStatus));
4214 if (err) {
4215 if (err == IE_ENUM) err = IE_ISDS;
4216 goto leave;
4218 zfree(unumber);
4221 leave:
4222 free(unumber);
4223 free(string);
4224 if (err) isds_message_status_change_free(changed_status);
4225 xmlXPathFreeObject(result);
4226 return err;
4228 #endif /* HAVE_LIBCURL */
4231 /* Find and convert isds:dmHash XML tree into structure
4232 * @context is ISDS context
4233 * @envelope is automatically reallocated message hash structure
4234 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4235 * In case of error @hash will be freed. */
4236 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4237 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4238 isds_error err = IE_SUCCESS;
4239 xmlNodePtr old_ctx_node;
4240 xmlXPathObjectPtr result = NULL;
4241 char *string = NULL;
4243 if (!context) return IE_INVALID_CONTEXT;
4244 if (!hash) return IE_INVAL;
4245 isds_hash_free(hash);
4246 if (!xpath_ctx) return IE_INVAL;
4248 old_ctx_node = xpath_ctx->node;
4250 *hash = calloc(1, sizeof(**hash));
4251 if (!*hash) {
4252 err = IE_NOMEM;
4253 goto leave;
4256 /* Locate dmHash */
4257 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4258 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4259 err = IE_ISDS;
4260 goto leave;
4262 if (err) {
4263 err = IE_ERROR;
4264 goto leave;
4267 /* Get hash algorithm */
4268 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4269 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4270 if (err) {
4271 if (err == IE_ENUM) {
4272 char *string_locale = _isds_utf82locale(string);
4273 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4274 string_locale);
4275 free(string_locale);
4277 goto leave;
4279 zfree(string);
4281 /* Get hash value */
4282 EXTRACT_STRING(".", string);
4283 if (!string) {
4284 isds_printf_message(context,
4285 _("sisds:dmHash element is missing hash value"));
4286 err = IE_ISDS;
4287 goto leave;
4289 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4290 if ((*hash)->length == (size_t) -1) {
4291 isds_printf_message(context,
4292 _("Error while Base64-decoding hash value"));
4293 err = IE_ERROR;
4294 goto leave;
4297 leave:
4298 if (err) isds_hash_free(hash);
4299 free(string);
4300 xmlXPathFreeObject(result);
4301 xpath_ctx->node = old_ctx_node;
4302 return err;
4306 /* Find and append isds:dmQTimestamp XML tree into envelope.
4307 * Because one service is allowed to miss time-stamp content, and we think
4308 * other could too (flaw in specification), this function is deliberated and
4309 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4310 * @context is ISDS context
4311 * @envelope is automatically allocated envelope structure
4312 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4313 * child
4314 * In case of error @envelope will be freed. */
4315 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4316 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4317 isds_error err = IE_SUCCESS;
4318 xmlXPathObjectPtr result = NULL;
4319 char *string = NULL;
4321 if (!context) return IE_INVALID_CONTEXT;
4322 if (!envelope) return IE_INVAL;
4323 if (!xpath_ctx) {
4324 isds_envelope_free(envelope);
4325 return IE_INVAL;
4328 if (!*envelope) {
4329 *envelope = calloc(1, sizeof(**envelope));
4330 if (!*envelope) {
4331 err = IE_NOMEM;
4332 goto leave;
4334 } else {
4335 zfree((*envelope)->timestamp);
4336 (*envelope)->timestamp_length = 0;
4339 /* Get dmQTimestamp */
4340 EXTRACT_STRING("sisds:dmQTimestamp", string);
4341 if (!string) {
4342 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4343 goto leave;
4345 (*envelope)->timestamp_length =
4346 _isds_b64decode(string, &((*envelope)->timestamp));
4347 if ((*envelope)->timestamp_length == (size_t) -1) {
4348 isds_printf_message(context,
4349 _("Error while Base64-decoding time stamp value"));
4350 err = IE_ERROR;
4351 goto leave;
4354 leave:
4355 if (err) isds_envelope_free(envelope);
4356 free(string);
4357 xmlXPathFreeObject(result);
4358 return err;
4362 /* Convert XSD tReturnedMessage XML tree into message structure.
4363 * It does not store serialized XML tree into message->raw.
4364 * It does store (pointer to) parsed XML tree into message->xml if needed.
4365 * @context is ISDS context
4366 * @include_documents Use true if documents must be extracted
4367 * (tReturnedMessage XSD type), use false if documents shall be omitted
4368 * (tReturnedMessageEnvelope).
4369 * @message is automatically reallocated message structure
4370 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4371 * type
4372 * In case of error @message will be freed. */
4373 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4374 const _Bool include_documents, struct isds_message **message,
4375 xmlXPathContextPtr xpath_ctx) {
4376 isds_error err = IE_SUCCESS;
4377 xmlNodePtr message_node;
4379 if (!context) return IE_INVALID_CONTEXT;
4380 if (!message) return IE_INVAL;
4381 isds_message_free(message);
4382 if (!xpath_ctx) return IE_INVAL;
4385 *message = calloc(1, sizeof(**message));
4386 if (!*message) {
4387 err = IE_NOMEM;
4388 goto leave;
4391 /* Save message XPATH context node */
4392 message_node = xpath_ctx->node;
4395 /* Extract dmDM */
4396 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4397 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4398 if (err) { err = IE_ERROR; goto leave; }
4399 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4400 if (err) goto leave;
4402 if (include_documents) {
4403 struct isds_list *item;
4405 /* Extract dmFiles */
4406 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4407 xpath_ctx);
4408 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4409 err = IE_ISDS; goto leave;
4411 if (err) { err = IE_ERROR; goto leave; }
4412 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4413 if (err) goto leave;
4415 /* Store xmlDoc of this message if needed */
4416 /* Only if we got a XML document in all the documents. */
4417 for (item = (*message)->documents; item; item = item->next) {
4418 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4419 (*message)->xml = xpath_ctx->doc;
4420 break;
4426 /* Restore context to message */
4427 xpath_ctx->node = message_node;
4429 /* Extract dmHash */
4430 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4431 xpath_ctx);
4432 if (err) goto leave;
4434 /* Extract dmQTimestamp, */
4435 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4436 xpath_ctx);
4437 if (err) goto leave;
4439 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4440 * dmAcceptanceTime. */
4441 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4442 if (err) goto leave;
4444 /* Get message type */
4445 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4446 if (err) goto leave;
4448 leave:
4449 if (err) isds_message_free(message);
4450 return err;
4454 /* Extract message event into reallocated isds_event structure
4455 * @context is ISDS context
4456 * @event is automatically reallocated message event structure
4457 * @xpath_ctx is XPath context with current node as isds:dmEvent
4458 * In case of error @event will be freed. */
4459 static isds_error extract_event(struct isds_ctx *context,
4460 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4461 isds_error err = IE_SUCCESS;
4462 xmlXPathObjectPtr result = NULL;
4463 xmlNodePtr event_node;
4464 char *string = NULL;
4466 if (!context) return IE_INVALID_CONTEXT;
4467 if (!event) return IE_INVAL;
4468 isds_event_free(event);
4469 if (!xpath_ctx) return IE_INVAL;
4470 event_node = xpath_ctx->node;
4472 *event = calloc(1, sizeof(**event));
4473 if (!*event) {
4474 err = IE_NOMEM;
4475 goto leave;
4478 /* Extract event data.
4479 * All elements are optional according XSD. That's funny. */
4480 EXTRACT_STRING("sisds:dmEventTime", string);
4481 if (string) {
4482 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4483 if (err) {
4484 char *string_locale = _isds_utf82locale(string);
4485 if (err == IE_DATE) err = IE_ISDS;
4486 isds_printf_message(context,
4487 _("Could not convert dmEventTime as ISO time: %s"),
4488 string_locale);
4489 free(string_locale);
4490 goto leave;
4492 zfree(string);
4495 /* dmEventDescr element has prefix and the rest */
4496 EXTRACT_STRING("sisds:dmEventDescr", string);
4497 if (string) {
4498 err = eventstring2event((xmlChar *) string, *event);
4499 if (err) goto leave;
4500 zfree(string);
4503 leave:
4504 if (err) isds_event_free(event);
4505 free(string);
4506 xmlXPathFreeObject(result);
4507 xpath_ctx->node = event_node;
4508 return err;
4512 /* Convert element of XSD tEventsArray type from XML tree into
4513 * isds_list of isds_event's structure. The list is automatically reallocated.
4514 * @context is ISDS context
4515 * @events is automatically reallocated list of event structures
4516 * @xpath_ctx is XPath context with current node as tEventsArray
4517 * In case of error @events will be freed. */
4518 static isds_error extract_events(struct isds_ctx *context,
4519 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4520 isds_error err = IE_SUCCESS;
4521 xmlXPathObjectPtr result = NULL;
4522 xmlNodePtr events_node;
4523 struct isds_list *event, *prev_event = NULL;
4525 if (!context) return IE_INVALID_CONTEXT;
4526 if (!events) return IE_INVAL;
4527 if (!xpath_ctx) return IE_INVAL;
4528 events_node = xpath_ctx->node;
4530 /* Free old list */
4531 isds_list_free(events);
4533 /* Find events */
4534 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4535 if (!result) {
4536 err = IE_XML;
4537 goto leave;
4540 /* No match */
4541 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4542 isds_printf_message(context,
4543 _("Delivery info does not contain any event"));
4544 err = IE_ISDS;
4545 goto leave;
4549 /* Iterate over events */
4550 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4552 /* Allocate and append list item */
4553 event = calloc(1, sizeof(*event));
4554 if (!event) {
4555 err = IE_NOMEM;
4556 goto leave;
4558 event->destructor = (void (*)(void **))isds_event_free;
4559 if (i == 0) *events = event;
4560 else prev_event->next = event;
4561 prev_event = event;
4563 /* Extract event */
4564 xpath_ctx->node = result->nodesetval->nodeTab[i];
4565 err = extract_event(context,
4566 (struct isds_event **) &(event->data), xpath_ctx);
4567 if (err) goto leave;
4571 leave:
4572 if (err) isds_list_free(events);
4573 xmlXPathFreeObject(result);
4574 xpath_ctx->node = events_node;
4575 return err;
4579 #if HAVE_LIBCURL
4580 /* Insert Base64 encoded data as element with text child.
4581 * @context is session context
4582 * @parent is XML node to append @element with @data as child
4583 * @ns is XML namespace of @element, use NULL to inherit from @parent
4584 * @element is UTF-8 encoded name of new element
4585 * @data is bit stream to encode into @element
4586 * @length is size of @data in bytes
4587 * @return standard error code and fill long error message if needed */
4588 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4589 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4590 const void *data, size_t length) {
4591 isds_error err = IE_SUCCESS;
4592 xmlNodePtr node;
4594 if (!context) return IE_INVALID_CONTEXT;
4595 if (!data && length > 0) return IE_INVAL;
4596 if (!parent || !element) return IE_INVAL;
4598 xmlChar *base64data = NULL;
4599 base64data = (xmlChar *) _isds_b64encode(data, length);
4600 if (!base64data) {
4601 isds_printf_message(context,
4602 ngettext("Not enough memory to encode %zd byte into Base64",
4603 "Not enough memory to encode %zd bytes into Base64",
4604 length),
4605 length);
4606 err = IE_NOMEM;
4607 goto leave;
4609 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4611 leave:
4612 free(base64data);
4613 return err;
4617 /* Convert isds_document structure into XML tree and append to dmFiles node.
4618 * @context is session context
4619 * @document is ISDS document
4620 * @dm_files is XML element the resulting tree will be appended to as a child.
4621 * @return error code, in case of error context' message is filled. */
4622 static isds_error insert_document(struct isds_ctx *context,
4623 struct isds_document *document, xmlNodePtr dm_files) {
4624 isds_error err = IE_SUCCESS;
4625 xmlNodePtr new_file = NULL, file = NULL, node;
4626 xmlAttrPtr attribute_node;
4628 if (!context) return IE_INVALID_CONTEXT;
4629 if (!document || !dm_files) return IE_INVAL;
4631 /* Allocate new dmFile */
4632 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4633 if (!new_file) {
4634 isds_printf_message(context, _("Could not allocate main dmFile"));
4635 err = IE_ERROR;
4636 goto leave;
4638 /* Append the new dmFile.
4639 * XXX: Main document must go first */
4640 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4641 file = xmlAddPrevSibling(dm_files->children, new_file);
4642 else
4643 file = xmlAddChild(dm_files, new_file);
4645 if (!file) {
4646 xmlFreeNode(new_file); new_file = NULL;
4647 isds_printf_message(context, _("Could not add dmFile child to "
4648 "%s element"), dm_files->name);
4649 err = IE_ERROR;
4650 goto leave;
4653 /* @dmMimeType is required */
4654 if (!document->dmMimeType) {
4655 isds_log_message(context,
4656 _("Document is missing mandatory MIME type definition"));
4657 err = IE_INVAL;
4658 goto leave;
4660 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4662 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4663 if (!string) {
4664 isds_printf_message(context,
4665 _("Document has unknown dmFileMetaType: %ld"),
4666 document->dmFileMetaType);
4667 err = IE_ENUM;
4668 goto leave;
4670 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4672 if (document->dmFileGuid) {
4673 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4675 if (document->dmUpFileGuid) {
4676 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4679 /* @dmFileDescr is required */
4680 if (!document->dmFileDescr) {
4681 isds_log_message(context,
4682 _("Document is missing mandatory description (title)"));
4683 err = IE_INVAL;
4684 goto leave;
4686 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4688 if (document->dmFormat) {
4689 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4693 /* Insert content (body) of the document. */
4694 if (document->is_xml) {
4695 /* XML document requested */
4697 /* Allocate new dmXMLContent */
4698 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4699 if (!xmlcontent) {
4700 isds_printf_message(context,
4701 _("Could not allocate dmXMLContent element"));
4702 err = IE_ERROR;
4703 goto leave;
4705 /* Append it */
4706 node = xmlAddChild(file, xmlcontent);
4707 if (!node) {
4708 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4709 isds_printf_message(context,
4710 _("Could not add dmXMLContent child to %s element"),
4711 file->name);
4712 err = IE_ERROR;
4713 goto leave;
4716 /* Copy non-empty node list */
4717 if (document->xml_node_list) {
4718 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4719 document->xml_node_list);
4720 if (!content) {
4721 isds_printf_message(context,
4722 _("Not enough memory to copy XML document"));
4723 err = IE_NOMEM;
4724 goto leave;
4727 if (!xmlAddChildList(node, content)) {
4728 xmlFreeNodeList(content);
4729 isds_printf_message(context,
4730 _("Error while adding XML document into dmXMLContent"));
4731 err = IE_XML;
4732 goto leave;
4734 /* XXX: We cannot free the content here because it's part of node's
4735 * document since now. It will be freed with it automatically. */
4737 } else {
4738 /* Binary document requested */
4739 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4740 document->data, document->data_length);
4741 if (err) goto leave;
4744 leave:
4745 return err;
4749 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4750 * The copy must be preallocated, the date are just appended into structure.
4751 * @context is ISDS context
4752 * @copy is message copy structure
4753 * @xpath_ctx is XPath context with current node as tMStatus */
4754 static isds_error append_TMStatus(struct isds_ctx *context,
4755 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4756 isds_error err = IE_SUCCESS;
4757 xmlXPathObjectPtr result = NULL;
4758 char *code = NULL, *message = NULL;
4760 if (!context) return IE_INVALID_CONTEXT;
4761 if (!copy || !xpath_ctx) return IE_INVAL;
4763 /* Free old values */
4764 zfree(copy->dmStatus);
4765 zfree(copy->dmID);
4767 /* Get error specific to this copy */
4768 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4769 if (!code) {
4770 isds_log_message(context,
4771 _("Missing isds:dmStatusCode under "
4772 "XSD:tMStatus type element"));
4773 err = IE_ISDS;
4774 goto leave;
4777 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4778 /* This copy failed */
4779 copy->error = IE_ISDS;
4780 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4781 if (message) {
4782 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4783 if (!copy->dmStatus) {
4784 copy->dmStatus = code;
4785 code = NULL;
4787 } else {
4788 copy->dmStatus = code;
4789 code = NULL;
4791 } else {
4792 /* This copy succeeded. In this case only, message ID is valid */
4793 copy->error = IE_SUCCESS;
4795 EXTRACT_STRING("isds:dmID", copy->dmID);
4796 if (!copy->dmID) {
4797 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4798 "but did not returned assigned message ID\n"));
4799 err = IE_ISDS;
4803 leave:
4804 free(code);
4805 free(message);
4806 xmlXPathFreeObject(result);
4807 return err;
4811 /* Insert struct isds_approval data (box approval) into XML tree
4812 * @context is session context
4813 * @approval is libisds structure with approval description. NULL is
4814 * acceptable.
4815 * @parent is XML element to append @approval to */
4816 static isds_error insert_GExtApproval(struct isds_ctx *context,
4817 const struct isds_approval *approval, xmlNodePtr parent) {
4819 isds_error err = IE_SUCCESS;
4820 xmlNodePtr node;
4822 if (!context) return IE_INVALID_CONTEXT;
4823 if (!parent) return IE_INVAL;
4825 if (!approval) return IE_SUCCESS;
4827 /* Build XSD:gExtApproval */
4828 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4829 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4831 leave:
4832 return err;
4836 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4837 * code
4838 * @context is session context
4839 * @service_name is name of SERVICE_DB_ACCESS
4840 * @response is reallocated server SOAP body response as XML document
4841 * @raw_response is reallocated bit stream with response body. Use
4842 * NULL if you don't care
4843 * @raw_response_length is size of @raw_response in bytes
4844 * @code is reallocated ISDS status code
4845 * @status_message is reallocated ISDS status message
4846 * @return error coded from lower layer, context message will be set up
4847 * appropriately. */
4848 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4849 const xmlChar *service_name,
4850 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4851 xmlChar **code, xmlChar **status_message) {
4853 isds_error err = IE_SUCCESS;
4854 char *service_name_locale = NULL;
4855 xmlNodePtr request = NULL, node;
4856 xmlNsPtr isds_ns = NULL;
4858 if (!context) return IE_INVALID_CONTEXT;
4859 if (!service_name) return IE_INVAL;
4860 if (!response || !code || !status_message) return IE_INVAL;
4861 if (!raw_response_length && raw_response) return IE_INVAL;
4863 /* Free output argument */
4864 xmlFreeDoc(*response); *response = NULL;
4865 if (raw_response) zfree(*raw_response);
4866 zfree(*code);
4867 zfree(*status_message);
4870 /* Check if connection is established
4871 * TODO: This check should be done downstairs. */
4872 if (!context->curl) return IE_CONNECTION_CLOSED;
4874 service_name_locale = _isds_utf82locale((char*)service_name);
4875 if (!service_name_locale) {
4876 err = IE_NOMEM;
4877 goto leave;
4880 /* Build request */
4881 request = xmlNewNode(NULL, service_name);
4882 if (!request) {
4883 isds_printf_message(context,
4884 _("Could not build %s request"), service_name_locale);
4885 err = IE_ERROR;
4886 goto leave;
4888 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4889 if(!isds_ns) {
4890 isds_log_message(context, _("Could not create ISDS name space"));
4891 err = IE_ERROR;
4892 goto leave;
4894 xmlSetNs(request, isds_ns);
4897 /* Add XSD:tDummyInput child */
4898 INSERT_STRING(request, "dbDummy", NULL);
4901 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4902 service_name_locale);
4904 /* Send request */
4905 err = _isds(context, SERVICE_DB_ACCESS, request, response,
4906 raw_response, raw_response_length);
4907 xmlFreeNode(request); request = NULL;
4909 if (err) {
4910 isds_log(ILF_ISDS, ILL_DEBUG,
4911 _("Processing ISDS response on %s request failed\n"),
4912 service_name_locale);
4913 goto leave;
4916 /* Check for response status */
4917 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4918 code, status_message, NULL);
4919 if (err) {
4920 isds_log(ILF_ISDS, ILL_DEBUG,
4921 _("ISDS response on %s request is missing status\n"),
4922 service_name_locale);
4923 goto leave;
4926 /* Request processed, but nothing found */
4927 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4928 char *code_locale = _isds_utf82locale((char*) *code);
4929 char *status_message_locale =
4930 _isds_utf82locale((char*) *status_message);
4931 isds_log(ILF_ISDS, ILL_DEBUG,
4932 _("Server refused %s request (code=%s, message=%s)\n"),
4933 service_name_locale, code_locale, status_message_locale);
4934 isds_log_message(context, status_message_locale);
4935 free(code_locale);
4936 free(status_message_locale);
4937 err = IE_ISDS;
4938 goto leave;
4941 leave:
4942 free(service_name_locale);
4943 xmlFreeNode(request);
4944 return err;
4946 #endif
4949 /* Get data about logged in user and his box. */
4950 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4951 struct isds_DbOwnerInfo **db_owner_info) {
4952 isds_error err = IE_SUCCESS;
4953 #if HAVE_LIBCURL
4954 xmlDocPtr response = NULL;
4955 xmlChar *code = NULL, *message = NULL;
4956 xmlXPathContextPtr xpath_ctx = NULL;
4957 xmlXPathObjectPtr result = NULL;
4958 char *string = NULL;
4959 #endif
4961 if (!context) return IE_INVALID_CONTEXT;
4962 zfree(context->long_message);
4963 if (!db_owner_info) return IE_INVAL;
4964 isds_DbOwnerInfo_free(db_owner_info);
4966 #if HAVE_LIBCURL
4967 /* Check if connection is established */
4968 if (!context->curl) return IE_CONNECTION_CLOSED;
4971 /* Do request and check for success */
4972 err = build_send_check_dbdummy_request(context,
4973 BAD_CAST "GetOwnerInfoFromLogin",
4974 &response, NULL, NULL, &code, &message);
4975 if (err) goto leave;
4978 /* Extract data */
4979 /* Prepare structure */
4980 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4981 if (!*db_owner_info) {
4982 err = IE_NOMEM;
4983 goto leave;
4985 xpath_ctx = xmlXPathNewContext(response);
4986 if (!xpath_ctx) {
4987 err = IE_ERROR;
4988 goto leave;
4990 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4991 err = IE_ERROR;
4992 goto leave;
4995 /* Set context node */
4996 result = xmlXPathEvalExpression(BAD_CAST
4997 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4998 if (!result) {
4999 err = IE_ERROR;
5000 goto leave;
5002 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5003 isds_log_message(context, _("Missing dbOwnerInfo element"));
5004 err = IE_ISDS;
5005 goto leave;
5007 if (result->nodesetval->nodeNr > 1) {
5008 isds_log_message(context, _("Multiple dbOwnerInfo element"));
5009 err = IE_ISDS;
5010 goto leave;
5012 xpath_ctx->node = result->nodesetval->nodeTab[0];
5013 xmlXPathFreeObject(result); result = NULL;
5015 /* Extract it */
5016 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
5019 leave:
5020 if (err) {
5021 isds_DbOwnerInfo_free(db_owner_info);
5024 free(string);
5025 xmlXPathFreeObject(result);
5026 xmlXPathFreeContext(xpath_ctx);
5028 free(code);
5029 free(message);
5030 xmlFreeDoc(response);
5032 if (!err)
5033 isds_log(ILF_ISDS, ILL_DEBUG,
5034 _("GetOwnerInfoFromLogin request processed by server "
5035 "successfully.\n"));
5036 #else /* not HAVE_LIBCURL */
5037 err = IE_NOTSUP;
5038 #endif
5040 return err;
5044 /* Get data about logged in user. */
5045 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
5046 struct isds_DbUserInfo **db_user_info) {
5047 isds_error err = IE_SUCCESS;
5048 #if HAVE_LIBCURL
5049 xmlDocPtr response = NULL;
5050 xmlChar *code = NULL, *message = NULL;
5051 xmlXPathContextPtr xpath_ctx = NULL;
5052 xmlXPathObjectPtr result = NULL;
5053 #endif
5055 if (!context) return IE_INVALID_CONTEXT;
5056 zfree(context->long_message);
5057 if (!db_user_info) return IE_INVAL;
5058 isds_DbUserInfo_free(db_user_info);
5060 #if HAVE_LIBCURL
5061 /* Check if connection is established */
5062 if (!context->curl) return IE_CONNECTION_CLOSED;
5065 /* Do request and check for success */
5066 err = build_send_check_dbdummy_request(context,
5067 BAD_CAST "GetUserInfoFromLogin",
5068 &response, NULL, NULL, &code, &message);
5069 if (err) goto leave;
5072 /* Extract data */
5073 /* Prepare structure */
5074 *db_user_info = calloc(1, sizeof(**db_user_info));
5075 if (!*db_user_info) {
5076 err = IE_NOMEM;
5077 goto leave;
5079 xpath_ctx = xmlXPathNewContext(response);
5080 if (!xpath_ctx) {
5081 err = IE_ERROR;
5082 goto leave;
5084 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5085 err = IE_ERROR;
5086 goto leave;
5089 /* Set context node */
5090 result = xmlXPathEvalExpression(BAD_CAST
5091 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
5092 if (!result) {
5093 err = IE_ERROR;
5094 goto leave;
5096 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5097 isds_log_message(context, _("Missing dbUserInfo element"));
5098 err = IE_ISDS;
5099 goto leave;
5101 if (result->nodesetval->nodeNr > 1) {
5102 isds_log_message(context, _("Multiple dbUserInfo element"));
5103 err = IE_ISDS;
5104 goto leave;
5106 xpath_ctx->node = result->nodesetval->nodeTab[0];
5107 xmlXPathFreeObject(result); result = NULL;
5109 /* Extract it */
5110 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5112 leave:
5113 if (err) {
5114 isds_DbUserInfo_free(db_user_info);
5117 xmlXPathFreeObject(result);
5118 xmlXPathFreeContext(xpath_ctx);
5120 free(code);
5121 free(message);
5122 xmlFreeDoc(response);
5124 if (!err)
5125 isds_log(ILF_ISDS, ILL_DEBUG,
5126 _("GetUserInfoFromLogin request processed by server "
5127 "successfully.\n"));
5128 #else /* not HAVE_LIBCURL */
5129 err = IE_NOTSUP;
5130 #endif
5132 return err;
5136 /* Get expiration time of current password
5137 * @context is session context
5138 * @expiration is automatically reallocated time when password expires. If
5139 * password expiration is disabled, NULL will be returned. In case of error
5140 * it will be nulled too. */
5141 isds_error isds_get_password_expiration(struct isds_ctx *context,
5142 struct timeval **expiration) {
5143 isds_error err = IE_SUCCESS;
5144 #if HAVE_LIBCURL
5145 xmlDocPtr response = NULL;
5146 xmlChar *code = NULL, *message = NULL;
5147 xmlXPathContextPtr xpath_ctx = NULL;
5148 xmlXPathObjectPtr result = NULL;
5149 char *string = NULL;
5150 #endif
5152 if (!context) return IE_INVALID_CONTEXT;
5153 zfree(context->long_message);
5154 if (!expiration) return IE_INVAL;
5155 zfree(*expiration);
5157 #if HAVE_LIBCURL
5158 /* Check if connection is established */
5159 if (!context->curl) return IE_CONNECTION_CLOSED;
5162 /* Do request and check for success */
5163 err = build_send_check_dbdummy_request(context,
5164 BAD_CAST "GetPasswordInfo",
5165 &response, NULL, NULL, &code, &message);
5166 if (err) goto leave;
5169 /* Extract data */
5170 xpath_ctx = xmlXPathNewContext(response);
5171 if (!xpath_ctx) {
5172 err = IE_ERROR;
5173 goto leave;
5175 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5176 err = IE_ERROR;
5177 goto leave;
5180 /* Set context node */
5181 result = xmlXPathEvalExpression(BAD_CAST
5182 "/isds:GetPasswordInfoResponse", xpath_ctx);
5183 if (!result) {
5184 err = IE_ERROR;
5185 goto leave;
5187 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5188 isds_log_message(context,
5189 _("Missing GetPasswordInfoResponse element"));
5190 err = IE_ISDS;
5191 goto leave;
5193 if (result->nodesetval->nodeNr > 1) {
5194 isds_log_message(context,
5195 _("Multiple GetPasswordInfoResponse element"));
5196 err = IE_ISDS;
5197 goto leave;
5199 xpath_ctx->node = result->nodesetval->nodeTab[0];
5200 xmlXPathFreeObject(result); result = NULL;
5202 /* Extract expiration date */
5203 EXTRACT_STRING("isds:pswExpDate", string);
5204 if (string) {
5205 /* And convert it if any returned. Otherwise expiration is disabled. */
5206 err = timestring2timeval((xmlChar *) string, expiration);
5207 if (err) {
5208 char *string_locale = _isds_utf82locale(string);
5209 if (err == IE_DATE) err = IE_ISDS;
5210 isds_printf_message(context,
5211 _("Could not convert pswExpDate as ISO time: %s"),
5212 string_locale);
5213 free(string_locale);
5214 goto leave;
5218 leave:
5219 if (err) {
5220 if (*expiration) {
5221 zfree(*expiration);
5225 free(string);
5226 xmlXPathFreeObject(result);
5227 xmlXPathFreeContext(xpath_ctx);
5229 free(code);
5230 free(message);
5231 xmlFreeDoc(response);
5233 if (!err)
5234 isds_log(ILF_ISDS, ILL_DEBUG,
5235 _("GetPasswordInfo request processed by server "
5236 "successfully.\n"));
5237 #else /* not HAVE_LIBCURL */
5238 err = IE_NOTSUP;
5239 #endif
5241 return err;
5245 #if HAVE_LIBCURL
5246 /* Request delivering new TOTP code from ISDS through side channel before
5247 * changing password.
5248 * @context is session context
5249 * @password is current password.
5250 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5251 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5252 * function for more details.
5253 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5254 * NULL, if you don't care.
5255 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5256 * error code. */
5257 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5258 const char *password, struct isds_otp *otp, char **refnumber) {
5259 isds_error err = IE_SUCCESS;
5260 char *saved_url = NULL; /* No copy */
5261 #if HAVE_CURL_REAUTHORIZATION_BUG
5262 CURL *saved_curl = NULL; /* No copy */
5263 #endif
5264 xmlNsPtr isds_ns = NULL;
5265 xmlNodePtr request = NULL;
5266 xmlDocPtr response = NULL;
5267 xmlChar *code = NULL, *message = NULL;
5268 const xmlChar *codes[] = {
5269 BAD_CAST "2300",
5270 BAD_CAST "2301",
5271 BAD_CAST "2302"
5273 const char *meanings[] = {
5274 N_("Unexpected error"),
5275 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5276 N_("One-time code could not been sent. Try later again.")
5278 const isds_otp_resolution resolutions[] = {
5279 OTP_RESOLUTION_UNKNOWN,
5280 OTP_RESOLUTION_TO_FAST,
5281 OTP_RESOLUTION_TOTP_NOT_SENT
5284 if (NULL == context) return IE_INVALID_CONTEXT;
5285 zfree(context->long_message);
5286 if (NULL == password) {
5287 isds_log_message(context,
5288 _("Second argument (password) of isds_change_password() "
5289 "is NULL"));
5290 return IE_INVAL;
5293 /* Check if connection is established
5294 * TODO: This check should be done downstairs. */
5295 if (!context->curl) return IE_CONNECTION_CLOSED;
5297 if (!context->otp) {
5298 isds_log_message(context, _("This function requires OTP-authenticated "
5299 "context"));
5300 return IE_INVALID_CONTEXT;
5302 if (NULL == otp) {
5303 isds_log_message(context, _("If one-time password authentication "
5304 "method is in use, requesting new OTP code requires "
5305 "one-time credentials argument either"));
5306 return IE_INVAL;
5308 if (otp->method != OTP_TIME) {
5309 isds_log_message(context, _("Requesting new time-based OTP code from "
5310 "server requires one-time password authentication "
5311 "method"));
5312 return IE_INVAL;
5314 if (otp->otp_code != NULL) {
5315 isds_log_message(context, _("Requesting new time-based OTP code from "
5316 "server requires undefined OTP code member in "
5317 "one-time credentials argument"));
5318 return IE_INVAL;
5322 /* Build request */
5323 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5324 if (!request) {
5325 isds_log_message(context, _("Could not build SendSMSCode request"));
5326 return IE_ERROR;
5328 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5329 if(!isds_ns) {
5330 isds_log_message(context, _("Could not create ISDS name space"));
5331 xmlFreeNode(request);
5332 return IE_ERROR;
5334 xmlSetNs(request, isds_ns);
5336 /* Change URL temporarily for sending this request only */
5338 char *new_url = NULL;
5339 if ((err = _isds_build_url_from_context(context,
5340 "%1$.*2$sasws/changePassword", &new_url))) {
5341 goto leave;
5343 saved_url = context->url;
5344 context->url = new_url;
5347 /* Store credentials for sending this request only */
5348 context->otp_credentials = otp;
5349 _isds_discard_credentials(context, 0);
5350 if ((err = _isds_store_credentials(context, context->saved_username,
5351 password, NULL))) {
5352 _isds_discard_credentials(context, 0);
5353 goto leave;
5355 #if HAVE_CURL_REAUTHORIZATION_BUG
5356 saved_curl = context->curl;
5357 context->curl = curl_easy_init();
5358 if (NULL == context->curl) {
5359 err = IE_ERROR;
5360 goto leave;
5362 if (context->timeout) {
5363 err = isds_set_timeout(context, context->timeout);
5364 if (err) goto leave;
5366 #endif
5368 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5370 /* Sent request */
5371 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5373 /* Remove temporal credentials */
5374 _isds_discard_credentials(context, 0);
5375 /* Detach pointer to OTP credentials from context */
5376 context->otp_credentials = NULL;
5377 /* Keep context->otp true to keep signaling this is OTP session */
5379 /* Destroy request */
5380 xmlFreeNode(request); request = NULL;
5382 if (err) {
5383 isds_log(ILF_ISDS, ILL_DEBUG,
5384 _("Processing ISDS response on SendSMSCode request failed\n"));
5385 goto leave;
5388 /* Check for response status */
5389 err = isds_response_status(context, SERVICE_ASWS, response,
5390 &code, &message, (xmlChar **)refnumber);
5391 if (err) {
5392 isds_log(ILF_ISDS, ILL_DEBUG,
5393 _("ISDS response on SendSMSCode request is missing "
5394 "status\n"));
5395 goto leave;
5398 /* Check for error */
5399 if (xmlStrcmp(code, BAD_CAST "0000")) {
5400 char *code_locale = _isds_utf82locale((char*)code);
5401 char *message_locale = _isds_utf82locale((char*)message);
5402 size_t i;
5403 isds_log(ILF_ISDS, ILL_DEBUG,
5404 _("Server refused to send new code on SendSMSCode "
5405 "request (code=%s, message=%s)\n"),
5406 code_locale, message_locale);
5408 /* Check for known error codes */
5409 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5410 if (!xmlStrcmp(code, codes[i])) break;
5412 if (i < sizeof(codes)/sizeof(*codes)) {
5413 isds_log_message(context, _(meanings[i]));
5414 /* Mimic otp->resolution according to the code, specification does
5415 * prescribe OTP header to be available. */
5416 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5417 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5418 otp->resolution = resolutions[i];
5419 } else
5420 isds_log_message(context, message_locale);
5422 free(code_locale);
5423 free(message_locale);
5425 err = IE_ISDS;
5426 goto leave;
5429 /* Otherwise new code sent successfully */
5430 /* Mimic otp->resolution according to the code, specification does
5431 * prescribe OTP header to be available. */
5432 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5433 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5435 leave:
5436 if (NULL != saved_url) {
5437 /* Revert URL to original one */
5438 zfree(context->url);
5439 context->url = saved_url;
5441 #if HAVE_CURL_REAUTHORIZATION_BUG
5442 if (NULL != saved_curl) {
5443 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5444 context->curl = saved_curl;
5446 #endif
5448 free(code);
5449 free(message);
5450 xmlFreeDoc(response);
5451 xmlFreeNode(request);
5453 if (!err)
5454 isds_log(ILF_ISDS, ILL_DEBUG,
5455 _("New OTP code has been sent successfully on SendSMSCode "
5456 "request.\n"));
5457 return err;
5461 /* Convert response status code to isds_error code and set long message
5462 * @context is context to save long message to
5463 * @map is mapping from codes to errors and messages. Pass NULL for generic
5464 * handling.
5465 * @code is status code to translate
5466 * @message is non-localized status message to put into long message in case
5467 * of uknown error. It can be NULL if server did not provide any.
5468 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5469 * invalid invocation. */
5470 static isds_error statuscode2isds_error(struct isds_ctx *context,
5471 const struct code_map_isds_error *map,
5472 const xmlChar *code, const xmlChar *message) {
5473 if (NULL == code) {
5474 isds_log_message(context,
5475 _("NULL status code passed to statuscode2isds_error()"));
5476 return IE_INVAL;
5479 if (NULL != map) {
5480 /* Check for known error codes */
5481 for (int i=0; map->codes[i] != NULL; i++) {
5482 if (!xmlStrcmp(code, map->codes[i])) {
5483 isds_log_message(context, _(map->meanings[i]));
5484 return map->errors[i];
5489 /* Other error */
5490 if (xmlStrcmp(code, BAD_CAST "0000")) {
5491 char *message_locale = _isds_utf82locale((char*)message);
5492 if (NULL == message_locale)
5493 isds_log_message(context, _("ISDS server returned unknown error"));
5494 else
5495 isds_log_message(context, message_locale);
5496 free(message_locale);
5497 return IE_ISDS;
5500 return IE_SUCCESS;
5502 #endif
5505 /* Change user password in ISDS.
5506 * User must supply old password, new password will takes effect after some
5507 * time, current session can continue. Password must fulfill some constraints.
5508 * @context is session context
5509 * @old_password is current password.
5510 * @new_password is requested new password
5511 * @otp auxiliary data required if one-time password authentication is in use,
5512 * defines OTP code (if known) and returns fine grade resolution of OTP
5513 * procedure. Pass NULL, if one-time password authentication is not needed.
5514 * Please note the @otp argument must match OTP method used at log-in time. See
5515 * isds_login() function for more details.
5516 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5517 * NULL, if you don't care.
5518 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5519 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5520 * awaiting OTP code that has been delivered by side channel to the user. */
5521 isds_error isds_change_password(struct isds_ctx *context,
5522 const char *old_password, const char *new_password,
5523 struct isds_otp *otp, char **refnumber) {
5524 isds_error err = IE_SUCCESS;
5525 #if HAVE_LIBCURL
5526 char *saved_url = NULL; /* No copy */
5527 #if HAVE_CURL_REAUTHORIZATION_BUG
5528 CURL *saved_curl = NULL; /* No copy */
5529 #endif
5530 xmlNsPtr isds_ns = NULL;
5531 xmlNodePtr request = NULL, node;
5532 xmlDocPtr response = NULL;
5533 xmlChar *code = NULL, *message = NULL;
5534 const xmlChar *codes[] = {
5535 BAD_CAST "1066",
5536 BAD_CAST "1067",
5537 BAD_CAST "1079",
5538 BAD_CAST "1080",
5539 BAD_CAST "1081",
5540 BAD_CAST "1082",
5541 BAD_CAST "1083",
5542 BAD_CAST "1090",
5543 BAD_CAST "1091",
5544 BAD_CAST "2300",
5545 BAD_CAST "9204"
5547 const char *meanings[] = {
5548 N_("Password length must be between 8 and 32 characters"),
5549 N_("Password cannot be reused"), /* Server does not distinguish 1067
5550 and 1091 on ChangePasswordOTP */
5551 N_("Password contains forbidden character"),
5552 N_("Password must contain at least one upper-case letter, "
5553 "one lower-case, and one digit"),
5554 N_("Password cannot contain sequence of three identical characters"),
5555 N_("Password cannot contain user identifier"),
5556 N_("Password is too simmple"),
5557 N_("Old password is not valid"),
5558 N_("Password cannot be reused"),
5559 N_("Unexpected error"),
5560 N_("LDAP update error")
5562 #endif
5564 if (!context) return IE_INVALID_CONTEXT;
5565 zfree(context->long_message);
5566 if (NULL != refnumber)
5567 zfree(*refnumber);
5568 if (NULL == old_password) {
5569 isds_log_message(context,
5570 _("Second argument (old password) of isds_change_password() "
5571 "is NULL"));
5572 return IE_INVAL;
5574 if (NULL == otp && NULL == new_password) {
5575 isds_log_message(context,
5576 _("Third argument (new password) of isds_change_password() "
5577 "is NULL"));
5578 return IE_INVAL;
5581 #if HAVE_LIBCURL
5582 /* Check if connection is established
5583 * TODO: This check should be done downstairs. */
5584 if (!context->curl) return IE_CONNECTION_CLOSED;
5586 if (context->otp && NULL == otp) {
5587 isds_log_message(context, _("If one-time password authentication "
5588 "method is in use, changing password requires one-time "
5589 "credentials either"));
5590 return IE_INVAL;
5593 /* Build ChangeISDSPassword request */
5594 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5595 BAD_CAST "ChangePasswordOTP");
5596 if (!request) {
5597 isds_log_message(context, (NULL == otp) ?
5598 _("Could not build ChangeISDSPassword request") :
5599 _("Could not build ChangePasswordOTP request"));
5600 return IE_ERROR;
5602 isds_ns = xmlNewNs(request,
5603 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5604 NULL);
5605 if(!isds_ns) {
5606 isds_log_message(context, _("Could not create ISDS name space"));
5607 xmlFreeNode(request);
5608 return IE_ERROR;
5610 xmlSetNs(request, isds_ns);
5612 INSERT_STRING(request, "dbOldPassword", old_password);
5613 INSERT_STRING(request, "dbNewPassword", new_password);
5615 if (NULL != otp) {
5616 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5617 switch (otp->method) {
5618 case OTP_HMAC:
5619 isds_log(ILF_SEC, ILL_INFO,
5620 _("Selected authentication method: "
5621 "HMAC-based one-time password\n"));
5622 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5623 break;
5624 case OTP_TIME:
5625 isds_log(ILF_SEC, ILL_INFO,
5626 _("Selected authentication method: "
5627 "Time-based one-time password\n"));
5628 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5629 if (otp->otp_code == NULL) {
5630 isds_log(ILF_SEC, ILL_INFO,
5631 _("OTP code has not been provided by "
5632 "application, requesting server for "
5633 "new one.\n"));
5634 err = _isds_request_totp_code(context, old_password, otp,
5635 refnumber);
5636 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5637 goto leave;
5639 } else {
5640 isds_log(ILF_SEC, ILL_INFO,
5641 _("OTP code has been provided by "
5642 "application, not requesting server "
5643 "for new one.\n"));
5645 break;
5646 default:
5647 isds_log_message(context,
5648 _("Unknown one-time password authentication "
5649 "method requested by application"));
5650 err = IE_ENUM;
5651 goto leave;
5654 /* Change URL temporarily for sending this request only */
5656 char *new_url = NULL;
5657 if ((err = _isds_build_url_from_context(context,
5658 "%1$.*2$sasws/changePassword", &new_url))) {
5659 goto leave;
5661 saved_url = context->url;
5662 context->url = new_url;
5665 /* Store credentials for sending this request only */
5666 context->otp_credentials = otp;
5667 _isds_discard_credentials(context, 0);
5668 if ((err = _isds_store_credentials(context, context->saved_username,
5669 old_password, NULL))) {
5670 _isds_discard_credentials(context, 0);
5671 goto leave;
5673 #if HAVE_CURL_REAUTHORIZATION_BUG
5674 saved_curl = context->curl;
5675 context->curl = curl_easy_init();
5676 if (NULL == context->curl) {
5677 err = IE_ERROR;
5678 goto leave;
5680 if (context->timeout) {
5681 err = isds_set_timeout(context, context->timeout);
5682 if (err) goto leave;
5684 #endif
5687 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5688 _("Sending ChangeISDSPassword request to ISDS\n") :
5689 _("Sending ChangePasswordOTP request to ISDS\n"));
5691 /* Sent request */
5692 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5693 request, &response, NULL, NULL);
5695 if (otp) {
5696 /* Remove temporal credentials */
5697 _isds_discard_credentials(context, 0);
5698 /* Detach pointer to OTP credentials from context */
5699 context->otp_credentials = NULL;
5700 /* Keep context->otp true to keep signaling this is OTP session */
5703 /* Destroy request */
5704 xmlFreeNode(request); request = NULL;
5706 if (err) {
5707 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5708 _("Processing ISDS response on ChangeISDSPassword "
5709 "request failed\n") :
5710 _("Processing ISDS response on ChangePasswordOTP "
5711 "request failed\n"));
5712 goto leave;
5715 /* Check for response status */
5716 err = isds_response_status(context,
5717 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5718 &code, &message, (xmlChar **)refnumber);
5719 if (err) {
5720 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5721 _("ISDS response on ChangeISDSPassword request is missing "
5722 "status\n") :
5723 _("ISDS response on ChangePasswordOTP request is missing "
5724 "status\n"));
5725 goto leave;
5728 /* Check for known error codes */
5729 for (size_t i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5730 if (!xmlStrcmp(code, codes[i])) {
5731 char *code_locale = _isds_utf82locale((char*)code);
5732 char *message_locale = _isds_utf82locale((char*)message);
5733 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5734 _("Server refused to change password on ChangeISDSPassword "
5735 "request (code=%s, message=%s)\n") :
5736 _("Server refused to change password on ChangePasswordOTP "
5737 "request (code=%s, message=%s)\n"),
5738 code_locale, message_locale);
5739 free(code_locale);
5740 free(message_locale);
5741 isds_log_message(context, _(meanings[i]));
5742 err = IE_INVAL;
5743 goto leave;
5747 /* Other error */
5748 if (xmlStrcmp(code, BAD_CAST "0000")) {
5749 char *code_locale = _isds_utf82locale((char*)code);
5750 char *message_locale = _isds_utf82locale((char*)message);
5751 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5752 _("Server refused to change password on ChangeISDSPassword "
5753 "request (code=%s, message=%s)\n") :
5754 _("Server refused to change password on ChangePasswordOTP "
5755 "request (code=%s, message=%s)\n"),
5756 code_locale, message_locale);
5757 isds_log_message(context, message_locale);
5758 free(code_locale);
5759 free(message_locale);
5760 err = IE_ISDS;
5761 goto leave;
5764 /* Otherwise password changed successfully */
5766 leave:
5767 if (NULL != saved_url) {
5768 /* Revert URL to original one */
5769 zfree(context->url);
5770 context->url = saved_url;
5772 #if HAVE_CURL_REAUTHORIZATION_BUG
5773 if (NULL != saved_curl) {
5774 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5775 context->curl = saved_curl;
5777 #endif
5779 free(code);
5780 free(message);
5781 xmlFreeDoc(response);
5782 xmlFreeNode(request);
5784 if (!err)
5785 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5786 _("Password changed successfully on ChangeISDSPassword "
5787 "request.\n") :
5788 _("Password changed successfully on ChangePasswordOTP "
5789 "request.\n"));
5790 #else /* not HAVE_LIBCURL */
5791 err = IE_NOTSUP;
5792 #endif
5794 return err;
5798 #if HAVE_LIBCURL
5799 /* Generic middle part with request sending and response check.
5800 * It sends prepared request and checks for error code.
5801 * @context is ISDS session context.
5802 * @service is ISDS service handler
5803 * @service_name is name in scope of given @service
5804 * @request is XML tree with request. Will be freed to save memory.
5805 * @response is XML document outputting ISDS response.
5806 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5807 * @map is mapping from status code to library error. Pass NULL if no special
5808 * handling is requested.
5809 * NULL, if you don't care. */
5810 static isds_error send_destroy_request_check_response(
5811 struct isds_ctx *context,
5812 const isds_service service, const xmlChar *service_name,
5813 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5814 const struct code_map_isds_error *map) {
5815 isds_error err = IE_SUCCESS;
5816 char *service_name_locale = NULL;
5817 xmlChar *code = NULL, *message = NULL;
5820 if (!context) return IE_INVALID_CONTEXT;
5821 if (!service_name || *service_name == '\0' || !request || !*request ||
5822 !response)
5823 return IE_INVAL;
5825 /* Check if connection is established
5826 * TODO: This check should be done downstairs. */
5827 if (!context->curl) return IE_CONNECTION_CLOSED;
5829 service_name_locale = _isds_utf82locale((char*) service_name);
5830 if (!service_name_locale) {
5831 err = IE_NOMEM;
5832 goto leave;
5835 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5836 service_name_locale);
5838 /* Send request */
5839 err = _isds(context, service, *request, response, NULL, NULL);
5840 xmlFreeNode(*request); *request = NULL;
5842 if (err) {
5843 isds_log(ILF_ISDS, ILL_DEBUG,
5844 _("Processing ISDS response on %s request failed\n"),
5845 service_name_locale);
5846 goto leave;
5849 /* Check for response status */
5850 err = isds_response_status(context, service, *response,
5851 &code, &message, refnumber);
5852 if (err) {
5853 isds_log(ILF_ISDS, ILL_DEBUG,
5854 _("ISDS response on %s request is missing status\n"),
5855 service_name_locale);
5856 goto leave;
5859 err = statuscode2isds_error(context, map, code, message);
5861 /* Request processed, but server failed */
5862 if (xmlStrcmp(code, BAD_CAST "0000")) {
5863 char *code_locale = _isds_utf82locale((char*) code);
5864 char *message_locale = _isds_utf82locale((char*) message);
5865 isds_log(ILF_ISDS, ILL_DEBUG,
5866 _("Server refused %s request (code=%s, message=%s)\n"),
5867 service_name_locale, code_locale, message_locale);
5868 free(code_locale);
5869 free(message_locale);
5870 goto leave;
5874 leave:
5875 free(code);
5876 free(message);
5877 if (err && *response) {
5878 xmlFreeDoc(*response);
5879 *response = NULL;
5881 if (*request) {
5882 xmlFreeNode(*request);
5883 *request = NULL;
5885 free(service_name_locale);
5887 return err;
5891 /* Generic bottom half with request sending.
5892 * It sends prepared request, checks for error code, destroys response and
5893 * request and log success or failure.
5894 * @context is ISDS session context.
5895 * @service is ISDS service handler
5896 * @service_name is name in scope of given @service
5897 * @request is XML tree with request. Will be freed to save memory.
5898 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5899 * NULL, if you don't care. */
5900 static isds_error send_request_check_drop_response(
5901 struct isds_ctx *context,
5902 const isds_service service, const xmlChar *service_name,
5903 xmlNodePtr *request, xmlChar **refnumber) {
5904 isds_error err = IE_SUCCESS;
5905 xmlDocPtr response = NULL;
5908 if (!context) return IE_INVALID_CONTEXT;
5909 if (!service_name || *service_name == '\0' || !request || !*request)
5910 return IE_INVAL;
5912 /* Send request and check response*/
5913 err = send_destroy_request_check_response(context,
5914 service, service_name, request, &response, refnumber, NULL);
5916 xmlFreeDoc(response);
5918 if (*request) {
5919 xmlFreeNode(*request);
5920 *request = NULL;
5923 if (!err) {
5924 char *service_name_locale = _isds_utf82locale((char *) service_name);
5925 isds_log(ILF_ISDS, ILL_DEBUG,
5926 _("%s request processed by server successfully.\n"),
5927 service_name_locale);
5928 free(service_name_locale);
5931 return err;
5935 /* Insert isds_credentials_delivery structure into XML request if not NULL
5936 * @context is session context
5937 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5938 * credentials delivery. The email field is passed.
5939 * @parent is XML element where to insert */
5940 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5941 const struct isds_credentials_delivery *credentials_delivery,
5942 xmlNodePtr parent) {
5943 isds_error err = IE_SUCCESS;
5944 xmlNodePtr node;
5946 if (!context) return IE_INVALID_CONTEXT;
5947 if (!parent) return IE_INVAL;
5949 if (credentials_delivery) {
5950 /* Following elements are valid only for services:
5951 * NewAccessData, AddDataBoxUser, CreateDataBox */
5952 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5953 INSERT_STRING(parent, "email", credentials_delivery->email);
5956 leave:
5957 return err;
5961 /* Extract credentials delivery from ISDS response.
5962 * @context is session context
5963 * @credentials_delivery is pointer to valid structure to fill in returned
5964 * user's password (and new log-in name). If NULL, do not extract the data.
5965 * @response is pointer to XML document with ISDS response
5966 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5967 * @return IE_SUCCESS even if new user name has not been found because it's not
5968 * clear whether it's returned always. */
5969 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5970 struct isds_credentials_delivery *credentials_delivery,
5971 xmlDocPtr response, const char *request_name) {
5972 isds_error err = IE_SUCCESS;
5973 xmlXPathContextPtr xpath_ctx = NULL;
5974 xmlXPathObjectPtr result = NULL;
5975 char *xpath_query = NULL;
5977 if (!context) return IE_INVALID_CONTEXT;
5978 if (credentials_delivery) {
5979 zfree(credentials_delivery->token);
5980 zfree(credentials_delivery->new_user_name);
5982 if (!response || !request_name || !*request_name) return IE_INVAL;
5985 /* Extract optional token */
5986 if (credentials_delivery) {
5987 xpath_ctx = xmlXPathNewContext(response);
5988 if (!xpath_ctx) {
5989 err = IE_ERROR;
5990 goto leave;
5992 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5993 err = IE_ERROR;
5994 goto leave;
5997 /* Verify root element */
5998 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
5999 request_name)) {
6000 err = IE_NOMEM;
6001 goto leave;
6003 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
6004 if (!result) {
6005 err = IE_ERROR;
6006 goto leave;
6008 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6009 char *request_name_locale = _isds_utf82locale(request_name);
6010 isds_log(ILF_ISDS, ILL_WARNING,
6011 _("Wrong element in ISDS response for %s request "
6012 "while extracting credentials delivery details\n"),
6013 request_name_locale);
6014 free(request_name_locale);
6015 err = IE_ERROR;
6016 goto leave;
6018 xpath_ctx->node = result->nodesetval->nodeTab[0];
6021 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6022 * optional. */
6023 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
6025 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
6026 if (!credentials_delivery->token) {
6027 char *request_name_locale = _isds_utf82locale(request_name);
6028 isds_log(ILF_ISDS, ILL_ERR,
6029 _("ISDS did not return token on %s request "
6030 "even if requested\n"), request_name_locale);
6031 free(request_name_locale);
6032 err = IE_ERROR;
6036 leave:
6037 free(xpath_query);
6038 xmlXPathFreeObject(result);
6039 xmlXPathFreeContext(xpath_ctx);
6041 return err;
6045 /* Build XSD:tCreateDBInput request type for box creating.
6046 * @context is session context
6047 * @request outputs built XML tree
6048 * @service_name is request name of SERVICE_DB_MANIPULATION service
6049 * @box is box description to create including single primary user (in case of
6050 * FO box type)
6051 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6052 * box, or contact address of PFO box owner)
6053 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6054 * @upper_box_id is optional ID of supper box if currently created box is
6055 * subordinated.
6056 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6057 * don't care.
6058 * @credentials_delivery is valid pointer if ISDS should return token that box
6059 * owner can use to obtain his new credentials in on-line way. Then valid email
6060 * member value should be supplied.
6061 * @approval is optional external approval of box manipulation */
6062 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
6063 xmlNodePtr *request, const xmlChar *service_name,
6064 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6065 const xmlChar *former_names, const xmlChar *upper_box_id,
6066 const xmlChar *ceo_label,
6067 const struct isds_credentials_delivery *credentials_delivery,
6068 const struct isds_approval *approval) {
6069 isds_error err = IE_SUCCESS;
6070 xmlNsPtr isds_ns = NULL;
6071 xmlNodePtr node, dbPrimaryUsers;
6072 xmlChar *string = NULL;
6073 const struct isds_list *item;
6076 if (!context) return IE_INVALID_CONTEXT;
6077 if (!request || !service_name || service_name[0] == '\0' || !box)
6078 return IE_INVAL;
6081 /* Build CreateDataBox-similar request */
6082 *request = xmlNewNode(NULL, service_name);
6083 if (!*request) {
6084 char *service_name_locale = _isds_utf82locale((char*) service_name);
6085 isds_printf_message(context, _("Could build %s request"),
6086 service_name_locale);
6087 free(service_name_locale);
6088 return IE_ERROR;
6090 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
6091 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
6092 if (!isds_ns) {
6093 isds_log_message(context, _("Could not create ISDS1 name space"));
6094 xmlFreeNode(*request);
6095 return IE_ERROR;
6097 } else {
6098 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6099 if (!isds_ns) {
6100 isds_log_message(context, _("Could not create ISDS name space"));
6101 xmlFreeNode(*request);
6102 return IE_ERROR;
6105 xmlSetNs(*request, isds_ns);
6107 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6108 err = insert_DbOwnerInfo(context, box, node);
6109 if (err) goto leave;
6111 /* Insert users */
6112 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6113 * verbose documentation allows none dbUserInfo */
6114 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6115 for (item = users; item; item = item->next) {
6116 if (item->data) {
6117 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6118 err = insert_DbUserInfo(context,
6119 (struct isds_DbUserInfo *) item->data, node);
6120 if (err) goto leave;
6124 INSERT_STRING(*request, "dbFormerNames", former_names);
6125 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6126 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6128 err = insert_credentials_delivery(context, credentials_delivery, *request);
6129 if (err) goto leave;
6131 err = insert_GExtApproval(context, approval, *request);
6132 if (err) goto leave;
6134 leave:
6135 if (err) {
6136 xmlFreeNode(*request);
6137 *request = NULL;
6139 free(string);
6140 return err;
6142 #endif /* HAVE_LIBCURL */
6145 /* Create new box.
6146 * @context is session context
6147 * @box is box description to create including single primary user (in case of
6148 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6149 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6150 * box, or contact address of PFO box owner)
6151 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6152 * @upper_box_id is optional ID of supper box if currently created box is
6153 * subordinated.
6154 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6155 * @credentials_delivery is NULL if new password should be delivered off-line
6156 * to box owner. It is valid pointer if owner should obtain new password on-line
6157 * on dedicated web server. Then input @credentials_delivery.email value is
6158 * his e-mail address he must provide to dedicated web server together
6159 * with output reallocated @credentials_delivery.token member. Output
6160 * member @credentials_delivery.new_user_name is unused up on this call.
6161 * @approval is optional external approval of box manipulation
6162 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6163 * NULL, if you don't care.*/
6164 isds_error isds_add_box(struct isds_ctx *context,
6165 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6166 const char *former_names, const char *upper_box_id,
6167 const char *ceo_label,
6168 struct isds_credentials_delivery *credentials_delivery,
6169 const struct isds_approval *approval, char **refnumber) {
6170 isds_error err = IE_SUCCESS;
6171 #if HAVE_LIBCURL
6172 xmlNodePtr request = NULL;
6173 xmlDocPtr response = NULL;
6174 xmlXPathContextPtr xpath_ctx = NULL;
6175 xmlXPathObjectPtr result = NULL;
6176 #endif
6179 if (!context) return IE_INVALID_CONTEXT;
6180 zfree(context->long_message);
6181 if (credentials_delivery) {
6182 zfree(credentials_delivery->token);
6183 zfree(credentials_delivery->new_user_name);
6185 if (!box) return IE_INVAL;
6187 #if HAVE_LIBCURL
6188 /* Scratch box ID */
6189 zfree(box->dbID);
6191 /* Build CreateDataBox request */
6192 err = build_CreateDBInput_request(context,
6193 &request, BAD_CAST "CreateDataBox",
6194 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6195 (xmlChar *) ceo_label, credentials_delivery, approval);
6196 if (err) goto leave;
6198 /* Send it to server and process response */
6199 err = send_destroy_request_check_response(context,
6200 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6201 &response, (xmlChar **) refnumber, NULL);
6203 /* Extract box ID */
6204 xpath_ctx = xmlXPathNewContext(response);
6205 if (!xpath_ctx) {
6206 err = IE_ERROR;
6207 goto leave;
6209 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6210 err = IE_ERROR;
6211 goto leave;
6213 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6215 /* Extract optional token */
6216 err = extract_credentials_delivery(context, credentials_delivery, response,
6217 "CreateDataBox");
6219 leave:
6220 xmlXPathFreeObject(result);
6221 xmlXPathFreeContext(xpath_ctx);
6222 xmlFreeDoc(response);
6223 xmlFreeNode(request);
6225 if (!err) {
6226 isds_log(ILF_ISDS, ILL_DEBUG,
6227 _("CreateDataBox request processed by server successfully.\n"));
6229 #else /* not HAVE_LIBCURL */
6230 err = IE_NOTSUP;
6231 #endif
6233 return err;
6237 /* Notify ISDS about new PFO entity.
6238 * This function has no real effect.
6239 * @context is session context
6240 * @box is PFO description including single primary user.
6241 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6242 * @former_names is optional undocumented string. Pass NULL if you don't care.
6243 * @upper_box_id is optional ID of supper box if currently created box is
6244 * subordinated.
6245 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6246 * @approval is optional external approval of box manipulation
6247 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6248 * NULL, if you don't care.*/
6249 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6250 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6251 const char *former_names, const char *upper_box_id,
6252 const char *ceo_label, const struct isds_approval *approval,
6253 char **refnumber) {
6254 isds_error err = IE_SUCCESS;
6255 #if HAVE_LIBCURL
6256 xmlNodePtr request = NULL;
6257 #endif
6259 if (!context) return IE_INVALID_CONTEXT;
6260 zfree(context->long_message);
6261 if (!box) return IE_INVAL;
6263 #if HAVE_LIBCURL
6264 /* Build CreateDataBoxPFOInfo request */
6265 err = build_CreateDBInput_request(context,
6266 &request, BAD_CAST "CreateDataBoxPFOInfo",
6267 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6268 (xmlChar *) ceo_label, NULL, approval);
6269 if (err) goto leave;
6271 /* Send it to server and process response */
6272 err = send_request_check_drop_response(context,
6273 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6274 (xmlChar **) refnumber);
6275 /* XXX: XML Schema names output dbID element but textual documentation
6276 * states no box identifier is returned. */
6277 leave:
6278 xmlFreeNode(request);
6279 #else /* not HAVE_LIBCURL */
6280 err = IE_NOTSUP;
6281 #endif
6282 return err;
6286 /* Common implementation for removing given box.
6287 * @context is session context
6288 * @service_name is UTF-8 encoded name fo ISDS service
6289 * @box is box description to delete
6290 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6291 * carry sane value. If NULL, do not inject this information into request.
6292 * @approval is optional external approval of box manipulation
6293 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6294 * NULL, if you don't care.*/
6295 isds_error _isds_delete_box_common(struct isds_ctx *context,
6296 const xmlChar *service_name,
6297 const struct isds_DbOwnerInfo *box, const struct tm *since,
6298 const struct isds_approval *approval, char **refnumber) {
6299 isds_error err = IE_SUCCESS;
6300 #if HAVE_LIBCURL
6301 xmlNsPtr isds_ns = NULL;
6302 xmlNodePtr request = NULL;
6303 xmlNodePtr node;
6304 xmlChar *string = NULL;
6305 #endif
6308 if (!context) return IE_INVALID_CONTEXT;
6309 zfree(context->long_message);
6310 if (!service_name || !*service_name || !box) return IE_INVAL;
6313 #if HAVE_LIBCURL
6314 /* Build DeleteDataBox(Promptly) request */
6315 request = xmlNewNode(NULL, service_name);
6316 if (!request) {
6317 char *service_name_locale = _isds_utf82locale((char*)service_name);
6318 isds_printf_message(context,
6319 _("Could build %s request"), service_name_locale);
6320 free(service_name_locale);
6321 return IE_ERROR;
6323 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6324 if(!isds_ns) {
6325 isds_log_message(context, _("Could not create ISDS name space"));
6326 xmlFreeNode(request);
6327 return IE_ERROR;
6329 xmlSetNs(request, isds_ns);
6331 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6332 err = insert_DbOwnerInfo(context, box, node);
6333 if (err) goto leave;
6335 if (since) {
6336 err = tm2datestring(since, &string);
6337 if (err) {
6338 isds_log_message(context,
6339 _("Could not convert `since' argument to ISO date string"));
6340 goto leave;
6342 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6343 zfree(string);
6346 err = insert_GExtApproval(context, approval, request);
6347 if (err) goto leave;
6350 /* Send it to server and process response */
6351 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6352 service_name, &request, (xmlChar **) refnumber);
6354 leave:
6355 xmlFreeNode(request);
6356 free(string);
6357 #else /* not HAVE_LIBCURL */
6358 err = IE_NOTSUP;
6359 #endif
6360 return err;
6364 /* Remove given box permanently.
6365 * @context is session context
6366 * @box is box description to delete
6367 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6368 * carry sane value.
6369 * @approval is optional external approval of box manipulation
6370 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6371 * NULL, if you don't care.*/
6372 isds_error isds_delete_box(struct isds_ctx *context,
6373 const struct isds_DbOwnerInfo *box, const struct tm *since,
6374 const struct isds_approval *approval, char **refnumber) {
6375 if (!context) return IE_INVALID_CONTEXT;
6376 zfree(context->long_message);
6377 if (!box || !since) return IE_INVAL;
6379 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6380 box, since, approval, refnumber);
6384 /* Undocumented function.
6385 * @context is session context
6386 * @box is box description to delete
6387 * @approval is optional external approval of box manipulation
6388 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6389 * NULL, if you don't care.*/
6390 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6391 const struct isds_DbOwnerInfo *box,
6392 const struct isds_approval *approval, char **refnumber) {
6393 if (!context) return IE_INVALID_CONTEXT;
6394 zfree(context->long_message);
6395 if (!box) return IE_INVAL;
6397 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6398 box, NULL, approval, refnumber);
6402 /* Update data about given box.
6403 * @context is session context
6404 * @old_box current box description
6405 * @new_box are updated data about @old_box
6406 * @approval is optional external approval of box manipulation
6407 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6408 * NULL, if you don't care.*/
6409 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6410 const struct isds_DbOwnerInfo *old_box,
6411 const struct isds_DbOwnerInfo *new_box,
6412 const struct isds_approval *approval, char **refnumber) {
6413 isds_error err = IE_SUCCESS;
6414 #if HAVE_LIBCURL
6415 xmlNsPtr isds_ns = NULL;
6416 xmlNodePtr request = NULL;
6417 xmlNodePtr node;
6418 #endif
6421 if (!context) return IE_INVALID_CONTEXT;
6422 zfree(context->long_message);
6423 if (!old_box || !new_box) return IE_INVAL;
6426 #if HAVE_LIBCURL
6427 /* Build UpdateDataBoxDescr request */
6428 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6429 if (!request) {
6430 isds_log_message(context,
6431 _("Could build UpdateDataBoxDescr request"));
6432 return IE_ERROR;
6434 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6435 if(!isds_ns) {
6436 isds_log_message(context, _("Could not create ISDS name space"));
6437 xmlFreeNode(request);
6438 return IE_ERROR;
6440 xmlSetNs(request, isds_ns);
6442 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6443 err = insert_DbOwnerInfo(context, old_box, node);
6444 if (err) goto leave;
6446 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6447 err = insert_DbOwnerInfo(context, new_box, node);
6448 if (err) goto leave;
6450 err = insert_GExtApproval(context, approval, request);
6451 if (err) goto leave;
6454 /* Send it to server and process response */
6455 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6456 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6458 leave:
6459 xmlFreeNode(request);
6460 #else /* not HAVE_LIBCURL */
6461 err = IE_NOTSUP;
6462 #endif
6464 return err;
6468 #if HAVE_LIBCURL
6469 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6470 * code
6471 * @context is session context
6472 * @service is SOAP service
6473 * @service_name is name of request in @service
6474 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6475 * @box_id is box ID of interest
6476 * @approval is optional external approval of box manipulation
6477 * @response is server SOAP body response as XML document
6478 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6479 * NULL, if you don't care.
6480 * @return error coded from lower layer, context message will be set up
6481 * appropriately. */
6482 static isds_error build_send_dbid_request_check_response(
6483 struct isds_ctx *context, const isds_service service,
6484 const xmlChar *service_name, const xmlChar *box_id_element,
6485 const xmlChar *box_id, const struct isds_approval *approval,
6486 xmlDocPtr *response, xmlChar **refnumber) {
6488 isds_error err = IE_SUCCESS;
6489 char *service_name_locale = NULL, *box_id_locale = NULL;
6490 xmlNodePtr request = NULL, node;
6491 xmlNsPtr isds_ns = NULL;
6493 if (!context) return IE_INVALID_CONTEXT;
6494 if (!service_name || !box_id) return IE_INVAL;
6495 if (!response) return IE_INVAL;
6497 /* Free output argument */
6498 xmlFreeDoc(*response); *response = NULL;
6500 /* Prepare strings */
6501 service_name_locale = _isds_utf82locale((char*)service_name);
6502 if (!service_name_locale) {
6503 err = IE_NOMEM;
6504 goto leave;
6506 box_id_locale = _isds_utf82locale((char*)box_id);
6507 if (!box_id_locale) {
6508 err = IE_NOMEM;
6509 goto leave;
6512 /* Build request */
6513 request = xmlNewNode(NULL, service_name);
6514 if (!request) {
6515 isds_printf_message(context,
6516 _("Could not build %s request for %s box"), service_name_locale,
6517 box_id_locale);
6518 err = IE_ERROR;
6519 goto leave;
6521 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6522 if(!isds_ns) {
6523 isds_log_message(context, _("Could not create ISDS name space"));
6524 err = IE_ERROR;
6525 goto leave;
6527 xmlSetNs(request, isds_ns);
6529 /* Add XSD:tIdDbInput children */
6530 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6531 INSERT_STRING(request, box_id_element, box_id);
6532 err = insert_GExtApproval(context, approval, request);
6533 if (err) goto leave;
6535 /* Send request and check response*/
6536 err = send_destroy_request_check_response(context,
6537 service, service_name, &request, response, refnumber, NULL);
6539 leave:
6540 free(service_name_locale);
6541 free(box_id_locale);
6542 xmlFreeNode(request);
6543 return err;
6545 #endif /* HAVE_LIBCURL */
6548 /* Get data about all users assigned to given box.
6549 * @context is session context
6550 * @box_id is box ID
6551 * @users is automatically reallocated list of struct isds_DbUserInfo */
6552 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6553 struct isds_list **users) {
6554 isds_error err = IE_SUCCESS;
6555 #if HAVE_LIBCURL
6556 xmlDocPtr response = NULL;
6557 xmlXPathContextPtr xpath_ctx = NULL;
6558 xmlXPathObjectPtr result = NULL;
6559 int i;
6560 struct isds_list *item, *prev_item = NULL;
6561 #endif
6563 if (!context) return IE_INVALID_CONTEXT;
6564 zfree(context->long_message);
6565 if (!users || !box_id) return IE_INVAL;
6566 isds_list_free(users);
6569 #if HAVE_LIBCURL
6570 /* Do request and check for success */
6571 err = build_send_dbid_request_check_response(context,
6572 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6573 BAD_CAST box_id, NULL, &response, NULL);
6574 if (err) goto leave;
6577 /* Extract data */
6578 /* Prepare structure */
6579 xpath_ctx = xmlXPathNewContext(response);
6580 if (!xpath_ctx) {
6581 err = IE_ERROR;
6582 goto leave;
6584 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6585 err = IE_ERROR;
6586 goto leave;
6589 /* Set context node */
6590 result = xmlXPathEvalExpression(BAD_CAST
6591 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6592 xpath_ctx);
6593 if (!result) {
6594 err = IE_ERROR;
6595 goto leave;
6597 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6598 /* Iterate over all users */
6599 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6601 /* Prepare structure */
6602 item = calloc(1, sizeof(*item));
6603 if (!item) {
6604 err = IE_NOMEM;
6605 goto leave;
6607 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6608 if (i == 0) *users = item;
6609 else prev_item->next = item;
6610 prev_item = item;
6612 /* Extract it */
6613 xpath_ctx->node = result->nodesetval->nodeTab[i];
6614 err = extract_DbUserInfo(context,
6615 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6616 if (err) goto leave;
6620 leave:
6621 if (err) {
6622 isds_list_free(users);
6625 xmlXPathFreeObject(result);
6626 xmlXPathFreeContext(xpath_ctx);
6627 xmlFreeDoc(response);
6629 if (!err)
6630 isds_log(ILF_ISDS, ILL_DEBUG,
6631 _("GetDataBoxUsers request processed by server "
6632 "successfully.\n"));
6633 #else /* not HAVE_LIBCURL */
6634 err = IE_NOTSUP;
6635 #endif
6637 return err;
6641 /* Update data about user assigned to given box.
6642 * @context is session context
6643 * @box is box identification
6644 * @old_user identifies user to update
6645 * @new_user are updated data about @old_user
6646 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6647 * NULL, if you don't care.*/
6648 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6649 const struct isds_DbOwnerInfo *box,
6650 const struct isds_DbUserInfo *old_user,
6651 const struct isds_DbUserInfo *new_user,
6652 char **refnumber) {
6653 isds_error err = IE_SUCCESS;
6654 #if HAVE_LIBCURL
6655 xmlNsPtr isds_ns = NULL;
6656 xmlNodePtr request = NULL;
6657 xmlNodePtr node;
6658 #endif
6661 if (!context) return IE_INVALID_CONTEXT;
6662 zfree(context->long_message);
6663 if (!box || !old_user || !new_user) return IE_INVAL;
6666 #if HAVE_LIBCURL
6667 /* Build UpdateDataBoxUser request */
6668 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6669 if (!request) {
6670 isds_log_message(context,
6671 _("Could build UpdateDataBoxUser request"));
6672 return IE_ERROR;
6674 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6675 if(!isds_ns) {
6676 isds_log_message(context, _("Could not create ISDS name space"));
6677 xmlFreeNode(request);
6678 return IE_ERROR;
6680 xmlSetNs(request, isds_ns);
6682 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6683 err = insert_DbOwnerInfo(context, box, node);
6684 if (err) goto leave;
6686 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6687 err = insert_DbUserInfo(context, old_user, node);
6688 if (err) goto leave;
6690 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6691 err = insert_DbUserInfo(context, new_user, node);
6692 if (err) goto leave;
6694 /* Send it to server and process response */
6695 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6696 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6698 leave:
6699 xmlFreeNode(request);
6700 #else /* not HAVE_LIBCURL */
6701 err = IE_NOTSUP;
6702 #endif
6704 return err;
6708 /* Undocumented function.
6709 * @context is session context
6710 * @box_id is UTF-8 encoded box identifier
6711 * @token is UTF-8 encoded temporary password
6712 * @user_id outputs UTF-8 encoded reallocated user identifier
6713 * @password outpus UTF-8 encoded reallocated user password
6714 * Output arguments will be nulled in case of error */
6715 isds_error isds_activate(struct isds_ctx *context,
6716 const char *box_id, const char *token,
6717 char **user_id, char **password) {
6718 isds_error err = IE_SUCCESS;
6719 #if HAVE_LIBCURL
6720 xmlNsPtr isds_ns = NULL;
6721 xmlNodePtr request = NULL, node;
6722 xmlDocPtr response = NULL;
6723 xmlXPathContextPtr xpath_ctx = NULL;
6724 xmlXPathObjectPtr result = NULL;
6725 #endif
6728 if (!context) return IE_INVALID_CONTEXT;
6729 zfree(context->long_message);
6731 if (user_id) zfree(*user_id);
6732 if (password) zfree(*password);
6734 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6737 #if HAVE_LIBCURL
6738 /* Build Activate request */
6739 request = xmlNewNode(NULL, BAD_CAST "Activate");
6740 if (!request) {
6741 isds_log_message(context, _("Could build Activate request"));
6742 return IE_ERROR;
6744 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6745 if(!isds_ns) {
6746 isds_log_message(context, _("Could not create ISDS name space"));
6747 xmlFreeNode(request);
6748 return IE_ERROR;
6750 xmlSetNs(request, isds_ns);
6752 INSERT_STRING(request, "dbAccessDataId", token);
6753 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6754 INSERT_STRING(request, "dbID", box_id);
6757 /* Send request and check response*/
6758 err = send_destroy_request_check_response(context,
6759 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6760 &response, NULL, NULL);
6761 if (err) goto leave;
6764 /* Extract data */
6765 xpath_ctx = xmlXPathNewContext(response);
6766 if (!xpath_ctx) {
6767 err = IE_ERROR;
6768 goto leave;
6770 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6771 err = IE_ERROR;
6772 goto leave;
6774 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6775 xpath_ctx);
6776 if (!result) {
6777 err = IE_ERROR;
6778 goto leave;
6780 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6781 isds_log_message(context, _("Missing ActivateResponse element"));
6782 err = IE_ISDS;
6783 goto leave;
6785 if (result->nodesetval->nodeNr > 1) {
6786 isds_log_message(context, _("Multiple ActivateResponse element"));
6787 err = IE_ISDS;
6788 goto leave;
6790 xpath_ctx->node = result->nodesetval->nodeTab[0];
6791 xmlXPathFreeObject(result); result = NULL;
6793 EXTRACT_STRING("isds:userId", *user_id);
6794 if (!*user_id)
6795 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6796 "but did not return `userId' element.\n"));
6798 EXTRACT_STRING("isds:password", *password);
6799 if (!*password)
6800 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6801 "but did not return `password' element.\n"));
6803 leave:
6804 xmlXPathFreeObject(result);
6805 xmlXPathFreeContext(xpath_ctx);
6806 xmlFreeDoc(response);
6807 xmlFreeNode(request);
6809 if (!err)
6810 isds_log(ILF_ISDS, ILL_DEBUG,
6811 _("Activate request processed by server successfully.\n"));
6812 #else /* not HAVE_LIBCURL */
6813 err = IE_NOTSUP;
6814 #endif
6816 return err;
6820 /* Reset credentials of user assigned to given box.
6821 * @context is session context
6822 * @box is box identification
6823 * @user identifies user to reset password
6824 * @fee_paid is true if fee has been paid, false otherwise
6825 * @approval is optional external approval of box manipulation
6826 * @credentials_delivery is NULL if new password should be delivered off-line
6827 * to the user. It is valid pointer if user should obtain new password on-line
6828 * on dedicated web server. Then input @credentials_delivery.email value is
6829 * user's e-mail address user must provide to dedicated web server together
6830 * with @credentials_delivery.token. The output reallocated token user needs
6831 * to use to authorize on the web server to view his new password. Output
6832 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6833 * ISDS changed up on this call. (No reason why server could change the name
6834 * is known now.)
6835 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6836 * NULL, if you don't care.*/
6837 isds_error isds_reset_password(struct isds_ctx *context,
6838 const struct isds_DbOwnerInfo *box,
6839 const struct isds_DbUserInfo *user,
6840 const _Bool fee_paid, const struct isds_approval *approval,
6841 struct isds_credentials_delivery *credentials_delivery,
6842 char **refnumber) {
6843 isds_error err = IE_SUCCESS;
6844 #if HAVE_LIBCURL
6845 xmlNsPtr isds_ns = NULL;
6846 xmlNodePtr request = NULL, node;
6847 xmlDocPtr response = NULL;
6848 #endif
6851 if (!context) return IE_INVALID_CONTEXT;
6852 zfree(context->long_message);
6854 if (credentials_delivery) {
6855 zfree(credentials_delivery->token);
6856 zfree(credentials_delivery->new_user_name);
6858 if (!box || !user) return IE_INVAL;
6861 #if HAVE_LIBCURL
6862 /* Build NewAccessData request */
6863 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6864 if (!request) {
6865 isds_log_message(context,
6866 _("Could build NewAccessData request"));
6867 return IE_ERROR;
6869 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6870 if(!isds_ns) {
6871 isds_log_message(context, _("Could not create ISDS name space"));
6872 xmlFreeNode(request);
6873 return IE_ERROR;
6875 xmlSetNs(request, isds_ns);
6877 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6878 err = insert_DbOwnerInfo(context, box, node);
6879 if (err) goto leave;
6881 INSERT_ELEMENT(node, request, "dbUserInfo");
6882 err = insert_DbUserInfo(context, user, node);
6883 if (err) goto leave;
6885 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6887 err = insert_credentials_delivery(context, credentials_delivery, request);
6888 if (err) goto leave;
6890 err = insert_GExtApproval(context, approval, request);
6891 if (err) goto leave;
6893 /* Send request and check response*/
6894 err = send_destroy_request_check_response(context,
6895 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6896 &response, (xmlChar **) refnumber, NULL);
6897 if (err) goto leave;
6900 /* Extract optional token */
6901 err = extract_credentials_delivery(context, credentials_delivery,
6902 response, "NewAccessData");
6904 leave:
6905 xmlFreeDoc(response);
6906 xmlFreeNode(request);
6908 if (!err)
6909 isds_log(ILF_ISDS, ILL_DEBUG,
6910 _("NewAccessData request processed by server "
6911 "successfully.\n"));
6912 #else /* not HAVE_LIBCURL */
6913 err = IE_NOTSUP;
6914 #endif
6916 return err;
6920 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6921 * code, destroy response and log success.
6922 * @context is ISDS session context.
6923 * @service_name is name of SERVICE_DB_MANIPULATION service
6924 * @box is box identification
6925 * @user identifies user to remove
6926 * @credentials_delivery is NULL if new user's password should be delivered
6927 * off-line to the user. It is valid pointer if user should obtain new
6928 * password on-line on dedicated web server. Then input
6929 * @credentials_delivery.email value is user's e-mail address user must
6930 * provide to dedicated web server together with @credentials_delivery.token.
6931 * The output reallocated token user needs to use to authorize on the web
6932 * server to view his new password. Output reallocated
6933 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6934 * assingned or changed up on this call.
6935 * @approval is optional external approval of box manipulation
6936 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6937 * NULL, if you don't care. */
6938 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6939 struct isds_ctx *context, const xmlChar *service_name,
6940 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6941 struct isds_credentials_delivery *credentials_delivery,
6942 const struct isds_approval *approval, xmlChar **refnumber) {
6943 isds_error err = IE_SUCCESS;
6944 #if HAVE_LIBCURL
6945 xmlNsPtr isds_ns = NULL;
6946 xmlNodePtr request = NULL, node;
6947 xmlDocPtr response = NULL;
6948 #endif
6951 if (!context) return IE_INVALID_CONTEXT;
6952 zfree(context->long_message);
6953 if (credentials_delivery) {
6954 zfree(credentials_delivery->token);
6955 zfree(credentials_delivery->new_user_name);
6957 if (!service_name || service_name[0] == '\0' || !box || !user)
6958 return IE_INVAL;
6961 #if HAVE_LIBCURL
6962 /* Build NewAccessData or similar request */
6963 request = xmlNewNode(NULL, service_name);
6964 if (!request) {
6965 char *service_name_locale = _isds_utf82locale((char *) service_name);
6966 isds_printf_message(context, _("Could not build %s request"),
6967 service_name_locale);
6968 free(service_name_locale);
6969 return IE_ERROR;
6971 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6972 if(!isds_ns) {
6973 isds_log_message(context, _("Could not create ISDS name space"));
6974 xmlFreeNode(request);
6975 return IE_ERROR;
6977 xmlSetNs(request, isds_ns);
6979 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6980 err = insert_DbOwnerInfo(context, box, node);
6981 if (err) goto leave;
6983 INSERT_ELEMENT(node, request, "dbUserInfo");
6984 err = insert_DbUserInfo(context, user, node);
6985 if (err) goto leave;
6987 err = insert_credentials_delivery(context, credentials_delivery, request);
6988 if (err) goto leave;
6990 err = insert_GExtApproval(context, approval, request);
6991 if (err) goto leave;
6994 /* Send request and check response*/
6995 err = send_destroy_request_check_response(context,
6996 SERVICE_DB_MANIPULATION, service_name, &request, &response,
6997 refnumber, NULL);
6999 xmlFreeNode(request);
7000 request = NULL;
7002 /* Pick up credentials_delivery if requested */
7003 err = extract_credentials_delivery(context, credentials_delivery, response,
7004 (char *)service_name);
7006 leave:
7007 xmlFreeDoc(response);
7008 if (request) xmlFreeNode(request);
7010 if (!err) {
7011 char *service_name_locale = _isds_utf82locale((char *) service_name);
7012 isds_log(ILF_ISDS, ILL_DEBUG,
7013 _("%s request processed by server successfully.\n"),
7014 service_name_locale);
7015 free(service_name_locale);
7017 #else /* not HAVE_LIBCURL */
7018 err = IE_NOTSUP;
7019 #endif
7021 return err;
7025 /* Assign new user to given box.
7026 * @context is session context
7027 * @box is box identification
7028 * @user defines new user to add
7029 * @credentials_delivery is NULL if new user's password should be delivered
7030 * off-line to the user. It is valid pointer if user should obtain new
7031 * password on-line on dedicated web server. Then input
7032 * @credentials_delivery.email value is user's e-mail address user must
7033 * provide to dedicated web server together with @credentials_delivery.token.
7034 * The output reallocated token user needs to use to authorize on the web
7035 * server to view his new password. Output reallocated
7036 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7037 * assingned up on this call.
7038 * @approval is optional external approval of box manipulation
7039 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7040 * NULL, if you don't care.*/
7041 isds_error isds_add_user(struct isds_ctx *context,
7042 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7043 struct isds_credentials_delivery *credentials_delivery,
7044 const struct isds_approval *approval, char **refnumber) {
7045 return build_send_manipulationboxuser_request_check_drop_response(context,
7046 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
7047 approval, (xmlChar **) refnumber);
7051 /* Remove user assigned to given box.
7052 * @context is session context
7053 * @box is box identification
7054 * @user identifies user to remove
7055 * @approval is optional external approval of box manipulation
7056 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7057 * NULL, if you don't care.*/
7058 isds_error isds_delete_user(struct isds_ctx *context,
7059 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7060 const struct isds_approval *approval, char **refnumber) {
7061 return build_send_manipulationboxuser_request_check_drop_response(context,
7062 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
7063 (xmlChar **) refnumber);
7067 /* Get list of boxes in ZIP archive.
7068 * @context is session context
7069 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7070 * System recognizes following values currently: ALL (all boxes), UPG
7071 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7072 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7073 * commercial messages). This argument is a string because specification
7074 * states new values can appear in the future. Not all list types are
7075 * available to all users.
7076 * @buffer is automatically reallocated memory to store the list of boxes. The
7077 * list is zipped CSV file.
7078 * @buffer_length is size of @buffer data in bytes.
7079 * In case of error @buffer will be freed and @buffer_length will be
7080 * undefined.*/
7081 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7082 const char *list_identifier, void **buffer, size_t *buffer_length) {
7083 isds_error err = IE_SUCCESS;
7084 #if HAVE_LIBCURL
7085 xmlNsPtr isds_ns = NULL;
7086 xmlNodePtr request = NULL, node;
7087 xmlDocPtr response = NULL;
7088 xmlXPathContextPtr xpath_ctx = NULL;
7089 xmlXPathObjectPtr result = NULL;
7090 char *string = NULL;
7091 #endif
7094 if (!context) return IE_INVALID_CONTEXT;
7095 zfree(context->long_message);
7096 if (buffer) zfree(*buffer);
7097 if (!buffer || !buffer_length) return IE_INVAL;
7100 #if HAVE_LIBCURL
7101 /* Check if connection is established
7102 * TODO: This check should be done downstairs. */
7103 if (!context->curl) return IE_CONNECTION_CLOSED;
7106 /* Build AuthenticateMessage request */
7107 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7108 if (!request) {
7109 isds_log_message(context,
7110 _("Could not build GetDataBoxList request"));
7111 return IE_ERROR;
7113 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7114 if(!isds_ns) {
7115 isds_log_message(context, _("Could not create ISDS name space"));
7116 xmlFreeNode(request);
7117 return IE_ERROR;
7119 xmlSetNs(request, isds_ns);
7120 INSERT_STRING(request, "dblType", list_identifier);
7122 /* Send request to server and process response */
7123 err = send_destroy_request_check_response(context,
7124 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7125 &response, NULL, NULL);
7126 if (err) goto leave;
7129 /* Extract Base-64 encoded ZIP file */
7130 xpath_ctx = xmlXPathNewContext(response);
7131 if (!xpath_ctx) {
7132 err = IE_ERROR;
7133 goto leave;
7135 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7136 err = IE_ERROR;
7137 goto leave;
7139 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7141 /* Decode non-empty archive */
7142 if (string && string[0] != '\0') {
7143 *buffer_length = _isds_b64decode(string, buffer);
7144 if (*buffer_length == (size_t) -1) {
7145 isds_printf_message(context,
7146 _("Error while Base64-decoding box list archive"));
7147 err = IE_ERROR;
7148 goto leave;
7153 leave:
7154 free(string);
7155 xmlXPathFreeObject(result);
7156 xmlXPathFreeContext(xpath_ctx);
7157 xmlFreeDoc(response);
7158 xmlFreeNode(request);
7160 if (!err) {
7161 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7162 "processed by server successfully.\n"));
7164 #else /* not HAVE_LIBCURL */
7165 err = IE_NOTSUP;
7166 #endif
7168 return err;
7172 /* Find boxes suiting given criteria.
7173 * @criteria is filter. You should fill in at least some members.
7174 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7175 * possibly empty. Input NULL or valid old structure.
7176 * @return:
7177 * IE_SUCCESS if search succeeded, @boxes contains useful data
7178 * IE_NOEXIST if no such box exists, @boxes will be NULL
7179 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7180 * contains still valid data
7181 * other code if something bad happens. @boxes will be NULL. */
7182 isds_error isds_FindDataBox(struct isds_ctx *context,
7183 const struct isds_DbOwnerInfo *criteria,
7184 struct isds_list **boxes) {
7185 isds_error err = IE_SUCCESS;
7186 #if HAVE_LIBCURL
7187 _Bool truncated = 0;
7188 xmlNsPtr isds_ns = NULL;
7189 xmlNodePtr request = NULL;
7190 xmlDocPtr response = NULL;
7191 xmlChar *code = NULL, *message = NULL;
7192 xmlNodePtr db_owner_info;
7193 xmlXPathContextPtr xpath_ctx = NULL;
7194 xmlXPathObjectPtr result = NULL;
7195 xmlChar *string = NULL;
7196 #endif
7199 if (!context) return IE_INVALID_CONTEXT;
7200 zfree(context->long_message);
7201 if (!boxes) return IE_INVAL;
7202 isds_list_free(boxes);
7204 if (!criteria) {
7205 return IE_INVAL;
7208 #if HAVE_LIBCURL
7209 /* Check if connection is established
7210 * TODO: This check should be done downstairs. */
7211 if (!context->curl) return IE_CONNECTION_CLOSED;
7214 /* Build FindDataBox request */
7215 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
7216 if (!request) {
7217 isds_log_message(context,
7218 _("Could build FindDataBox request"));
7219 return IE_ERROR;
7221 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7222 if(!isds_ns) {
7223 isds_log_message(context, _("Could not create ISDS name space"));
7224 xmlFreeNode(request);
7225 return IE_ERROR;
7227 xmlSetNs(request, isds_ns);
7228 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7229 if (!db_owner_info) {
7230 isds_log_message(context, _("Could not add dbOwnerInfo child to "
7231 "FindDataBox element"));
7232 xmlFreeNode(request);
7233 return IE_ERROR;
7236 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
7237 if (err) goto leave;
7240 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
7242 /* Sent request */
7243 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7245 /* Destroy request */
7246 xmlFreeNode(request); request = NULL;
7248 if (err) {
7249 isds_log(ILF_ISDS, ILL_DEBUG,
7250 _("Processing ISDS response on FindDataBox "
7251 "request failed\n"));
7252 goto leave;
7255 /* Check for response status */
7256 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7257 &code, &message, NULL);
7258 if (err) {
7259 isds_log(ILF_ISDS, ILL_DEBUG,
7260 _("ISDS response on FindDataBox request is missing status\n"));
7261 goto leave;
7264 /* Request processed, but nothing found */
7265 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7266 !xmlStrcmp(code, BAD_CAST "5001")) {
7267 char *code_locale = _isds_utf82locale((char*)code);
7268 char *message_locale = _isds_utf82locale((char*)message);
7269 isds_log(ILF_ISDS, ILL_DEBUG,
7270 _("Server did not found any box on FindDataBox request "
7271 "(code=%s, message=%s)\n"), code_locale, message_locale);
7272 isds_log_message(context, message_locale);
7273 free(code_locale);
7274 free(message_locale);
7275 err = IE_NOEXIST;
7276 goto leave;
7279 /* Warning, not a error */
7280 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7281 char *code_locale = _isds_utf82locale((char*)code);
7282 char *message_locale = _isds_utf82locale((char*)message);
7283 isds_log(ILF_ISDS, ILL_DEBUG,
7284 _("Server truncated response on FindDataBox request "
7285 "(code=%s, message=%s)\n"), code_locale, message_locale);
7286 isds_log_message(context, message_locale);
7287 free(code_locale);
7288 free(message_locale);
7289 truncated = 1;
7292 /* Other error */
7293 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7294 char *code_locale = _isds_utf82locale((char*)code);
7295 char *message_locale = _isds_utf82locale((char*)message);
7296 isds_log(ILF_ISDS, ILL_DEBUG,
7297 _("Server refused FindDataBox request "
7298 "(code=%s, message=%s)\n"), code_locale, message_locale);
7299 isds_log_message(context, message_locale);
7300 free(code_locale);
7301 free(message_locale);
7302 err = IE_ISDS;
7303 goto leave;
7306 xpath_ctx = xmlXPathNewContext(response);
7307 if (!xpath_ctx) {
7308 err = IE_ERROR;
7309 goto leave;
7311 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7312 err = IE_ERROR;
7313 goto leave;
7316 /* Extract boxes if they present */
7317 result = xmlXPathEvalExpression(BAD_CAST
7318 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7319 xpath_ctx);
7320 if (!result) {
7321 err = IE_ERROR;
7322 goto leave;
7324 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7325 struct isds_list *item, *prev_item = NULL;
7326 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7327 item = calloc(1, sizeof(*item));
7328 if (!item) {
7329 err = IE_NOMEM;
7330 goto leave;
7333 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7334 if (i == 0) *boxes = item;
7335 else prev_item->next = item;
7336 prev_item = item;
7338 xpath_ctx->node = result->nodesetval->nodeTab[i];
7339 err = extract_DbOwnerInfo(context,
7340 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7341 if (err) goto leave;
7345 leave:
7346 if (err) {
7347 isds_list_free(boxes);
7348 } else {
7349 if (truncated) err = IE_2BIG;
7352 free(string);
7353 xmlFreeNode(request);
7354 xmlXPathFreeObject(result);
7355 xmlXPathFreeContext(xpath_ctx);
7357 free(code);
7358 free(message);
7359 xmlFreeDoc(response);
7361 if (!err)
7362 isds_log(ILF_ISDS, ILL_DEBUG,
7363 _("FindDataBox request processed by server successfully.\n"));
7364 #else /* not HAVE_LIBCURL */
7365 err = IE_NOTSUP;
7366 #endif
7368 return err;
7372 #if HAVE_LIBCURL
7373 /* Convert a string with match markers into a plain string with list of
7374 * pointers to the matches
7375 * @string is an UTF-8 encoded non-constant string with match markers
7376 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7377 * The markers will be removed from the string.
7378 * @starts is a reallocated list of static pointers into the @string pointing
7379 * to places where match start markers occured.
7380 * @ends is a reallocated list of static pointers into the @string pointing
7381 * to places where match end markers occured.
7382 * @return IE_SUCCESS in case of no failure. */
7383 static isds_error interpret_matches(xmlChar *string,
7384 struct isds_list **starts, struct isds_list **ends) {
7385 isds_error err = IE_SUCCESS;
7386 xmlChar *pointer, *destination, *source;
7387 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7389 isds_list_free(starts);
7390 isds_list_free(ends);
7391 if (NULL == starts || NULL == ends) return IE_INVAL;
7392 if (NULL == string) return IE_SUCCESS;
7394 for (pointer = string; *pointer != '\0';) {
7395 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7396 /* Remove the start marker */
7397 for (source = pointer + 14, destination = pointer;
7398 *source != '\0'; source++, destination++) {
7399 *destination = *source;
7401 *destination = '\0';
7402 /* Append the pointer into the list */
7403 item = calloc(1, sizeof(*item));
7404 if (!item) {
7405 err = IE_NOMEM;
7406 goto leave;
7408 item->destructor = (void (*)(void **))NULL;
7409 item->data = pointer;
7410 if (NULL == prev_start) *starts = item;
7411 else prev_start->next = item;
7412 prev_start = item;
7413 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7414 /* Remove the end marker */
7415 for (source = pointer + 12, destination = pointer;
7416 *source != '\0'; source++, destination++) {
7417 *destination = *source;
7419 *destination = '\0';
7420 /* Append the pointer into the list */
7421 item = calloc(1, sizeof(*item));
7422 if (!item) {
7423 err = IE_NOMEM;
7424 goto leave;
7426 item->destructor = (void (*)(void **))NULL;
7427 item->data = pointer;
7428 if (NULL == prev_end) *ends = item;
7429 else prev_end->next = item;
7430 prev_end = item;
7431 } else {
7432 pointer++;
7436 leave:
7437 if (err) {
7438 isds_list_free(starts);
7439 isds_list_free(ends);
7441 return err;
7445 /* Convert isds:dbResult XML tree into structure
7446 * @context is ISDS context.
7447 * @fulltext_result is automatically reallocated found box structure.
7448 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7449 * @collect_matches is true to interpret match markers.
7450 * In case of error @result will be freed. */
7451 static isds_error extract_dbResult(struct isds_ctx *context,
7452 struct isds_fulltext_result **fulltext_result,
7453 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7454 isds_error err = IE_SUCCESS;
7455 xmlXPathObjectPtr result = NULL;
7456 char *string = NULL;
7458 if (NULL == context) return IE_INVALID_CONTEXT;
7459 if (NULL == fulltext_result) return IE_INVAL;
7460 isds_fulltext_result_free(fulltext_result);
7461 if (!xpath_ctx) return IE_INVAL;
7464 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7465 if (NULL == *fulltext_result) {
7466 err = IE_NOMEM;
7467 goto leave;
7470 /* Extract data */
7471 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7473 EXTRACT_STRING("isds:dbType", string);
7474 if (NULL == string) {
7475 err = IE_ISDS;
7476 isds_log_message(context, _("Empty isds:dbType element"));
7477 goto leave;
7479 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7480 if (err) {
7481 if (err == IE_ENUM) {
7482 err = IE_ISDS;
7483 char *string_locale = _isds_utf82locale(string);
7484 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7485 string_locale);
7486 free(string_locale);
7488 goto leave;
7490 zfree(string);
7492 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7493 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7495 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7496 if (err) goto leave;
7498 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7499 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7500 (*fulltext_result)->dbEffectiveOVM);
7502 EXTRACT_STRING("isds:dbSendOptions", string);
7503 if (NULL == string) {
7504 err = IE_ISDS;
7505 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7506 goto leave;
7508 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7509 (*fulltext_result)->active = 1;
7510 (*fulltext_result)->public_sending = 1;
7511 (*fulltext_result)->commercial_sending = 0;
7512 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7513 (*fulltext_result)->active = 1;
7514 (*fulltext_result)->public_sending = 1;
7515 (*fulltext_result)->commercial_sending = 1;
7516 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7517 (*fulltext_result)->active = 1;
7518 (*fulltext_result)->public_sending = 0;
7519 (*fulltext_result)->commercial_sending = 1;
7520 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7521 (*fulltext_result)->active = 1;
7522 (*fulltext_result)->public_sending = 0;
7523 (*fulltext_result)->commercial_sending = 0;
7524 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7525 (*fulltext_result)->active = 0;
7526 (*fulltext_result)->public_sending = 0;
7527 (*fulltext_result)->commercial_sending = 0;
7528 } else {
7529 err = IE_ISDS;
7530 char *string_locale = _isds_utf82locale(string);
7531 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7532 string_locale);
7533 free(string_locale);
7534 goto leave;
7536 zfree(string);
7538 /* Interpret match marks */
7539 if (collect_matches) {
7540 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7541 &((*fulltext_result)->name_match_start),
7542 &((*fulltext_result)->name_match_end));
7543 if (err) goto leave;
7544 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7545 &((*fulltext_result)->address_match_start),
7546 &((*fulltext_result)->address_match_end));
7547 if (err) goto leave;
7550 leave:
7551 if (err) isds_fulltext_result_free(fulltext_result);
7552 free(string);
7553 xmlXPathFreeObject(result);
7554 return err;
7556 #endif /* HAVE_LIBCURL */
7559 /* Find boxes matching a given full-text criteria.
7560 * @context is a session context
7561 * @query is a non-empty string which consists of words to search
7562 * @target selects box attributes to search for @query words. Pass NULL if you
7563 * don't care.
7564 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7565 * to search in all box types. Pass NULL to let server to use default value
7566 * which is DBTYPE_SYSTEM.
7567 * @page_size defines count of boxes to constitute a response page. It counts
7568 * from zero. Pass NULL to let server to use a default value (50 now).
7569 * @page_number defines ordinar number of the response page to return. It
7570 * counts from zero. Pass NULL to let server to use a default value (0 now).
7571 * @track_matches points to true for marking @query words found in the box
7572 * attributes. It points to false for not marking. Pass NULL to let the server
7573 * to use default value (false now).
7574 * @total_matching_boxes outputs reallocated number of all boxes matching the
7575 * query. Will be pointer to NULL if server did not provide the value.
7576 * Pass NULL if you don't care.
7577 * @current_page_beginning outputs reallocated ordinar number of the first box
7578 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7579 * server did not provide the value. Pass NULL if you don't care.
7580 * @current_page_size outputs reallocated count of boxes in the this @boxes
7581 * page. It will be pointer to NULL if the server did not provide the value.
7582 * Pass NULL if you don't care.
7583 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7584 * is the last one, false if more boxes match, NULL if the server did not
7585 * provude the value. Pass NULL if you don't care.
7586 * @boxes outputs reallocated list of isds_fulltext_result structures,
7587 * possibly empty.
7588 * @return:
7589 * IE_SUCCESS if search succeeded
7590 * IE_2BIG if @page_size is too large
7591 * other code if something bad happens; output arguments will be NULL. */
7592 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7593 const char *query,
7594 const isds_fulltext_target *target,
7595 const isds_DbType *box_type,
7596 const unsigned long int *page_size,
7597 const unsigned long int *page_number,
7598 const _Bool *track_matches,
7599 unsigned long int **total_matching_boxes,
7600 unsigned long int **current_page_beginning,
7601 unsigned long int **current_page_size,
7602 _Bool **last_page,
7603 struct isds_list **boxes) {
7604 isds_error err = IE_SUCCESS;
7605 #if HAVE_LIBCURL
7606 xmlNsPtr isds_ns = NULL;
7607 xmlNodePtr request = NULL;
7608 xmlDocPtr response = NULL;
7609 xmlNodePtr node;
7610 xmlXPathContextPtr xpath_ctx = NULL;
7611 xmlXPathObjectPtr result = NULL;
7612 const xmlChar *static_string = NULL;
7613 xmlChar *string = NULL;
7615 const xmlChar *codes[] = {
7616 BAD_CAST "1004",
7617 BAD_CAST "1152",
7618 BAD_CAST "1153",
7619 BAD_CAST "1154",
7620 BAD_CAST "1155",
7621 BAD_CAST "1156",
7622 BAD_CAST "9002",
7623 NULL
7625 const char *meanings[] = {
7626 N_("You are not allowed to perform the search"),
7627 N_("The query string is empty"),
7628 N_("Searched box ID is malformed"),
7629 N_("Searched organization ID is malformed"),
7630 N_("Invalid input"),
7631 N_("Requested page size is too large"),
7632 N_("Search engine internal error")
7634 const isds_error errors[] = {
7635 IE_ISDS,
7636 IE_INVAL,
7637 IE_INVAL,
7638 IE_INVAL,
7639 IE_INVAL,
7640 IE_2BIG,
7641 IE_ISDS
7643 struct code_map_isds_error map = {
7644 .codes = codes,
7645 .meanings = meanings,
7646 .errors = errors
7648 #endif
7651 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7652 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7653 if (NULL != current_page_size) zfree(*current_page_size);
7654 if (NULL != last_page) zfree(*last_page);
7655 isds_list_free(boxes);
7657 if (NULL == context) return IE_INVALID_CONTEXT;
7658 zfree(context->long_message);
7660 if (NULL == boxes) return IE_INVAL;
7662 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
7663 isds_log_message(context, _("Query string must be non-empty"));
7664 return IE_INVAL;
7667 #if HAVE_LIBCURL
7668 /* Check if connection is established
7669 * TODO: This check should be done downstairs. */
7670 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7672 /* Build FindDataBox request */
7673 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
7674 if (NULL == request) {
7675 isds_log_message(context,
7676 _("Could not build ISDSSearch2 request"));
7677 return IE_ERROR;
7679 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7680 if(NULL == isds_ns) {
7681 isds_log_message(context, _("Could not create ISDS name space"));
7682 xmlFreeNode(request);
7683 return IE_ERROR;
7685 xmlSetNs(request, isds_ns);
7687 INSERT_STRING(request, "searchText", query);
7689 if (NULL != target) {
7690 static_string = isds_fulltext_target2string(*(target));
7691 if (NULL == static_string) {
7692 isds_printf_message(context, _("Invalid target value: %d"),
7693 *(target));
7694 err = IE_ENUM;
7695 goto leave;
7698 INSERT_STRING(request, "searchType", static_string);
7699 static_string = NULL;
7701 if (NULL != box_type) {
7702 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7703 if (DBTYPE_SYSTEM == *box_type) {
7704 static_string = BAD_CAST "ALL";
7705 } else {
7706 static_string = isds_DbType2string(*(box_type));
7707 if (NULL == static_string) {
7708 isds_printf_message(context, _("Invalid box type value: %d"),
7709 *(box_type));
7710 err = IE_ENUM;
7711 goto leave;
7715 INSERT_STRING(request, "searchScope", static_string);
7716 static_string = NULL;
7718 INSERT_ULONGINT(request, "page", page_number, string);
7719 INSERT_ULONGINT(request, "pageSize", page_size, string);
7720 INSERT_BOOLEAN(request, "highlighting", track_matches);
7722 /* Send request and check response */
7723 err = send_destroy_request_check_response(context,
7724 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
7725 &request, &response, NULL, &map);
7726 if (err) goto leave;
7728 /* Parse response */
7729 xpath_ctx = xmlXPathNewContext(response);
7730 if (NULL == xpath_ctx) {
7731 err = IE_ERROR;
7732 goto leave;
7734 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7735 err = IE_ERROR;
7736 goto leave;
7738 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
7739 xpath_ctx);
7740 if (!result) {
7741 err = IE_ERROR;
7742 goto leave;
7744 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7745 isds_log_message(context, _("Missing ISDSSearch2 element"));
7746 err = IE_ISDS;
7747 goto leave;
7749 if (result->nodesetval->nodeNr > 1) {
7750 isds_log_message(context, _("Multiple ISDSSearch2 element"));
7751 err = IE_ISDS;
7752 goto leave;
7754 xpath_ctx->node = result->nodesetval->nodeTab[0];
7755 xmlXPathFreeObject(result); result = NULL;
7758 /* Extract counters */
7759 if (NULL != total_matching_boxes) {
7760 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
7762 if (NULL != current_page_size) {
7763 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
7765 if (NULL != current_page_beginning) {
7766 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
7768 if (NULL != last_page) {
7769 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
7771 xmlXPathFreeObject(result); result = NULL;
7773 /* Extract boxes if they present */
7774 result = xmlXPathEvalExpression(BAD_CAST
7775 "isds:dbResults/isds:dbResult", xpath_ctx);
7776 if (NULL == result) {
7777 err = IE_ERROR;
7778 goto leave;
7780 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7781 struct isds_list *item, *prev_item = NULL;
7782 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7783 item = calloc(1, sizeof(*item));
7784 if (!item) {
7785 err = IE_NOMEM;
7786 goto leave;
7789 item->destructor = (void (*)(void **))isds_fulltext_result_free;
7790 if (i == 0) *boxes = item;
7791 else prev_item->next = item;
7792 prev_item = item;
7794 xpath_ctx->node = result->nodesetval->nodeTab[i];
7795 err = extract_dbResult(context,
7796 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
7797 (NULL == track_matches) ? 0 : *track_matches);
7798 if (err) goto leave;
7802 leave:
7803 if (err) {
7804 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7805 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7806 if (NULL != current_page_size) zfree(*current_page_size);
7807 if (NULL != last_page) zfree(*last_page);
7808 isds_list_free(boxes);
7811 free(string);
7812 xmlFreeNode(request);
7813 xmlXPathFreeObject(result);
7814 xmlXPathFreeContext(xpath_ctx);
7815 xmlFreeDoc(response);
7817 if (!err)
7818 isds_log(ILF_ISDS, ILL_DEBUG,
7819 _("ISDSSearch2 request processed by server successfully.\n"));
7820 #else /* not HAVE_LIBCURL */
7821 err = IE_NOTSUP;
7822 #endif
7824 return err;
7828 /* Get status of a box.
7829 * @context is ISDS session context.
7830 * @box_id is UTF-8 encoded box identifier as zero terminated string
7831 * @box_status is return value of box status.
7832 * @return:
7833 * IE_SUCCESS if box has been found and its status retrieved
7834 * IE_NOEXIST if box is not known to ISDS server
7835 * or other appropriate error.
7836 * You can use isds_DbState to enumerate box status. However out of enum
7837 * range value can be returned too. This is feature because ISDS
7838 * specification leaves the set of values open.
7839 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7840 * the box has been deleted, but ISDS still lists its former existence. */
7841 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7842 long int *box_status) {
7843 isds_error err = IE_SUCCESS;
7844 #if HAVE_LIBCURL
7845 xmlNsPtr isds_ns = NULL;
7846 xmlNodePtr request = NULL, db_id;
7847 xmlDocPtr response = NULL;
7848 xmlXPathContextPtr xpath_ctx = NULL;
7849 xmlXPathObjectPtr result = NULL;
7850 xmlChar *string = NULL;
7852 const xmlChar *codes[] = {
7853 BAD_CAST "5001",
7854 BAD_CAST "1007",
7855 BAD_CAST "2011",
7856 NULL
7858 const char *meanings[] = {
7859 "The box does not exist",
7860 "Box ID is malformed",
7861 "Box ID malformed",
7863 const isds_error errors[] = {
7864 IE_NOEXIST,
7865 IE_INVAL,
7866 IE_INVAL,
7868 struct code_map_isds_error map = {
7869 .codes = codes,
7870 .meanings = meanings,
7871 .errors = errors
7873 #endif
7875 if (!context) return IE_INVALID_CONTEXT;
7876 zfree(context->long_message);
7877 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7879 #if HAVE_LIBCURL
7880 /* Check if connection is established
7881 * TODO: This check should be done downstairs. */
7882 if (!context->curl) return IE_CONNECTION_CLOSED;
7885 /* Build CheckDataBox request */
7886 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7887 if (!request) {
7888 isds_log_message(context,
7889 _("Could build CheckDataBox request"));
7890 return IE_ERROR;
7892 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7893 if(!isds_ns) {
7894 isds_log_message(context, _("Could not create ISDS name space"));
7895 xmlFreeNode(request);
7896 return IE_ERROR;
7898 xmlSetNs(request, isds_ns);
7899 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7900 if (!db_id) {
7901 isds_log_message(context, _("Could not add dbID child to "
7902 "CheckDataBox element"));
7903 xmlFreeNode(request);
7904 return IE_ERROR;
7908 /* Send request and check response*/
7909 err = send_destroy_request_check_response(context,
7910 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7911 &request, &response, NULL, &map);
7912 if (err) goto leave;
7915 /* Extract data */
7916 xpath_ctx = xmlXPathNewContext(response);
7917 if (!xpath_ctx) {
7918 err = IE_ERROR;
7919 goto leave;
7921 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7922 err = IE_ERROR;
7923 goto leave;
7925 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7926 xpath_ctx);
7927 if (!result) {
7928 err = IE_ERROR;
7929 goto leave;
7931 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7932 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7933 err = IE_ISDS;
7934 goto leave;
7936 if (result->nodesetval->nodeNr > 1) {
7937 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7938 err = IE_ISDS;
7939 goto leave;
7941 xpath_ctx->node = result->nodesetval->nodeTab[0];
7942 xmlXPathFreeObject(result); result = NULL;
7944 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7947 leave:
7948 free(string);
7949 xmlXPathFreeObject(result);
7950 xmlXPathFreeContext(xpath_ctx);
7952 xmlFreeDoc(response);
7954 if (!err)
7955 isds_log(ILF_ISDS, ILL_DEBUG,
7956 _("CheckDataBox request processed by server successfully.\n"));
7957 #else /* not HAVE_LIBCURL */
7958 err = IE_NOTSUP;
7959 #endif
7961 return err;
7965 /* Get list of permissions to send commercial messages.
7966 * @context is ISDS session context.
7967 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7968 * @permissions is a reallocated list of permissions (struct
7969 * isds_commercial_permission*) to send commercial messages from @box_id. The
7970 * order of permissions is significant as the server applies the permissions
7971 * and associated pre-paid credits in the order. Empty list means no
7972 * permission.
7973 * @return:
7974 * IE_SUCCESS if the list has been obtained correctly,
7975 * or other appropriate error. */
7976 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7977 const char *box_id, struct isds_list **permissions) {
7978 isds_error err = IE_SUCCESS;
7979 #if HAVE_LIBCURL
7980 xmlDocPtr response = NULL;
7981 xmlXPathContextPtr xpath_ctx = NULL;
7982 xmlXPathObjectPtr result = NULL;
7983 #endif
7985 if (!context) return IE_INVALID_CONTEXT;
7986 zfree(context->long_message);
7987 if (NULL == permissions) return IE_INVAL;
7988 isds_list_free(permissions);
7989 if (NULL == box_id) return IE_INVAL;
7991 #if HAVE_LIBCURL
7992 /* Check if connection is established */
7993 if (!context->curl) return IE_CONNECTION_CLOSED;
7995 /* Do request and check for success */
7996 err = build_send_dbid_request_check_response(context,
7997 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7998 BAD_CAST box_id, NULL, &response, NULL);
7999 if (!err) {
8000 isds_log(ILF_ISDS, ILL_DEBUG,
8001 _("PDZInfo request processed by server successfully.\n"));
8004 /* Extract data */
8005 /* Prepare structure */
8006 xpath_ctx = xmlXPathNewContext(response);
8007 if (!xpath_ctx) {
8008 err = IE_ERROR;
8009 goto leave;
8011 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8012 err = IE_ERROR;
8013 goto leave;
8016 /* Set context node */
8017 result = xmlXPathEvalExpression(BAD_CAST
8018 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8019 xpath_ctx);
8020 if (!result) {
8021 err = IE_ERROR;
8022 goto leave;
8024 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8025 struct isds_list *prev_item = NULL;
8027 /* Iterate over all permission records */
8028 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8029 struct isds_list *item;
8031 /* Prepare structure */
8032 item = calloc(1, sizeof(*item));
8033 if (!item) {
8034 err = IE_NOMEM;
8035 goto leave;
8037 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8038 if (i == 0) *permissions = item;
8039 else prev_item->next = item;
8040 prev_item = item;
8042 /* Extract it */
8043 xpath_ctx->node = result->nodesetval->nodeTab[i];
8044 err = extract_DbPDZRecord(context,
8045 (struct isds_commercial_permission **) (&item->data),
8046 xpath_ctx);
8047 if (err) goto leave;
8051 leave:
8052 if (err) {
8053 isds_list_free(permissions);
8056 xmlXPathFreeObject(result);
8057 xmlXPathFreeContext(xpath_ctx);
8058 xmlFreeDoc(response);
8060 #else /* not HAVE_LIBCURL */
8061 err = IE_NOTSUP;
8062 #endif
8064 return err;
8068 /* Get details about credit for sending pre-paid commercial messages.
8069 * @context is ISDS session context.
8070 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8071 * @from_date is first day of credit history to return in @history. Only
8072 * tm_year, tm_mon and tm_mday carry sane value.
8073 * @to_date is last day of credit history to return in @history. Only
8074 * tm_year, tm_mon and tm_mday carry sane value.
8075 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8076 * if you don't care. This and all other credit values are integers in
8077 * hundredths of Czech Crowns.
8078 * @email outputs notification e-mail address where notifications about credit
8079 * are sent. This is automatically reallocated string. Pass NULL if you don't
8080 * care. It can return NULL if no address is defined.
8081 * @history outputs auto-reallocated list of pointers to struct
8082 * isds_credit_event. Events in closed interval @from_time to @to_time are
8083 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8084 * are sorted by time.
8085 * @return:
8086 * IE_SUCCESS if the credit details have been obtained correctly,
8087 * or other appropriate error. Please note that server allows to retrieve
8088 * only limited history of events. */
8089 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8090 const char *box_id,
8091 const struct tm *from_date, const struct tm *to_date,
8092 long int *credit, char **email, struct isds_list **history) {
8093 isds_error err = IE_SUCCESS;
8094 #if HAVE_LIBCURL
8095 char *box_id_locale = NULL;
8096 xmlNodePtr request = NULL, node;
8097 xmlNsPtr isds_ns = NULL;
8098 xmlChar *string = NULL;
8100 xmlDocPtr response = NULL;
8101 xmlXPathContextPtr xpath_ctx = NULL;
8102 xmlXPathObjectPtr result = NULL;
8104 const xmlChar *codes[] = {
8105 BAD_CAST "1004",
8106 BAD_CAST "2011",
8107 BAD_CAST "1093",
8108 BAD_CAST "1137",
8109 BAD_CAST "1058",
8110 NULL
8112 const char *meanings[] = {
8113 "Insufficient priviledges for the box",
8114 "The box does not exist",
8115 "Date is too long (history is not available after 15 months)",
8116 "Interval is too long (limit is 3 months)",
8117 "Invalid date"
8119 const isds_error errors[] = {
8120 IE_ISDS,
8121 IE_NOEXIST,
8122 IE_DATE,
8123 IE_DATE,
8124 IE_DATE,
8126 struct code_map_isds_error map = {
8127 .codes = codes,
8128 .meanings = meanings,
8129 .errors = errors
8131 #endif
8133 if (!context) return IE_INVALID_CONTEXT;
8134 zfree(context->long_message);
8136 /* Free output argument */
8137 if (NULL != credit) *credit = 0;
8138 if (NULL != email) zfree(*email);
8139 isds_list_free(history);
8141 if (NULL == box_id) return IE_INVAL;
8143 #if HAVE_LIBCURL
8144 /* Check if connection is established */
8145 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8147 box_id_locale = _isds_utf82locale((char*)box_id);
8148 if (NULL == box_id_locale) {
8149 err = IE_NOMEM;
8150 goto leave;
8153 /* Build request */
8154 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8155 if (NULL == request) {
8156 isds_printf_message(context,
8157 _("Could not build DataBoxCreditInfo request for %s box"),
8158 box_id_locale);
8159 err = IE_ERROR;
8160 goto leave;
8162 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8163 if(!isds_ns) {
8164 isds_log_message(context, _("Could not create ISDS name space"));
8165 err = IE_ERROR;
8166 goto leave;
8168 xmlSetNs(request, isds_ns);
8170 /* Add mandatory XSD:tIdDbInput child */
8171 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8172 /* Add mandatory dates elements with optional values */
8173 if (from_date) {
8174 err = tm2datestring(from_date, &string);
8175 if (err) {
8176 isds_log_message(context,
8177 _("Could not convert `from_date' argument to ISO date "
8178 "string"));
8179 goto leave;
8181 INSERT_STRING(request, "ciFromDate", string);
8182 zfree(string);
8183 } else {
8184 INSERT_STRING(request, "ciFromDate", NULL);
8186 if (to_date) {
8187 err = tm2datestring(to_date, &string);
8188 if (err) {
8189 isds_log_message(context,
8190 _("Could not convert `to_date' argument to ISO date "
8191 "string"));
8192 goto leave;
8194 INSERT_STRING(request, "ciTodate", string);
8195 zfree(string);
8196 } else {
8197 INSERT_STRING(request, "ciTodate", NULL);
8200 /* Send request and check response*/
8201 err = send_destroy_request_check_response(context,
8202 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8203 &request, &response, NULL, &map);
8204 if (err) goto leave;
8207 /* Extract data */
8208 /* Set context to the root */
8209 xpath_ctx = xmlXPathNewContext(response);
8210 if (!xpath_ctx) {
8211 err = IE_ERROR;
8212 goto leave;
8214 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8215 err = IE_ERROR;
8216 goto leave;
8218 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8219 xpath_ctx);
8220 if (!result) {
8221 err = IE_ERROR;
8222 goto leave;
8224 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8225 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8226 err = IE_ISDS;
8227 goto leave;
8229 if (result->nodesetval->nodeNr > 1) {
8230 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8231 err = IE_ISDS;
8232 goto leave;
8234 xpath_ctx->node = result->nodesetval->nodeTab[0];
8235 xmlXPathFreeObject(result); result = NULL;
8237 /* Extract common data */
8238 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8239 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8241 /* Extract records */
8242 if (NULL == history) goto leave;
8243 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8244 xpath_ctx);
8245 if (!result) {
8246 err = IE_ERROR;
8247 goto leave;
8249 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8250 struct isds_list *prev_item = NULL;
8252 /* Iterate over all records */
8253 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8254 struct isds_list *item;
8256 /* Prepare structure */
8257 item = calloc(1, sizeof(*item));
8258 if (!item) {
8259 err = IE_NOMEM;
8260 goto leave;
8262 item->destructor = (void(*)(void**))isds_credit_event_free;
8263 if (i == 0) *history = item;
8264 else prev_item->next = item;
8265 prev_item = item;
8267 /* Extract it */
8268 xpath_ctx->node = result->nodesetval->nodeTab[i];
8269 err = extract_CiRecord(context,
8270 (struct isds_credit_event **) (&item->data),
8271 xpath_ctx);
8272 if (err) goto leave;
8276 leave:
8277 if (!err) {
8278 isds_log(ILF_ISDS, ILL_DEBUG,
8279 _("DataBoxCreditInfo request processed by server successfully.\n"));
8281 if (err) {
8282 isds_list_free(history);
8283 if (NULL != email) zfree(*email)
8286 free(box_id_locale);
8287 xmlXPathFreeObject(result);
8288 xmlXPathFreeContext(xpath_ctx);
8289 xmlFreeDoc(response);
8291 #else /* not HAVE_LIBCURL */
8292 err = IE_NOTSUP;
8293 #endif
8295 return err;
8299 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8300 * code, destroy response and log success.
8301 * @context is ISDS session context.
8302 * @service_name is name of SERVICE_DB_MANIPULATION service
8303 * @box_id is UTF-8 encoded box identifier as zero terminated string
8304 * @approval is optional external approval of box manipulation
8305 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8306 * NULL, if you don't care. */
8307 static isds_error build_send_manipulationdbid_request_check_drop_response(
8308 struct isds_ctx *context, const xmlChar *service_name,
8309 const xmlChar *box_id, const struct isds_approval *approval,
8310 xmlChar **refnumber) {
8311 isds_error err = IE_SUCCESS;
8312 #if HAVE_LIBCURL
8313 xmlDocPtr response = NULL;
8314 #endif
8316 if (!context) return IE_INVALID_CONTEXT;
8317 zfree(context->long_message);
8318 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8320 #if HAVE_LIBCURL
8321 /* Check if connection is established */
8322 if (!context->curl) return IE_CONNECTION_CLOSED;
8324 /* Do request and check for success */
8325 err = build_send_dbid_request_check_response(context,
8326 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8327 &response, refnumber);
8328 xmlFreeDoc(response);
8330 if (!err) {
8331 char *service_name_locale = _isds_utf82locale((char *) service_name);
8332 isds_log(ILF_ISDS, ILL_DEBUG,
8333 _("%s request processed by server successfully.\n"),
8334 service_name_locale);
8335 free(service_name_locale);
8337 #else /* not HAVE_LIBCURL */
8338 err = IE_NOTSUP;
8339 #endif
8341 return err;
8345 /* Switch box into state where box can receive commercial messages (off by
8346 * default)
8347 * @context is ISDS session context.
8348 * @box_id is UTF-8 encoded box identifier as zero terminated string
8349 * @allow is true for enable, false for disable commercial messages income
8350 * @approval is optional external approval of box manipulation
8351 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8352 * NULL, if you don't care. */
8353 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8354 const char *box_id, const _Bool allow,
8355 const struct isds_approval *approval, char **refnumber) {
8356 return build_send_manipulationdbid_request_check_drop_response(context,
8357 (allow) ? BAD_CAST "SetOpenAddressing" :
8358 BAD_CAST "ClearOpenAddressing",
8359 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8363 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8364 * message acceptance). This is just a box permission. Sender must apply
8365 * such role by sending each message.
8366 * @context is ISDS session context.
8367 * @box_id is UTF-8 encoded box identifier as zero terminated string
8368 * @allow is true for enable, false for disable OVM role permission
8369 * @approval is optional external approval of box manipulation
8370 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8371 * NULL, if you don't care. */
8372 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
8373 const char *box_id, const _Bool allow,
8374 const struct isds_approval *approval, char **refnumber) {
8375 return build_send_manipulationdbid_request_check_drop_response(context,
8376 (allow) ? BAD_CAST "SetEffectiveOVM" :
8377 BAD_CAST "ClearEffectiveOVM",
8378 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8382 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8383 * code, destroy response and log success.
8384 * @context is ISDS session context.
8385 * @service_name is name of SERVICE_DB_MANIPULATION service
8386 * @owner is structure describing box
8387 * @approval is optional external approval of box manipulation
8388 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8389 * NULL, if you don't care. */
8390 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8391 struct isds_ctx *context, const xmlChar *service_name,
8392 const struct isds_DbOwnerInfo *owner,
8393 const struct isds_approval *approval, xmlChar **refnumber) {
8394 isds_error err = IE_SUCCESS;
8395 #if HAVE_LIBCURL
8396 char *service_name_locale = NULL;
8397 xmlNodePtr request = NULL, db_owner_info;
8398 xmlNsPtr isds_ns = NULL;
8399 #endif
8402 if (!context) return IE_INVALID_CONTEXT;
8403 zfree(context->long_message);
8404 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8406 #if HAVE_LIBCURL
8407 service_name_locale = _isds_utf82locale((char*)service_name);
8408 if (!service_name_locale) {
8409 err = IE_NOMEM;
8410 goto leave;
8413 /* Build request */
8414 request = xmlNewNode(NULL, service_name);
8415 if (!request) {
8416 isds_printf_message(context,
8417 _("Could not build %s request"), service_name_locale);
8418 err = IE_ERROR;
8419 goto leave;
8421 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8422 if(!isds_ns) {
8423 isds_log_message(context, _("Could not create ISDS name space"));
8424 err = IE_ERROR;
8425 goto leave;
8427 xmlSetNs(request, isds_ns);
8430 /* Add XSD:tOwnerInfoInput child*/
8431 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8432 err = insert_DbOwnerInfo(context, owner, db_owner_info);
8433 if (err) goto leave;
8435 /* Add XSD:gExtApproval*/
8436 err = insert_GExtApproval(context, approval, request);
8437 if (err) goto leave;
8439 /* Send it to server and process response */
8440 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8441 service_name, &request, refnumber);
8443 leave:
8444 xmlFreeNode(request);
8445 free(service_name_locale);
8446 #else /* not HAVE_LIBCURL */
8447 err = IE_NOTSUP;
8448 #endif
8450 return err;
8454 /* Switch box accessibility state on request of box owner.
8455 * Despite the name, owner must do the request off-line. This function is
8456 * designed for such off-line meeting points (e.g. Czech POINT).
8457 * @context is ISDS session context.
8458 * @box identifies box to switch accessibility state.
8459 * @allow is true for making accessible, false to disallow access.
8460 * @approval is optional external approval of box manipulation
8461 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8462 * NULL, if you don't care. */
8463 isds_error isds_switch_box_accessibility_on_owner_request(
8464 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8465 const _Bool allow, const struct isds_approval *approval,
8466 char **refnumber) {
8467 return build_send_manipulationdbowner_request_check_drop_response(context,
8468 (allow) ? BAD_CAST "EnableOwnDataBox" :
8469 BAD_CAST "DisableOwnDataBox",
8470 box, approval, (xmlChar **) refnumber);
8474 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8475 * date.
8476 * @context is ISDS session context.
8477 * @box identifies box to switch accessibility state.
8478 * @since is date since accessibility has been denied. This can be past too.
8479 * Only tm_year, tm_mon and tm_mday carry sane value.
8480 * @approval is optional external approval of box manipulation
8481 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8482 * NULL, if you don't care. */
8483 isds_error isds_disable_box_accessibility_externaly(
8484 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8485 const struct tm *since, const struct isds_approval *approval,
8486 char **refnumber) {
8487 isds_error err = IE_SUCCESS;
8488 #if HAVE_LIBCURL
8489 char *service_name_locale = NULL;
8490 xmlNodePtr request = NULL, node;
8491 xmlNsPtr isds_ns = NULL;
8492 xmlChar *string = NULL;
8493 #endif
8496 if (!context) return IE_INVALID_CONTEXT;
8497 zfree(context->long_message);
8498 if (!box || !since) return IE_INVAL;
8500 #if HAVE_LIBCURL
8501 /* Build request */
8502 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
8503 if (!request) {
8504 isds_printf_message(context,
8505 _("Could not build %s request"), "DisableDataBoxExternally");
8506 err = IE_ERROR;
8507 goto leave;
8509 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8510 if(!isds_ns) {
8511 isds_log_message(context, _("Could not create ISDS name space"));
8512 err = IE_ERROR;
8513 goto leave;
8515 xmlSetNs(request, isds_ns);
8518 /* Add @box identification */
8519 INSERT_ELEMENT(node, request, "dbOwnerInfo");
8520 err = insert_DbOwnerInfo(context, box, node);
8521 if (err) goto leave;
8523 /* Add @since date */
8524 err = tm2datestring(since, &string);
8525 if(err) {
8526 isds_log_message(context,
8527 _("Could not convert `since' argument to ISO date string"));
8528 goto leave;
8530 INSERT_STRING(request, "dbOwnerDisableDate", string);
8531 zfree(string);
8533 /* Add @approval */
8534 err = insert_GExtApproval(context, approval, request);
8535 if (err) goto leave;
8537 /* Send it to server and process response */
8538 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8539 BAD_CAST "DisableDataBoxExternally", &request,
8540 (xmlChar **) refnumber);
8542 leave:
8543 free(string);
8544 xmlFreeNode(request);
8545 free(service_name_locale);
8546 #else /* not HAVE_LIBCURL */
8547 err = IE_NOTSUP;
8548 #endif
8550 return err;
8554 #if HAVE_LIBCURL
8555 /* Insert struct isds_message data (envelope (recipient data optional) and
8556 * documents into XML tree
8557 * @context is session context
8558 * @outgoing_message is libisds structure with message data
8559 * @create_message is XML CreateMessage or CreateMultipleMessage element
8560 * @process_recipient true for recipient data serialization, false for no
8561 * serialization */
8562 static isds_error insert_envelope_files(struct isds_ctx *context,
8563 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8564 const _Bool process_recipient) {
8566 isds_error err = IE_SUCCESS;
8567 xmlNodePtr envelope, dm_files, node;
8568 xmlChar *string = NULL;
8570 if (!context) return IE_INVALID_CONTEXT;
8571 if (!outgoing_message || !create_message) return IE_INVAL;
8574 /* Build envelope */
8575 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8576 if (!envelope) {
8577 isds_printf_message(context, _("Could not add dmEnvelope child to "
8578 "%s element"), create_message->name);
8579 return IE_ERROR;
8582 if (!outgoing_message->envelope) {
8583 isds_log_message(context, _("Outgoing message is missing envelope"));
8584 err = IE_INVAL;
8585 goto leave;
8588 /* Insert optional message type */
8589 err = insert_message_type(context, outgoing_message->envelope->dmType,
8590 envelope);
8591 if (err) goto leave;
8593 INSERT_STRING(envelope, "dmSenderOrgUnit",
8594 outgoing_message->envelope->dmSenderOrgUnit);
8595 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8596 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8598 if (process_recipient) {
8599 if (!outgoing_message->envelope->dbIDRecipient) {
8600 isds_log_message(context,
8601 _("Outgoing message is missing recipient box identifier"));
8602 err = IE_INVAL;
8603 goto leave;
8605 INSERT_STRING(envelope, "dbIDRecipient",
8606 outgoing_message->envelope->dbIDRecipient);
8608 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8609 outgoing_message->envelope->dmRecipientOrgUnit);
8610 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8611 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8612 INSERT_STRING(envelope, "dmToHands",
8613 outgoing_message->envelope->dmToHands);
8616 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8617 "dmAnnotation");
8618 INSERT_STRING(envelope, "dmAnnotation",
8619 outgoing_message->envelope->dmAnnotation);
8621 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8622 0, 50, "dmRecipientRefNumber");
8623 INSERT_STRING(envelope, "dmRecipientRefNumber",
8624 outgoing_message->envelope->dmRecipientRefNumber);
8626 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8627 0, 50, "dmSenderRefNumber");
8628 INSERT_STRING(envelope, "dmSenderRefNumber",
8629 outgoing_message->envelope->dmSenderRefNumber);
8631 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8632 0, 50, "dmRecipientIdent");
8633 INSERT_STRING(envelope, "dmRecipientIdent",
8634 outgoing_message->envelope->dmRecipientIdent);
8636 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8637 0, 50, "dmSenderIdent");
8638 INSERT_STRING(envelope, "dmSenderIdent",
8639 outgoing_message->envelope->dmSenderIdent);
8641 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8642 outgoing_message->envelope->dmLegalTitleLaw, string);
8643 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8644 outgoing_message->envelope->dmLegalTitleYear, string);
8645 INSERT_STRING(envelope, "dmLegalTitleSect",
8646 outgoing_message->envelope->dmLegalTitleSect);
8647 INSERT_STRING(envelope, "dmLegalTitlePar",
8648 outgoing_message->envelope->dmLegalTitlePar);
8649 INSERT_STRING(envelope, "dmLegalTitlePoint",
8650 outgoing_message->envelope->dmLegalTitlePoint);
8652 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8653 outgoing_message->envelope->dmPersonalDelivery);
8654 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8655 outgoing_message->envelope->dmAllowSubstDelivery);
8657 /* ???: Should we require value for dbEffectiveOVM sender?
8658 * ISDS has default as true */
8659 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8660 INSERT_BOOLEAN(envelope, "dmOVM",
8661 outgoing_message->envelope->dmPublishOwnID);
8664 /* Append dmFiles */
8665 if (!outgoing_message->documents) {
8666 isds_log_message(context,
8667 _("Outgoing message is missing list of documents"));
8668 err = IE_INVAL;
8669 goto leave;
8671 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8672 if (!dm_files) {
8673 isds_printf_message(context, _("Could not add dmFiles child to "
8674 "%s element"), create_message->name);
8675 err = IE_ERROR;
8676 goto leave;
8679 /* Check for document hierarchy */
8680 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8681 if (err) goto leave;
8683 /* Process each document */
8684 for (struct isds_list *item =
8685 (struct isds_list *) outgoing_message->documents;
8686 item; item = item->next) {
8687 if (!item->data) {
8688 isds_log_message(context,
8689 _("List of documents contains empty item"));
8690 err = IE_INVAL;
8691 goto leave;
8693 /* FIXME: Check for dmFileMetaType and for document references.
8694 * Only first document can be of MAIN type */
8695 err = insert_document(context, (struct isds_document*) item->data,
8696 dm_files);
8698 if (err) goto leave;
8701 leave:
8702 free(string);
8703 return err;
8705 #endif /* HAVE_LIBCURL */
8708 /* Send a message via ISDS to a recipient
8709 * @context is session context
8710 * @outgoing_message is message to send; Some members are mandatory (like
8711 * dbIDRecipient), some are optional and some are irrelevant (especially data
8712 * about sender). Included pointer to isds_list documents must contain at
8713 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8714 * members will be filled with valid data from ISDS. Exact list of write
8715 * members is subject to change. Currently dmID is changed.
8716 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8717 isds_error isds_send_message(struct isds_ctx *context,
8718 struct isds_message *outgoing_message) {
8720 isds_error err = IE_SUCCESS;
8721 #if HAVE_LIBCURL
8722 xmlNsPtr isds_ns = NULL;
8723 xmlNodePtr request = NULL;
8724 xmlDocPtr response = NULL;
8725 xmlChar *code = NULL, *message = NULL;
8726 xmlXPathContextPtr xpath_ctx = NULL;
8727 xmlXPathObjectPtr result = NULL;
8728 /*_Bool message_is_complete = 0;*/
8729 #endif
8731 if (!context) return IE_INVALID_CONTEXT;
8732 zfree(context->long_message);
8733 if (!outgoing_message) return IE_INVAL;
8735 #if HAVE_LIBCURL
8736 /* Check if connection is established
8737 * TODO: This check should be done downstairs. */
8738 if (!context->curl) return IE_CONNECTION_CLOSED;
8741 /* Build CreateMessage request */
8742 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8743 if (!request) {
8744 isds_log_message(context,
8745 _("Could not build CreateMessage request"));
8746 return IE_ERROR;
8748 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8749 if(!isds_ns) {
8750 isds_log_message(context, _("Could not create ISDS name space"));
8751 xmlFreeNode(request);
8752 return IE_ERROR;
8754 xmlSetNs(request, isds_ns);
8756 /* Append envelope and files */
8757 err = insert_envelope_files(context, outgoing_message, request, 1);
8758 if (err) goto leave;
8761 /* Signal we can serialize message since now */
8762 /*message_is_complete = 1;*/
8765 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8767 /* Sent request */
8768 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8770 /* Don't' destroy request, we want to provide it to application later */
8772 if (err) {
8773 isds_log(ILF_ISDS, ILL_DEBUG,
8774 _("Processing ISDS response on CreateMessage "
8775 "request failed\n"));
8776 goto leave;
8779 /* Check for response status */
8780 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8781 &code, &message, NULL);
8782 if (err) {
8783 isds_log(ILF_ISDS, ILL_DEBUG,
8784 _("ISDS response on CreateMessage request "
8785 "is missing status\n"));
8786 goto leave;
8789 /* Request processed, but refused by server or server failed */
8790 if (xmlStrcmp(code, BAD_CAST "0000")) {
8791 char *box_id_locale =
8792 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8793 char *code_locale = _isds_utf82locale((char*)code);
8794 char *message_locale = _isds_utf82locale((char*)message);
8795 isds_log(ILF_ISDS, ILL_DEBUG,
8796 _("Server did not accept message for %s on CreateMessage "
8797 "request (code=%s, message=%s)\n"),
8798 box_id_locale, code_locale, message_locale);
8799 isds_log_message(context, message_locale);
8800 free(box_id_locale);
8801 free(code_locale);
8802 free(message_locale);
8803 err = IE_ISDS;
8804 goto leave;
8808 /* Extract data */
8809 xpath_ctx = xmlXPathNewContext(response);
8810 if (!xpath_ctx) {
8811 err = IE_ERROR;
8812 goto leave;
8814 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8815 err = IE_ERROR;
8816 goto leave;
8818 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8819 xpath_ctx);
8820 if (!result) {
8821 err = IE_ERROR;
8822 goto leave;
8824 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8825 isds_log_message(context, _("Missing CreateMessageResponse element"));
8826 err = IE_ISDS;
8827 goto leave;
8829 if (result->nodesetval->nodeNr > 1) {
8830 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8831 err = IE_ISDS;
8832 goto leave;
8834 xpath_ctx->node = result->nodesetval->nodeTab[0];
8835 xmlXPathFreeObject(result); result = NULL;
8837 if (outgoing_message->envelope->dmID) {
8838 free(outgoing_message->envelope->dmID);
8839 outgoing_message->envelope->dmID = NULL;
8841 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8842 if (!outgoing_message->envelope->dmID) {
8843 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8844 "but did not return assigned message ID\n"));
8847 leave:
8848 /* TODO: Serialize message into structure member raw */
8849 /* XXX: Each web service transport message in different format.
8850 * Therefore it's not possible to save them directly.
8851 * To save them, one must figure out common format.
8852 * We can leave it on application, or we can implement the ESS format. */
8853 /*if (message_is_complete) {
8854 if (outgoing_message->envelope->dmID) {
8856 /* Add assigned message ID as first child*/
8857 /*xmlNodePtr dmid_text = xmlNewText(
8858 (xmlChar *) outgoing_message->envelope->dmID);
8859 if (!dmid_text) goto serialization_failed;
8861 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8862 BAD_CAST "dmID");
8863 if (!dmid_element) {
8864 xmlFreeNode(dmid_text);
8865 goto serialization_failed;
8868 xmlNodePtr dmid_element_with_text =
8869 xmlAddChild(dmid_element, dmid_text);
8870 if (!dmid_element_with_text) {
8871 xmlFreeNode(dmid_element);
8872 xmlFreeNode(dmid_text);
8873 goto serialization_failed;
8876 node = xmlAddPrevSibling(envelope->childern,
8877 dmid_element_with_text);
8878 if (!node) {
8879 xmlFreeNodeList(dmid_element_with_text);
8880 goto serialization_failed;
8884 /* Serialize message with ID into raw */
8885 /*buffer = serialize_element(envelope)*/
8886 /* }
8888 serialization_failed:
8892 /* Clean up */
8893 xmlXPathFreeObject(result);
8894 xmlXPathFreeContext(xpath_ctx);
8896 free(code);
8897 free(message);
8898 xmlFreeDoc(response);
8899 xmlFreeNode(request);
8901 if (!err)
8902 isds_log(ILF_ISDS, ILL_DEBUG,
8903 _("CreateMessage request processed by server "
8904 "successfully.\n"));
8905 #else /* not HAVE_LIBCURL */
8906 err = IE_NOTSUP;
8907 #endif
8909 return err;
8913 /* Send a message via ISDS to a multiple recipients
8914 * @context is session context
8915 * @outgoing_message is message to send; Some members are mandatory,
8916 * some are optional and some are irrelevant (especially data
8917 * about sender). Data about recipient will be substituted by ISDS from
8918 * @copies. Included pointer to isds_list documents must
8919 * contain at least one document of FILEMETATYPE_MAIN.
8920 * @copies is list of isds_message_copy structures addressing all desired
8921 * recipients. This is read-write structure, some members will be filled with
8922 * valid data from ISDS (message IDs, error codes, error descriptions).
8923 * @return
8924 * ISDS_SUCCESS if all messages have been sent
8925 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8926 * succeeded messages can be identified by copies->data->error),
8927 * or other error code if something other goes wrong. */
8928 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8929 const struct isds_message *outgoing_message,
8930 struct isds_list *copies) {
8932 isds_error err = IE_SUCCESS;
8933 #if HAVE_LIBCURL
8934 isds_error append_err;
8935 xmlNsPtr isds_ns = NULL;
8936 xmlNodePtr request = NULL, recipients, recipient, node;
8937 struct isds_list *item;
8938 struct isds_message_copy *copy;
8939 xmlDocPtr response = NULL;
8940 xmlChar *code = NULL, *message = NULL;
8941 xmlXPathContextPtr xpath_ctx = NULL;
8942 xmlXPathObjectPtr result = NULL;
8943 xmlChar *string = NULL;
8944 int i;
8945 #endif
8947 if (!context) return IE_INVALID_CONTEXT;
8948 zfree(context->long_message);
8949 if (!outgoing_message || !copies) return IE_INVAL;
8951 #if HAVE_LIBCURL
8952 /* Check if connection is established
8953 * TODO: This check should be done downstairs. */
8954 if (!context->curl) return IE_CONNECTION_CLOSED;
8957 /* Build CreateMultipleMessage request */
8958 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
8959 if (!request) {
8960 isds_log_message(context,
8961 _("Could not build CreateMultipleMessage request"));
8962 return IE_ERROR;
8964 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8965 if(!isds_ns) {
8966 isds_log_message(context, _("Could not create ISDS name space"));
8967 xmlFreeNode(request);
8968 return IE_ERROR;
8970 xmlSetNs(request, isds_ns);
8973 /* Build recipients */
8974 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8975 if (!recipients) {
8976 isds_log_message(context, _("Could not add dmRecipients child to "
8977 "CreateMultipleMessage element"));
8978 xmlFreeNode(request);
8979 return IE_ERROR;
8982 /* Insert each recipient */
8983 for (item = copies; item; item = item->next) {
8984 copy = (struct isds_message_copy *) item->data;
8985 if (!copy) {
8986 isds_log_message(context,
8987 _("`copies' list item contains empty data"));
8988 err = IE_INVAL;
8989 goto leave;
8992 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
8993 if (!recipient) {
8994 isds_log_message(context, _("Could not add dmRecipient child to "
8995 "dmRecipients element"));
8996 err = IE_ERROR;
8997 goto leave;
9000 if (!copy->dbIDRecipient) {
9001 isds_log_message(context,
9002 _("Message copy is missing recipient box identifier"));
9003 err = IE_INVAL;
9004 goto leave;
9006 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
9007 INSERT_STRING(recipient, "dmRecipientOrgUnit",
9008 copy->dmRecipientOrgUnit);
9009 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
9010 copy->dmRecipientOrgUnitNum, string);
9011 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
9014 /* Append envelope and files */
9015 err = insert_envelope_files(context, outgoing_message, request, 0);
9016 if (err) goto leave;
9019 isds_log(ILF_ISDS, ILL_DEBUG,
9020 _("Sending CreateMultipleMessage request to ISDS\n"));
9022 /* Sent request */
9023 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9024 if (err) {
9025 isds_log(ILF_ISDS, ILL_DEBUG,
9026 _("Processing ISDS response on CreateMultipleMessage "
9027 "request failed\n"));
9028 goto leave;
9031 /* Check for response status */
9032 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9033 &code, &message, NULL);
9034 if (err) {
9035 isds_log(ILF_ISDS, ILL_DEBUG,
9036 _("ISDS response on CreateMultipleMessage request "
9037 "is missing status\n"));
9038 goto leave;
9041 /* Request processed, but some copies failed */
9042 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9043 char *box_id_locale =
9044 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9045 char *code_locale = _isds_utf82locale((char*)code);
9046 char *message_locale = _isds_utf82locale((char*)message);
9047 isds_log(ILF_ISDS, ILL_DEBUG,
9048 _("Server did accept message for multiple recipients "
9049 "on CreateMultipleMessage request but delivery to "
9050 "some of them failed (code=%s, message=%s)\n"),
9051 box_id_locale, code_locale, message_locale);
9052 isds_log_message(context, message_locale);
9053 free(box_id_locale);
9054 free(code_locale);
9055 free(message_locale);
9056 err = IE_PARTIAL_SUCCESS;
9059 /* Request refused by server as whole */
9060 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9061 char *box_id_locale =
9062 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9063 char *code_locale = _isds_utf82locale((char*)code);
9064 char *message_locale = _isds_utf82locale((char*)message);
9065 isds_log(ILF_ISDS, ILL_DEBUG,
9066 _("Server did not accept message for multiple recipients "
9067 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9068 box_id_locale, code_locale, message_locale);
9069 isds_log_message(context, message_locale);
9070 free(box_id_locale);
9071 free(code_locale);
9072 free(message_locale);
9073 err = IE_ISDS;
9074 goto leave;
9078 /* Extract data */
9079 xpath_ctx = xmlXPathNewContext(response);
9080 if (!xpath_ctx) {
9081 err = IE_ERROR;
9082 goto leave;
9084 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9085 err = IE_ERROR;
9086 goto leave;
9088 result = xmlXPathEvalExpression(
9089 BAD_CAST "/isds:CreateMultipleMessageResponse"
9090 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9091 xpath_ctx);
9092 if (!result) {
9093 err = IE_ERROR;
9094 goto leave;
9096 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9097 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9098 err = IE_ISDS;
9099 goto leave;
9102 /* Extract message ID and delivery status for each copy */
9103 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9104 item = item->next, i++) {
9105 copy = (struct isds_message_copy *) item->data;
9106 xpath_ctx->node = result->nodesetval->nodeTab[i];
9108 append_err = append_TMStatus(context, copy, xpath_ctx);
9109 if (append_err) {
9110 err = append_err;
9111 goto leave;
9114 if (item || i < result->nodesetval->nodeNr) {
9115 isds_printf_message(context, _("ISDS returned unexpected number of "
9116 "message copy delivery states: %d"),
9117 result->nodesetval->nodeNr);
9118 err = IE_ISDS;
9119 goto leave;
9123 leave:
9124 /* Clean up */
9125 free(string);
9126 xmlXPathFreeObject(result);
9127 xmlXPathFreeContext(xpath_ctx);
9129 free(code);
9130 free(message);
9131 xmlFreeDoc(response);
9132 xmlFreeNode(request);
9134 if (!err)
9135 isds_log(ILF_ISDS, ILL_DEBUG,
9136 _("CreateMultipleMessageResponse request processed by server "
9137 "successfully.\n"));
9138 #else /* not HAVE_LIBCURL */
9139 err = IE_NOTSUP;
9140 #endif
9142 return err;
9146 /* Get list of messages. This is common core for getting sent or received
9147 * messages.
9148 * Any criterion argument can be NULL, if you don't care about it.
9149 * @context is session context. Must not be NULL.
9150 * @outgoing_direction is true if you want list of outgoing messages,
9151 * it's false if you want incoming messages.
9152 * @from_time is minimal time and date of message sending inclusive.
9153 * @to_time is maximal time and date of message sending inclusive
9154 * @organization_unit_number is number of sender/recipient respectively.
9155 * @status_filter is bit field of isds_message_status values. Use special
9156 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9157 * all values, you can use bit-wise arithmetic if you want.)
9158 * @offset is index of first message we are interested in. First message is 1.
9159 * Set to 0 (or 1) if you don't care.
9160 * @number is maximal length of list you want to get as input value, outputs
9161 * number of messages matching these criteria. Can be NULL if you don't care
9162 * (applies to output value either).
9163 * @messages is automatically reallocated list of isds_message's. Be ware that
9164 * it returns only brief overview (envelope and some other fields) about each
9165 * message, not the complete message. FIXME: Specify exact fields.
9166 * The list is sorted by delivery time in ascending order.
9167 * Use NULL if you don't care about don't need the data (useful if you want to
9168 * know only the @number). If you provide &NULL, list will be allocated on
9169 * heap, if you provide pointer to non-NULL, list will be freed automatically
9170 * at first. Also in case of error the list will be NULLed.
9171 * @return IE_SUCCESS or appropriate error code. */
9172 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9173 _Bool outgoing_direction,
9174 const struct timeval *from_time, const struct timeval *to_time,
9175 const long int *organization_unit_number,
9176 const unsigned int status_filter,
9177 const unsigned long int offset, unsigned long int *number,
9178 struct isds_list **messages) {
9180 isds_error err = IE_SUCCESS;
9181 #if HAVE_LIBCURL
9182 xmlNsPtr isds_ns = NULL;
9183 xmlNodePtr request = NULL, node;
9184 xmlDocPtr response = NULL;
9185 xmlChar *code = NULL, *message = NULL;
9186 xmlXPathContextPtr xpath_ctx = NULL;
9187 xmlXPathObjectPtr result = NULL;
9188 xmlChar *string = NULL;
9189 int count = 0;
9190 #endif
9192 if (!context) return IE_INVALID_CONTEXT;
9193 zfree(context->long_message);
9195 /* Free former message list if any */
9196 if (messages) isds_list_free(messages);
9198 #if HAVE_LIBCURL
9199 /* Check if connection is established
9200 * TODO: This check should be done downstairs. */
9201 if (!context->curl) return IE_CONNECTION_CLOSED;
9203 /* Build GetListOf*Messages request */
9204 request = xmlNewNode(NULL,
9205 (outgoing_direction) ?
9206 BAD_CAST "GetListOfSentMessages" :
9207 BAD_CAST "GetListOfReceivedMessages"
9209 if (!request) {
9210 isds_log_message(context,
9211 (outgoing_direction) ?
9212 _("Could not build GetListOfSentMessages request") :
9213 _("Could not build GetListOfReceivedMessages request")
9215 return IE_ERROR;
9217 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9218 if(!isds_ns) {
9219 isds_log_message(context, _("Could not create ISDS name space"));
9220 xmlFreeNode(request);
9221 return IE_ERROR;
9223 xmlSetNs(request, isds_ns);
9226 if (from_time) {
9227 err = timeval2timestring(from_time, &string);
9228 if (err) goto leave;
9230 INSERT_STRING(request, "dmFromTime", string);
9231 free(string); string = NULL;
9233 if (to_time) {
9234 err = timeval2timestring(to_time, &string);
9235 if (err) goto leave;
9237 INSERT_STRING(request, "dmToTime", string);
9238 free(string); string = NULL;
9240 if (outgoing_direction) {
9241 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9242 organization_unit_number, string);
9243 } else {
9244 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9245 organization_unit_number, string);
9248 if (status_filter > MESSAGESTATE_ANY) {
9249 isds_printf_message(context,
9250 _("Invalid message state filter value: %ld"), status_filter);
9251 err = IE_INVAL;
9252 goto leave;
9254 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9256 if (offset > 0 ) {
9257 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9258 } else {
9259 INSERT_STRING(request, "dmOffset", "1");
9262 /* number 0 means no limit */
9263 if (number && *number == 0) {
9264 INSERT_STRING(request, "dmLimit", NULL);
9265 } else {
9266 INSERT_ULONGINT(request, "dmLimit", number, string);
9270 isds_log(ILF_ISDS, ILL_DEBUG,
9271 (outgoing_direction) ?
9272 _("Sending GetListOfSentMessages request to ISDS\n") :
9273 _("Sending GetListOfReceivedMessages request to ISDS\n")
9276 /* Sent request */
9277 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9278 xmlFreeNode(request); request = NULL;
9280 if (err) {
9281 isds_log(ILF_ISDS, ILL_DEBUG,
9282 (outgoing_direction) ?
9283 _("Processing ISDS response on GetListOfSentMessages "
9284 "request failed\n") :
9285 _("Processing ISDS response on GetListOfReceivedMessages "
9286 "request failed\n")
9288 goto leave;
9291 /* Check for response status */
9292 err = isds_response_status(context, SERVICE_DM_INFO, response,
9293 &code, &message, NULL);
9294 if (err) {
9295 isds_log(ILF_ISDS, ILL_DEBUG,
9296 (outgoing_direction) ?
9297 _("ISDS response on GetListOfSentMessages request "
9298 "is missing status\n") :
9299 _("ISDS response on GetListOfReceivedMessages request "
9300 "is missing status\n")
9302 goto leave;
9305 /* Request processed, but nothing found */
9306 if (xmlStrcmp(code, BAD_CAST "0000")) {
9307 char *code_locale = _isds_utf82locale((char*)code);
9308 char *message_locale = _isds_utf82locale((char*)message);
9309 isds_log(ILF_ISDS, ILL_DEBUG,
9310 (outgoing_direction) ?
9311 _("Server refused GetListOfSentMessages request "
9312 "(code=%s, message=%s)\n") :
9313 _("Server refused GetListOfReceivedMessages request "
9314 "(code=%s, message=%s)\n"),
9315 code_locale, message_locale);
9316 isds_log_message(context, message_locale);
9317 free(code_locale);
9318 free(message_locale);
9319 err = IE_ISDS;
9320 goto leave;
9324 /* Extract data */
9325 xpath_ctx = xmlXPathNewContext(response);
9326 if (!xpath_ctx) {
9327 err = IE_ERROR;
9328 goto leave;
9330 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9331 err = IE_ERROR;
9332 goto leave;
9334 result = xmlXPathEvalExpression(
9335 (outgoing_direction) ?
9336 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9337 "isds:dmRecords/isds:dmRecord" :
9338 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9339 "isds:dmRecords/isds:dmRecord",
9340 xpath_ctx);
9341 if (!result) {
9342 err = IE_ERROR;
9343 goto leave;
9346 /* Fill output arguments in */
9347 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9348 struct isds_envelope *envelope;
9349 struct isds_list *item = NULL, *last_item = NULL;
9351 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9352 /* Create new message */
9353 item = calloc(1, sizeof(*item));
9354 if (!item) {
9355 err = IE_NOMEM;
9356 goto leave;
9358 item->destructor = (void(*)(void**)) &isds_message_free;
9359 item->data = calloc(1, sizeof(struct isds_message));
9360 if (!item->data) {
9361 isds_list_free(&item);
9362 err = IE_NOMEM;
9363 goto leave;
9366 /* Extract envelope data */
9367 xpath_ctx->node = result->nodesetval->nodeTab[count];
9368 envelope = NULL;
9369 err = extract_DmRecord(context, &envelope, xpath_ctx);
9370 if (err) {
9371 isds_list_free(&item);
9372 goto leave;
9375 /* Attach extracted envelope */
9376 ((struct isds_message *) item->data)->envelope = envelope;
9378 /* Append new message into the list */
9379 if (!*messages) {
9380 *messages = last_item = item;
9381 } else {
9382 last_item->next = item;
9383 last_item = item;
9387 if (number) *number = count;
9389 leave:
9390 if (err) {
9391 isds_list_free(messages);
9394 free(string);
9395 xmlXPathFreeObject(result);
9396 xmlXPathFreeContext(xpath_ctx);
9398 free(code);
9399 free(message);
9400 xmlFreeDoc(response);
9401 xmlFreeNode(request);
9403 if (!err)
9404 isds_log(ILF_ISDS, ILL_DEBUG,
9405 (outgoing_direction) ?
9406 _("GetListOfSentMessages request processed by server "
9407 "successfully.\n") :
9408 _("GetListOfReceivedMessages request processed by server "
9409 "successfully.\n")
9411 #else /* not HAVE_LIBCURL */
9412 err = IE_NOTSUP;
9413 #endif
9414 return err;
9418 /* Get list of outgoing (already sent) messages.
9419 * Any criterion argument can be NULL, if you don't care about it.
9420 * @context is session context. Must not be NULL.
9421 * @from_time is minimal time and date of message sending inclusive.
9422 * @to_time is maximal time and date of message sending inclusive
9423 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9424 * @status_filter is bit field of isds_message_status values. Use special
9425 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9426 * all values, you can use bit-wise arithmetic if you want.)
9427 * @offset is index of first message we are interested in. First message is 1.
9428 * Set to 0 (or 1) if you don't care.
9429 * @number is maximal length of list you want to get as input value, outputs
9430 * number of messages matching these criteria. Can be NULL if you don't care
9431 * (applies to output value either).
9432 * @messages is automatically reallocated list of isds_message's. Be ware that
9433 * it returns only brief overview (envelope and some other fields) about each
9434 * message, not the complete message. FIXME: Specify exact fields.
9435 * The list is sorted by delivery time in ascending order.
9436 * Use NULL if you don't care about the meta data (useful if you want to know
9437 * only the @number). If you provide &NULL, list will be allocated on heap,
9438 * if you provide pointer to non-NULL, list will be freed automatically at
9439 * first. Also in case of error the list will be NULLed.
9440 * @return IE_SUCCESS or appropriate error code. */
9441 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9442 const struct timeval *from_time, const struct timeval *to_time,
9443 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9444 const unsigned long int offset, unsigned long int *number,
9445 struct isds_list **messages) {
9447 return isds_get_list_of_messages(
9448 context, 1,
9449 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9450 offset, number,
9451 messages);
9455 /* Get list of incoming (addressed to you) messages.
9456 * Any criterion argument can be NULL, if you don't care about it.
9457 * @context is session context. Must not be NULL.
9458 * @from_time is minimal time and date of message sending inclusive.
9459 * @to_time is maximal time and date of message sending inclusive
9460 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9461 * @status_filter is bit field of isds_message_status values. Use special
9462 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9463 * all values, you can use bit-wise arithmetic if you want.)
9464 * @offset is index of first message we are interested in. First message is 1.
9465 * Set to 0 (or 1) if you don't care.
9466 * @number is maximal length of list you want to get as input value, outputs
9467 * number of messages matching these criteria. Can be NULL if you don't care
9468 * (applies to output value either).
9469 * @messages is automatically reallocated list of isds_message's. Be ware that
9470 * it returns only brief overview (envelope and some other fields) about each
9471 * message, not the complete message. FIXME: Specify exact fields.
9472 * Use NULL if you don't care about the meta data (useful if you want to know
9473 * only the @number). If you provide &NULL, list will be allocated on heap,
9474 * if you provide pointer to non-NULL, list will be freed automatically at
9475 * first. Also in case of error the list will be NULLed.
9476 * @return IE_SUCCESS or appropriate error code. */
9477 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9478 const struct timeval *from_time, const struct timeval *to_time,
9479 const long int *dmRecipientOrgUnitNum,
9480 const unsigned int status_filter,
9481 const unsigned long int offset, unsigned long int *number,
9482 struct isds_list **messages) {
9484 return isds_get_list_of_messages(
9485 context, 0,
9486 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9487 offset, number,
9488 messages);
9492 /* Get list of sent message state changes.
9493 * Any criterion argument can be NULL, if you don't care about it.
9494 * @context is session context. Must not be NULL.
9495 * @from_time is minimal time and date of status changes inclusive
9496 * @to_time is maximal time and date of status changes inclusive
9497 * @changed_states is automatically reallocated list of
9498 * isds_message_status_change's. If you provide &NULL, list will be allocated
9499 * on heap, if you provide pointer to non-NULL, list will be freed
9500 * automatically at first. Also in case of error the list will be NULLed.
9501 * XXX: The list item ordering is not specified.
9502 * XXX: Server provides only `recent' changes.
9503 * @return IE_SUCCESS or appropriate error code. */
9504 isds_error isds_get_list_of_sent_message_state_changes(
9505 struct isds_ctx *context,
9506 const struct timeval *from_time, const struct timeval *to_time,
9507 struct isds_list **changed_states) {
9509 isds_error err = IE_SUCCESS;
9510 #if HAVE_LIBCURL
9511 xmlNsPtr isds_ns = NULL;
9512 xmlNodePtr request = NULL, node;
9513 xmlDocPtr response = NULL;
9514 xmlXPathContextPtr xpath_ctx = NULL;
9515 xmlXPathObjectPtr result = NULL;
9516 xmlChar *string = NULL;
9517 int count = 0;
9518 #endif
9520 if (!context) return IE_INVALID_CONTEXT;
9521 zfree(context->long_message);
9523 /* Free former message list if any */
9524 isds_list_free(changed_states);
9526 #if HAVE_LIBCURL
9527 /* Check if connection is established
9528 * TODO: This check should be done downstairs. */
9529 if (!context->curl) return IE_CONNECTION_CLOSED;
9531 /* Build GetMessageStateChanges request */
9532 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
9533 if (!request) {
9534 isds_log_message(context,
9535 _("Could not build GetMessageStateChanges request"));
9536 return IE_ERROR;
9538 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9539 if(!isds_ns) {
9540 isds_log_message(context, _("Could not create ISDS name space"));
9541 xmlFreeNode(request);
9542 return IE_ERROR;
9544 xmlSetNs(request, isds_ns);
9547 if (from_time) {
9548 err = timeval2timestring(from_time, &string);
9549 if (err) goto leave;
9551 INSERT_STRING(request, "dmFromTime", string);
9552 zfree(string);
9554 if (to_time) {
9555 err = timeval2timestring(to_time, &string);
9556 if (err) goto leave;
9558 INSERT_STRING(request, "dmToTime", string);
9559 zfree(string);
9562 /* Sent request */
9563 err = send_destroy_request_check_response(context,
9564 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9565 &response, NULL, NULL);
9566 if (err) goto leave;
9569 /* Extract data */
9570 xpath_ctx = xmlXPathNewContext(response);
9571 if (!xpath_ctx) {
9572 err = IE_ERROR;
9573 goto leave;
9575 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9576 err = IE_ERROR;
9577 goto leave;
9579 result = xmlXPathEvalExpression(
9580 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9581 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9582 if (!result) {
9583 err = IE_ERROR;
9584 goto leave;
9587 /* Fill output arguments in */
9588 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9589 struct isds_list *item = NULL, *last_item = NULL;
9591 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9592 /* Create new status change */
9593 item = calloc(1, sizeof(*item));
9594 if (!item) {
9595 err = IE_NOMEM;
9596 goto leave;
9598 item->destructor =
9599 (void(*)(void**)) &isds_message_status_change_free;
9601 /* Extract message status change */
9602 xpath_ctx->node = result->nodesetval->nodeTab[count];
9603 err = extract_StateChangesRecord(context,
9604 (struct isds_message_status_change **) &item->data,
9605 xpath_ctx);
9606 if (err) {
9607 isds_list_free(&item);
9608 goto leave;
9611 /* Append new message status change into the list */
9612 if (!*changed_states) {
9613 *changed_states = last_item = item;
9614 } else {
9615 last_item->next = item;
9616 last_item = item;
9621 leave:
9622 if (err) {
9623 isds_list_free(changed_states);
9626 free(string);
9627 xmlXPathFreeObject(result);
9628 xmlXPathFreeContext(xpath_ctx);
9629 xmlFreeDoc(response);
9630 xmlFreeNode(request);
9632 if (!err)
9633 isds_log(ILF_ISDS, ILL_DEBUG,
9634 _("GetMessageStateChanges request processed by server "
9635 "successfully.\n"));
9636 #else /* not HAVE_LIBCURL */
9637 err = IE_NOTSUP;
9638 #endif
9639 return err;
9643 #if HAVE_LIBCURL
9644 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9645 * code
9646 * @context is session context
9647 * @service is ISDS WS service handler
9648 * @service_name is name of SERVICE_DM_OPERATIONS
9649 * @message_id is message ID to send as service argument to ISDS
9650 * @response is reallocated server SOAP body response as XML document
9651 * @raw_response is reallocated bit stream with response body. Use
9652 * NULL if you don't care
9653 * @raw_response_length is size of @raw_response in bytes
9654 * @code is reallocated ISDS status code
9655 * @status_message is reallocated ISDS status message
9656 * @return error coded from lower layer, context message will be set up
9657 * appropriately. */
9658 static isds_error build_send_check_message_request(struct isds_ctx *context,
9659 const isds_service service, const xmlChar *service_name,
9660 const char *message_id,
9661 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9662 xmlChar **code, xmlChar **status_message) {
9664 isds_error err = IE_SUCCESS;
9665 char *service_name_locale = NULL, *message_id_locale = NULL;
9666 xmlNodePtr request = NULL, node;
9667 xmlNsPtr isds_ns = NULL;
9669 if (!context) return IE_INVALID_CONTEXT;
9670 if (!service_name || !message_id) return IE_INVAL;
9671 if (!response || !code || !status_message) return IE_INVAL;
9672 if (!raw_response_length && raw_response) return IE_INVAL;
9674 /* Free output argument */
9675 xmlFreeDoc(*response); *response = NULL;
9676 if (raw_response) zfree(*raw_response);
9677 zfree(*code);
9678 zfree(*status_message);
9681 /* Check if connection is established
9682 * TODO: This check should be done downstairs. */
9683 if (!context->curl) return IE_CONNECTION_CLOSED;
9685 service_name_locale = _isds_utf82locale((char*)service_name);
9686 message_id_locale = _isds_utf82locale(message_id);
9687 if (!service_name_locale || !message_id_locale) {
9688 err = IE_NOMEM;
9689 goto leave;
9692 /* Build request */
9693 request = xmlNewNode(NULL, service_name);
9694 if (!request) {
9695 isds_printf_message(context,
9696 _("Could not build %s request for %s message ID"),
9697 service_name_locale, message_id_locale);
9698 err = IE_ERROR;
9699 goto leave;
9701 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9702 if(!isds_ns) {
9703 isds_log_message(context, _("Could not create ISDS name space"));
9704 err = IE_ERROR;
9705 goto leave;
9707 xmlSetNs(request, isds_ns);
9710 /* Add requested ID */
9711 err = validate_message_id_length(context, (xmlChar *) message_id);
9712 if (err) goto leave;
9713 INSERT_STRING(request, "dmID", message_id);
9716 isds_log(ILF_ISDS, ILL_DEBUG,
9717 _("Sending %s request for %s message ID to ISDS\n"),
9718 service_name_locale, message_id_locale);
9720 /* Send request */
9721 err = _isds(context, service, request, response,
9722 raw_response, raw_response_length);
9723 xmlFreeNode(request); request = NULL;
9725 if (err) {
9726 isds_log(ILF_ISDS, ILL_DEBUG,
9727 _("Processing ISDS response on %s request failed\n"),
9728 service_name_locale);
9729 goto leave;
9732 /* Check for response status */
9733 err = isds_response_status(context, service, *response,
9734 code, status_message, NULL);
9735 if (err) {
9736 isds_log(ILF_ISDS, ILL_DEBUG,
9737 _("ISDS response on %s request is missing status\n"),
9738 service_name_locale);
9739 goto leave;
9742 /* Request processed, but nothing found */
9743 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9744 char *code_locale = _isds_utf82locale((char*) *code);
9745 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9746 isds_log(ILF_ISDS, ILL_DEBUG,
9747 _("Server refused %s request for %s message ID "
9748 "(code=%s, message=%s)\n"),
9749 service_name_locale, message_id_locale,
9750 code_locale, status_message_locale);
9751 isds_log_message(context, status_message_locale);
9752 free(code_locale);
9753 free(status_message_locale);
9754 err = IE_ISDS;
9755 goto leave;
9758 leave:
9759 free(message_id_locale);
9760 free(service_name_locale);
9761 xmlFreeNode(request);
9762 return err;
9766 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9767 * signed data and free ISDS response.
9768 * @context is session context
9769 * @message_id is UTF-8 encoded message ID for logging purpose
9770 * @response is parsed XML document. It will be freed and NULLed in the middle
9771 * of function run to save memory. This is not guaranteed in case of error.
9772 * @request_name is name of ISDS request used to construct response root
9773 * element name and for logging purpose.
9774 * @raw is reallocated output buffer with DER encoded CMS data
9775 * @raw_length is size of @raw buffer in bytes
9776 * @returns standard error codes, in case of error, @raw will be freed and
9777 * NULLed, @response sometimes. */
9778 static isds_error find_extract_signed_data_free_response(
9779 struct isds_ctx *context, const xmlChar *message_id,
9780 xmlDocPtr *response, const xmlChar *request_name,
9781 void **raw, size_t *raw_length) {
9783 isds_error err = IE_SUCCESS;
9784 char *xpath_expression = NULL;
9785 xmlXPathContextPtr xpath_ctx = NULL;
9786 xmlXPathObjectPtr result = NULL;
9787 char *encoded_structure = NULL;
9789 if (!context) return IE_INVALID_CONTEXT;
9790 if (!raw) return IE_INVAL;
9791 zfree(*raw);
9792 if (!message_id || !response || !*response || !request_name || !raw_length)
9793 return IE_INVAL;
9795 /* Build XPath expression */
9796 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9797 "Response/isds:dmSignature");
9798 if (!xpath_expression) return IE_NOMEM;
9800 /* Extract data */
9801 xpath_ctx = xmlXPathNewContext(*response);
9802 if (!xpath_ctx) {
9803 err = IE_ERROR;
9804 goto leave;
9806 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9807 err = IE_ERROR;
9808 goto leave;
9810 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9811 if (!result) {
9812 err = IE_ERROR;
9813 goto leave;
9815 /* Empty response */
9816 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9817 char *message_id_locale = _isds_utf82locale((char*) message_id);
9818 isds_printf_message(context,
9819 _("Server did not return any signed data for message ID `%s' "
9820 "on %s request"),
9821 message_id_locale, request_name);
9822 free(message_id_locale);
9823 err = IE_ISDS;
9824 goto leave;
9826 /* More responses */
9827 if (result->nodesetval->nodeNr > 1) {
9828 char *message_id_locale = _isds_utf82locale((char*) message_id);
9829 isds_printf_message(context,
9830 _("Server did return more signed data for message ID `%s' "
9831 "on %s request"),
9832 message_id_locale, request_name);
9833 free(message_id_locale);
9834 err = IE_ISDS;
9835 goto leave;
9837 /* One response */
9838 xpath_ctx->node = result->nodesetval->nodeTab[0];
9840 /* Extract PKCS#7 structure */
9841 EXTRACT_STRING(".", encoded_structure);
9842 if (!encoded_structure) {
9843 isds_log_message(context, _("dmSignature element is empty"));
9846 /* Here we have delivery info as standalone CMS in encoded_structure.
9847 * We don't need any other data, free them: */
9848 xmlXPathFreeObject(result); result = NULL;
9849 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9850 xmlFreeDoc(*response); *response = NULL;
9853 /* Decode PKCS#7 to DER format */
9854 *raw_length = _isds_b64decode(encoded_structure, raw);
9855 if (*raw_length == (size_t) -1) {
9856 isds_log_message(context,
9857 _("Error while Base64-decoding PKCS#7 structure"));
9858 err = IE_ERROR;
9859 goto leave;
9862 leave:
9863 if (err) {
9864 zfree(*raw);
9865 raw_length = 0;
9868 free(encoded_structure);
9869 xmlXPathFreeObject(result);
9870 xmlXPathFreeContext(xpath_ctx);
9871 free(xpath_expression);
9873 return err;
9875 #endif /* HAVE_LIBCURL */
9878 /* Download incoming message envelope identified by ID.
9879 * @context is session context
9880 * @message_id is message identifier (you can get them from
9881 * isds_get_list_of_received_messages())
9882 * @message is automatically reallocated message retrieved from ISDS.
9883 * It will miss documents per se. Use isds_get_received_message(), if you are
9884 * interested in documents (content) too.
9885 * Returned hash and timestamp require documents to be verifiable. */
9886 isds_error isds_get_received_envelope(struct isds_ctx *context,
9887 const char *message_id, struct isds_message **message) {
9889 isds_error err = IE_SUCCESS;
9890 #if HAVE_LIBCURL
9891 xmlDocPtr response = NULL;
9892 xmlChar *code = NULL, *status_message = NULL;
9893 xmlXPathContextPtr xpath_ctx = NULL;
9894 xmlXPathObjectPtr result = NULL;
9895 #endif
9897 if (!context) return IE_INVALID_CONTEXT;
9898 zfree(context->long_message);
9900 /* Free former message if any */
9901 if (!message) return IE_INVAL;
9902 isds_message_free(message);
9904 #if HAVE_LIBCURL
9905 /* Do request and check for success */
9906 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9907 BAD_CAST "MessageEnvelopeDownload", message_id,
9908 &response, NULL, NULL, &code, &status_message);
9909 if (err) goto leave;
9911 /* Extract data */
9912 xpath_ctx = xmlXPathNewContext(response);
9913 if (!xpath_ctx) {
9914 err = IE_ERROR;
9915 goto leave;
9917 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9918 err = IE_ERROR;
9919 goto leave;
9921 result = xmlXPathEvalExpression(
9922 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9923 "isds:dmReturnedMessageEnvelope",
9924 xpath_ctx);
9925 if (!result) {
9926 err = IE_ERROR;
9927 goto leave;
9929 /* Empty response */
9930 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9931 char *message_id_locale = _isds_utf82locale((char*) message_id);
9932 isds_printf_message(context,
9933 _("Server did not return any envelope for ID `%s' "
9934 "on MessageEnvelopeDownload request"), message_id_locale);
9935 free(message_id_locale);
9936 err = IE_ISDS;
9937 goto leave;
9939 /* More envelops */
9940 if (result->nodesetval->nodeNr > 1) {
9941 char *message_id_locale = _isds_utf82locale((char*) message_id);
9942 isds_printf_message(context,
9943 _("Server did return more envelopes for ID `%s' "
9944 "on MessageEnvelopeDownload request"), message_id_locale);
9945 free(message_id_locale);
9946 err = IE_ISDS;
9947 goto leave;
9949 /* One message */
9950 xpath_ctx->node = result->nodesetval->nodeTab[0];
9952 /* Extract the envelope (= message without documents, hence 0) */
9953 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9954 if (err) goto leave;
9956 /* Save XML blob */
9957 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9958 &(*message)->raw_length);
9960 leave:
9961 if (err) {
9962 isds_message_free(message);
9965 xmlXPathFreeObject(result);
9966 xmlXPathFreeContext(xpath_ctx);
9968 free(code);
9969 free(status_message);
9970 if (!*message || !(*message)->xml) {
9971 xmlFreeDoc(response);
9974 if (!err)
9975 isds_log(ILF_ISDS, ILL_DEBUG,
9976 _("MessageEnvelopeDownload request processed by server "
9977 "successfully.\n")
9979 #else /* not HAVE_LIBCURL */
9980 err = IE_NOTSUP;
9981 #endif
9982 return err;
9986 /* Load delivery info of any format from buffer.
9987 * @context is session context
9988 * @raw_type advertises format of @buffer content. Only delivery info types
9989 * are accepted.
9990 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9991 * retrieve such data from message->raw after calling
9992 * isds_get_signed_delivery_info().
9993 * @length is length of buffer in bytes.
9994 * @message is automatically reallocated message parsed from @buffer.
9995 * @strategy selects how buffer will be attached into raw isds_message member.
9996 * */
9997 isds_error isds_load_delivery_info(struct isds_ctx *context,
9998 const isds_raw_type raw_type,
9999 const void *buffer, const size_t length,
10000 struct isds_message **message, const isds_buffer_strategy strategy) {
10002 isds_error err = IE_SUCCESS;
10003 message_ns_type message_ns;
10004 xmlDocPtr message_doc = NULL;
10005 xmlXPathContextPtr xpath_ctx = NULL;
10006 xmlXPathObjectPtr result = NULL;
10007 void *xml_stream = NULL;
10008 size_t xml_stream_length = 0;
10010 if (!context) return IE_INVALID_CONTEXT;
10011 zfree(context->long_message);
10012 if (!message) return IE_INVAL;
10013 isds_message_free(message);
10014 if (!buffer) return IE_INVAL;
10017 /* Select buffer format and extract XML from CMS*/
10018 switch (raw_type) {
10019 case RAWTYPE_DELIVERYINFO:
10020 message_ns = MESSAGE_NS_UNSIGNED;
10021 xml_stream = (void *) buffer;
10022 xml_stream_length = length;
10023 break;
10025 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
10026 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10027 xml_stream = (void *) buffer;
10028 xml_stream_length = length;
10029 break;
10031 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10032 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10033 err = _isds_extract_cms_data(context, buffer, length,
10034 &xml_stream, &xml_stream_length);
10035 if (err) goto leave;
10036 break;
10038 default:
10039 isds_log_message(context, _("Bad raw delivery representation type"));
10040 return IE_INVAL;
10041 break;
10044 isds_log(ILF_ISDS, ILL_DEBUG,
10045 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10046 xml_stream_length, xml_stream);
10048 /* Convert delivery info XML stream into XPath context */
10049 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10050 if (!message_doc) {
10051 err = IE_XML;
10052 goto leave;
10054 xpath_ctx = xmlXPathNewContext(message_doc);
10055 if (!xpath_ctx) {
10056 err = IE_ERROR;
10057 goto leave;
10059 /* XXX: Name spaces mangled for signed delivery info:
10060 * http://isds.czechpoint.cz/v20/delivery:
10062 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10063 * <q:dmDelivery>
10064 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10065 * <p:dmID>170272</p:dmID>
10066 * ...
10067 * </p:dmDm>
10068 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10069 * ...
10070 * </q:dmEvents>...</q:dmEvents>
10071 * </q:dmDelivery>
10072 * </q:GetDeliveryInfoResponse>
10073 * */
10074 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10075 err = IE_ERROR;
10076 goto leave;
10078 result = xmlXPathEvalExpression(
10079 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10080 xpath_ctx);
10081 if (!result) {
10082 err = IE_ERROR;
10083 goto leave;
10085 /* Empty delivery info */
10086 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10087 isds_printf_message(context,
10088 _("XML document is not sisds:dmDelivery document"));
10089 err = IE_ISDS;
10090 goto leave;
10092 /* More delivery info's */
10093 if (result->nodesetval->nodeNr > 1) {
10094 isds_printf_message(context,
10095 _("XML document has more sisds:dmDelivery elements"));
10096 err = IE_ISDS;
10097 goto leave;
10099 /* One delivery info */
10100 xpath_ctx->node = result->nodesetval->nodeTab[0];
10102 /* Extract the envelope (= message without documents, hence 0).
10103 * XXX: extract_TReturnedMessage() can obtain attachments size,
10104 * but delivery info carries none. It's coded as option elements,
10105 * so it should work. */
10106 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10107 if (err) goto leave;
10109 /* Extract events */
10110 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10111 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10112 if (err) { err = IE_ERROR; goto leave; }
10113 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10114 if (err) goto leave;
10116 /* Append raw CMS structure into message */
10117 (*message)->raw_type = raw_type;
10118 switch (strategy) {
10119 case BUFFER_DONT_STORE:
10120 break;
10121 case BUFFER_COPY:
10122 (*message)->raw = malloc(length);
10123 if (!(*message)->raw) {
10124 err = IE_NOMEM;
10125 goto leave;
10127 memcpy((*message)->raw, buffer, length);
10128 (*message)->raw_length = length;
10129 break;
10130 case BUFFER_MOVE:
10131 (*message)->raw = (void *) buffer;
10132 (*message)->raw_length = length;
10133 break;
10134 default:
10135 err = IE_ENUM;
10136 goto leave;
10139 leave:
10140 if (err) {
10141 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10142 isds_message_free(message);
10145 xmlXPathFreeObject(result);
10146 xmlXPathFreeContext(xpath_ctx);
10147 if (!*message || !(*message)->xml) {
10148 xmlFreeDoc(message_doc);
10150 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10152 if (!err)
10153 isds_log(ILF_ISDS, ILL_DEBUG,
10154 _("Delivery info loaded successfully.\n"));
10155 return err;
10159 /* Download signed delivery info-sheet of given message identified by ID.
10160 * @context is session context
10161 * @message_id is message identifier (you can get them from
10162 * isds_get_list_of_{sent,received}_messages())
10163 * @message is automatically reallocated message retrieved from ISDS.
10164 * It will miss documents per se. Use isds_get_signed_received_message(),
10165 * if you are interested in documents (content). OTOH, only this function
10166 * can get list events message has gone through. */
10167 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10168 const char *message_id, struct isds_message **message) {
10170 isds_error err = IE_SUCCESS;
10171 #if HAVE_LIBCURL
10172 xmlDocPtr response = NULL;
10173 xmlChar *code = NULL, *status_message = NULL;
10174 void *raw = NULL;
10175 size_t raw_length = 0;
10176 #endif
10178 if (!context) return IE_INVALID_CONTEXT;
10179 zfree(context->long_message);
10181 /* Free former message if any */
10182 if (!message) return IE_INVAL;
10183 isds_message_free(message);
10185 #if HAVE_LIBCURL
10186 /* Do request and check for success */
10187 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10188 BAD_CAST "GetSignedDeliveryInfo", message_id,
10189 &response, NULL, NULL, &code, &status_message);
10190 if (err) goto leave;
10192 /* Find signed delivery info, extract it into raw and maybe free
10193 * response */
10194 err = find_extract_signed_data_free_response(context,
10195 (xmlChar *)message_id, &response,
10196 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10197 if (err) goto leave;
10199 /* Parse delivery info */
10200 err = isds_load_delivery_info(context,
10201 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10202 message, BUFFER_MOVE);
10203 if (err) goto leave;
10205 raw = NULL;
10207 leave:
10208 if (err) {
10209 isds_message_free(message);
10212 free(raw);
10213 free(code);
10214 free(status_message);
10215 xmlFreeDoc(response);
10217 if (!err)
10218 isds_log(ILF_ISDS, ILL_DEBUG,
10219 _("GetSignedDeliveryInfo request processed by server "
10220 "successfully.\n")
10222 #else /* not HAVE_LIBCURL */
10223 err = IE_NOTSUP;
10224 #endif
10225 return err;
10229 /* Download delivery info-sheet of given message identified by ID.
10230 * @context is session context
10231 * @message_id is message identifier (you can get them from
10232 * isds_get_list_of_{sent,received}_messages())
10233 * @message is automatically reallocated message retrieved from ISDS.
10234 * It will miss documents per se. Use isds_get_received_message(), if you are
10235 * interested in documents (content). OTOH, only this function can get list
10236 * of events message has gone through. */
10237 isds_error isds_get_delivery_info(struct isds_ctx *context,
10238 const char *message_id, struct isds_message **message) {
10240 isds_error err = IE_SUCCESS;
10241 #if HAVE_LIBCURL
10242 xmlDocPtr response = NULL;
10243 xmlChar *code = NULL, *status_message = NULL;
10244 xmlNodePtr delivery_node = NULL;
10245 void *raw = NULL;
10246 size_t raw_length = 0;
10247 #endif
10249 if (!context) return IE_INVALID_CONTEXT;
10250 zfree(context->long_message);
10252 /* Free former message if any */
10253 if (!message) return IE_INVAL;
10254 isds_message_free(message);
10256 #if HAVE_LIBCURL
10257 /* Do request and check for success */
10258 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10259 BAD_CAST "GetDeliveryInfo", message_id,
10260 &response, NULL, NULL, &code, &status_message);
10261 if (err) goto leave;
10264 /* Serialize delivery info */
10265 delivery_node = xmlDocGetRootElement(response);
10266 if (!delivery_node) {
10267 char *message_id_locale = _isds_utf82locale((char*) message_id);
10268 isds_printf_message(context,
10269 _("Server did not return any delivery info for ID `%s' "
10270 "on GetDeliveryInfo request"), message_id_locale);
10271 free(message_id_locale);
10272 err = IE_ISDS;
10273 goto leave;
10275 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10276 if (err) goto leave;
10278 /* Parse delivery info */
10279 /* TODO: Here we parse the response second time. We could single delivery
10280 * parser from isds_load_delivery_info() to make things faster. */
10281 err = isds_load_delivery_info(context,
10282 RAWTYPE_DELIVERYINFO, raw, raw_length,
10283 message, BUFFER_MOVE);
10284 if (err) goto leave;
10286 raw = NULL;
10289 leave:
10290 if (err) {
10291 isds_message_free(message);
10294 free(raw);
10295 free(code);
10296 free(status_message);
10297 xmlFreeDoc(response);
10299 if (!err)
10300 isds_log(ILF_ISDS, ILL_DEBUG,
10301 _("GetDeliveryInfo request processed by server "
10302 "successfully.\n")
10304 #else /* not HAVE_LIBCURL */
10305 err = IE_NOTSUP;
10306 #endif
10307 return err;
10311 /* Download incoming message identified by ID.
10312 * @context is session context
10313 * @message_id is message identifier (you can get them from
10314 * isds_get_list_of_received_messages())
10315 * @message is automatically reallocated message retrieved from ISDS */
10316 isds_error isds_get_received_message(struct isds_ctx *context,
10317 const char *message_id, struct isds_message **message) {
10319 isds_error err = IE_SUCCESS;
10320 #if HAVE_LIBCURL
10321 xmlDocPtr response = NULL;
10322 void *xml_stream = NULL;
10323 size_t xml_stream_length;
10324 xmlChar *code = NULL, *status_message = NULL;
10325 xmlXPathContextPtr xpath_ctx = NULL;
10326 xmlXPathObjectPtr result = NULL;
10327 char *phys_path = NULL;
10328 size_t phys_start, phys_end;
10329 #endif
10331 if (!context) return IE_INVALID_CONTEXT;
10332 zfree(context->long_message);
10334 /* Free former message if any */
10335 if (NULL == message) return IE_INVAL;
10336 if (message) isds_message_free(message);
10338 #if HAVE_LIBCURL
10339 /* Do request and check for success */
10340 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10341 BAD_CAST "MessageDownload", message_id,
10342 &response, &xml_stream, &xml_stream_length,
10343 &code, &status_message);
10344 if (err) goto leave;
10346 /* Extract data */
10347 xpath_ctx = xmlXPathNewContext(response);
10348 if (!xpath_ctx) {
10349 err = IE_ERROR;
10350 goto leave;
10352 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10353 err = IE_ERROR;
10354 goto leave;
10356 result = xmlXPathEvalExpression(
10357 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10358 xpath_ctx);
10359 if (!result) {
10360 err = IE_ERROR;
10361 goto leave;
10363 /* Empty response */
10364 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10365 char *message_id_locale = _isds_utf82locale((char*) message_id);
10366 isds_printf_message(context,
10367 _("Server did not return any message for ID `%s' "
10368 "on MessageDownload request"), message_id_locale);
10369 free(message_id_locale);
10370 err = IE_ISDS;
10371 goto leave;
10373 /* More messages */
10374 if (result->nodesetval->nodeNr > 1) {
10375 char *message_id_locale = _isds_utf82locale((char*) message_id);
10376 isds_printf_message(context,
10377 _("Server did return more messages for ID `%s' "
10378 "on MessageDownload request"), message_id_locale);
10379 free(message_id_locale);
10380 err = IE_ISDS;
10381 goto leave;
10383 /* One message */
10384 xpath_ctx->node = result->nodesetval->nodeTab[0];
10386 /* Extract the message */
10387 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10388 if (err) goto leave;
10390 /* Locate raw XML blob */
10391 phys_path = strdup(
10392 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10393 PHYSXML_ELEMENT_SEPARATOR
10394 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10395 PHYSXML_ELEMENT_SEPARATOR
10396 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10398 if (!phys_path) {
10399 err = IE_NOMEM;
10400 goto leave;
10402 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10403 phys_path, &phys_start, &phys_end);
10404 zfree(phys_path);
10405 if (err) {
10406 isds_log_message(context,
10407 _("Substring with isds:MessageDownloadResponse element "
10408 "could not be located in raw SOAP message"));
10409 goto leave;
10411 /* Save XML blob */
10412 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10413 &(*message)->raw_length);*/
10414 /* TODO: Store name space declarations from ancestors */
10415 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10416 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10417 (*message)->raw_length = phys_end - phys_start + 1;
10418 (*message)->raw = malloc((*message)->raw_length);
10419 if (!(*message)->raw) {
10420 err = IE_NOMEM;
10421 goto leave;
10423 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10426 leave:
10427 if (err) {
10428 isds_message_free(message);
10431 free(phys_path);
10433 xmlXPathFreeObject(result);
10434 xmlXPathFreeContext(xpath_ctx);
10436 free(code);
10437 free(status_message);
10438 free(xml_stream);
10439 if (!*message || !(*message)->xml) {
10440 xmlFreeDoc(response);
10443 if (!err)
10444 isds_log(ILF_ISDS, ILL_DEBUG,
10445 _("MessageDownload request processed by server "
10446 "successfully.\n")
10448 #else /* not HAVE_LIBCURL */
10449 err = IE_NOTSUP;
10450 #endif
10451 return err;
10455 /* Load message of any type from buffer.
10456 * @context is session context
10457 * @raw_type defines content type of @buffer. Only message types are allowed.
10458 * @buffer is message raw representation. Format (CMS, plain signed,
10459 * message direction) is defined in @raw_type. You can retrieve such data
10460 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10461 * @length is length of buffer in bytes.
10462 * @message is automatically reallocated message parsed from @buffer.
10463 * @strategy selects how buffer will be attached into raw isds_message member.
10464 * */
10465 isds_error isds_load_message(struct isds_ctx *context,
10466 const isds_raw_type raw_type, const void *buffer, const size_t length,
10467 struct isds_message **message, const isds_buffer_strategy strategy) {
10469 isds_error err = IE_SUCCESS;
10470 void *xml_stream = NULL;
10471 size_t xml_stream_length = 0;
10472 message_ns_type message_ns;
10473 xmlDocPtr message_doc = NULL;
10474 xmlXPathContextPtr xpath_ctx = NULL;
10475 xmlXPathObjectPtr result = NULL;
10477 if (!context) return IE_INVALID_CONTEXT;
10478 zfree(context->long_message);
10479 if (!message) return IE_INVAL;
10480 isds_message_free(message);
10481 if (!buffer) return IE_INVAL;
10484 /* Select buffer format and extract XML from CMS*/
10485 switch (raw_type) {
10486 case RAWTYPE_INCOMING_MESSAGE:
10487 message_ns = MESSAGE_NS_UNSIGNED;
10488 xml_stream = (void *) buffer;
10489 xml_stream_length = length;
10490 break;
10492 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10493 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10494 xml_stream = (void *) buffer;
10495 xml_stream_length = length;
10496 break;
10498 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10499 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10500 err = _isds_extract_cms_data(context, buffer, length,
10501 &xml_stream, &xml_stream_length);
10502 if (err) goto leave;
10503 break;
10505 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10506 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10507 xml_stream = (void *) buffer;
10508 xml_stream_length = length;
10509 break;
10511 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10512 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10513 err = _isds_extract_cms_data(context, buffer, length,
10514 &xml_stream, &xml_stream_length);
10515 if (err) goto leave;
10516 break;
10518 default:
10519 isds_log_message(context, _("Bad raw message representation type"));
10520 return IE_INVAL;
10521 break;
10524 isds_log(ILF_ISDS, ILL_DEBUG,
10525 _("Loading message:\n%.*s\nEnd of message\n"),
10526 xml_stream_length, xml_stream);
10528 /* Convert messages XML stream into XPath context */
10529 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10530 if (!message_doc) {
10531 err = IE_XML;
10532 goto leave;
10534 xpath_ctx = xmlXPathNewContext(message_doc);
10535 if (!xpath_ctx) {
10536 err = IE_ERROR;
10537 goto leave;
10539 /* XXX: Standard name space for unsigned incoming direction:
10540 * http://isds.czechpoint.cz/v20/
10542 * XXX: Name spaces mangled for signed outgoing direction:
10543 * http://isds.czechpoint.cz/v20/SentMessage:
10545 * <q:MessageDownloadResponse
10546 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10547 * <q:dmReturnedMessage>
10548 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10549 * <p:dmID>151916</p:dmID>
10550 * ...
10551 * </p:dmDm>
10552 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10553 * ...
10554 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10555 * </q:dmReturnedMessage>
10556 * </q:MessageDownloadResponse>
10558 * XXX: Name spaces mangled for signed incoming direction:
10559 * http://isds.czechpoint.cz/v20/message:
10561 * <q:MessageDownloadResponse
10562 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10563 * <q:dmReturnedMessage>
10564 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10565 * <p:dmID>151916</p:dmID>
10566 * ...
10567 * </p:dmDm>
10568 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10569 * ...
10570 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10571 * </q:dmReturnedMessage>
10572 * </q:MessageDownloadResponse>
10574 * Stupidity of ISDS developers is unlimited */
10575 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10576 err = IE_ERROR;
10577 goto leave;
10579 result = xmlXPathEvalExpression(
10580 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10581 xpath_ctx);
10582 if (!result) {
10583 err = IE_ERROR;
10584 goto leave;
10586 /* Empty message */
10587 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10588 isds_printf_message(context,
10589 _("XML document does not contain "
10590 "sisds:dmReturnedMessage element"));
10591 err = IE_ISDS;
10592 goto leave;
10594 /* More messages */
10595 if (result->nodesetval->nodeNr > 1) {
10596 isds_printf_message(context,
10597 _("XML document has more sisds:dmReturnedMessage elements"));
10598 err = IE_ISDS;
10599 goto leave;
10601 /* One message */
10602 xpath_ctx->node = result->nodesetval->nodeTab[0];
10604 /* Extract the message */
10605 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10606 if (err) goto leave;
10608 /* Append raw buffer into message */
10609 (*message)->raw_type = raw_type;
10610 switch (strategy) {
10611 case BUFFER_DONT_STORE:
10612 break;
10613 case BUFFER_COPY:
10614 (*message)->raw = malloc(length);
10615 if (!(*message)->raw) {
10616 err = IE_NOMEM;
10617 goto leave;
10619 memcpy((*message)->raw, buffer, length);
10620 (*message)->raw_length = length;
10621 break;
10622 case BUFFER_MOVE:
10623 (*message)->raw = (void *) buffer;
10624 (*message)->raw_length = length;
10625 break;
10626 default:
10627 err = IE_ENUM;
10628 goto leave;
10632 leave:
10633 if (err) {
10634 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10635 isds_message_free(message);
10638 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10639 xmlXPathFreeObject(result);
10640 xmlXPathFreeContext(xpath_ctx);
10641 if (!*message || !(*message)->xml) {
10642 xmlFreeDoc(message_doc);
10645 if (!err)
10646 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10647 return err;
10651 /* Determine type of raw message or delivery info according some heuristics.
10652 * It does not validate the raw blob.
10653 * @context is session context
10654 * @raw_type returns content type of @buffer. Valid only if exit code of this
10655 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10656 * reallocated memory.
10657 * @buffer is message raw representation.
10658 * @length is length of buffer in bytes. */
10659 isds_error isds_guess_raw_type(struct isds_ctx *context,
10660 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10661 isds_error err;
10662 void *xml_stream = NULL;
10663 size_t xml_stream_length = 0;
10664 xmlDocPtr document = NULL;
10665 xmlNodePtr root = NULL;
10667 if (!context) return IE_INVALID_CONTEXT;
10668 zfree(context->long_message);
10669 if (length == 0 || !buffer) return IE_INVAL;
10670 if (!raw_type) return IE_INVAL;
10672 /* Try CMS */
10673 err = _isds_extract_cms_data(context, buffer, length,
10674 &xml_stream, &xml_stream_length);
10675 if (err) {
10676 xml_stream = (void *) buffer;
10677 xml_stream_length = (size_t) length;
10678 err = IE_SUCCESS;
10681 /* Try XML */
10682 document = xmlParseMemory(xml_stream, xml_stream_length);
10683 if (!document) {
10684 isds_printf_message(context,
10685 _("Could not parse data as XML document"));
10686 err = IE_NOTSUP;
10687 goto leave;
10690 /* Get root element */
10691 root = xmlDocGetRootElement(document);
10692 if (!root) {
10693 isds_printf_message(context,
10694 _("XML document is missing root element"));
10695 err = IE_XML;
10696 goto leave;
10699 if (!root->ns || !root->ns->href) {
10700 isds_printf_message(context,
10701 _("Root element does not belong to any name space"));
10702 err = IE_NOTSUP;
10703 goto leave;
10706 /* Test name space */
10707 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10708 if (xml_stream == buffer)
10709 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10710 else
10711 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10712 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10713 if (xml_stream == buffer)
10714 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10715 else
10716 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10717 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10718 if (xml_stream == buffer)
10719 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10720 else
10721 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10722 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10723 if (xml_stream != buffer) {
10724 isds_printf_message(context,
10725 _("Document in ISDS name space is encapsulated into CMS" ));
10726 err = IE_NOTSUP;
10727 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10728 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10729 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10730 *raw_type = RAWTYPE_DELIVERYINFO;
10731 else {
10732 isds_printf_message(context,
10733 _("Unknown root element in ISDS name space"));
10734 err = IE_NOTSUP;
10736 } else {
10737 isds_printf_message(context,
10738 _("Unknown name space"));
10739 err = IE_NOTSUP;
10742 leave:
10743 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10744 xmlFreeDoc(document);
10745 return err;
10749 /* Download signed incoming/outgoing message identified by ID.
10750 * @context is session context
10751 * @output is true for outgoing message, false for incoming message
10752 * @message_id is message identifier (you can get them from
10753 * isds_get_list_of_{sent,received}_messages())
10754 * @message is automatically reallocated message retrieved from ISDS. The raw
10755 * member will be filled with PKCS#7 structure in DER format. */
10756 static isds_error isds_get_signed_message(struct isds_ctx *context,
10757 const _Bool outgoing, const char *message_id,
10758 struct isds_message **message) {
10760 isds_error err = IE_SUCCESS;
10761 #if HAVE_LIBCURL
10762 xmlDocPtr response = NULL;
10763 xmlChar *code = NULL, *status_message = NULL;
10764 xmlXPathContextPtr xpath_ctx = NULL;
10765 xmlXPathObjectPtr result = NULL;
10766 char *encoded_structure = NULL;
10767 void *raw = NULL;
10768 size_t raw_length = 0;
10769 #endif
10771 if (!context) return IE_INVALID_CONTEXT;
10772 zfree(context->long_message);
10773 if (!message) return IE_INVAL;
10774 isds_message_free(message);
10776 #if HAVE_LIBCURL
10777 /* Do request and check for success */
10778 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10779 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10780 BAD_CAST "SignedMessageDownload",
10781 message_id, &response, NULL, NULL, &code, &status_message);
10782 if (err) goto leave;
10784 /* Find signed message, extract it into raw and maybe free
10785 * response */
10786 err = find_extract_signed_data_free_response(context,
10787 (xmlChar *)message_id, &response,
10788 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10789 BAD_CAST "SignedMessageDownload",
10790 &raw, &raw_length);
10791 if (err) goto leave;
10793 /* Parse message */
10794 err = isds_load_message(context,
10795 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10796 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10797 raw, raw_length, message, BUFFER_MOVE);
10798 if (err) goto leave;
10800 raw = NULL;
10802 leave:
10803 if (err) {
10804 isds_message_free(message);
10807 free(encoded_structure);
10808 xmlXPathFreeObject(result);
10809 xmlXPathFreeContext(xpath_ctx);
10810 free(raw);
10812 free(code);
10813 free(status_message);
10814 xmlFreeDoc(response);
10816 if (!err)
10817 isds_log(ILF_ISDS, ILL_DEBUG,
10818 (outgoing) ?
10819 _("SignedSentMessageDownload request processed by server "
10820 "successfully.\n") :
10821 _("SignedMessageDownload request processed by server "
10822 "successfully.\n")
10824 #else /* not HAVE_LIBCURL */
10825 err = IE_NOTSUP;
10826 #endif
10827 return err;
10831 /* Download signed incoming message identified by ID.
10832 * @context is session context
10833 * @message_id is message identifier (you can get them from
10834 * isds_get_list_of_received_messages())
10835 * @message is automatically reallocated message retrieved from ISDS. The raw
10836 * member will be filled with PKCS#7 structure in DER format. */
10837 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10838 const char *message_id, struct isds_message **message) {
10839 return isds_get_signed_message(context, 0, message_id, message);
10843 /* Download signed outgoing message identified by ID.
10844 * @context is session context
10845 * @message_id is message identifier (you can get them from
10846 * isds_get_list_of_sent_messages())
10847 * @message is automatically reallocated message retrieved from ISDS. The raw
10848 * member will be filled with PKCS#7 structure in DER format. */
10849 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10850 const char *message_id, struct isds_message **message) {
10851 return isds_get_signed_message(context, 1, message_id, message);
10855 /* Get type and name of user who sent a message identified by ID.
10856 * @context is session context
10857 * @message_id is message identifier
10858 * @sender_type is pointer to automatically allocated type of sender detected
10859 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10860 * library or to the server, NULL will be returned. Pass NULL if you don't
10861 * care about it.
10862 * @raw_sender_type is automatically reallocated UTF-8 string describing
10863 * sender type or NULL if not known to server. Pass NULL if you don't care.
10864 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10865 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10866 isds_error isds_get_message_sender(struct isds_ctx *context,
10867 const char *message_id, isds_sender_type **sender_type,
10868 char **raw_sender_type, char **sender_name) {
10869 isds_error err = IE_SUCCESS;
10870 #if HAVE_LIBCURL
10871 xmlDocPtr response = NULL;
10872 xmlChar *code = NULL, *status_message = NULL;
10873 xmlXPathContextPtr xpath_ctx = NULL;
10874 xmlXPathObjectPtr result = NULL;
10875 char *type_string = NULL;
10876 #endif
10878 if (!context) return IE_INVALID_CONTEXT;
10879 zfree(context->long_message);
10880 if (sender_type) zfree(*sender_type);
10881 if (raw_sender_type) zfree(*raw_sender_type);
10882 if (sender_name) zfree(*sender_name);
10883 if (!message_id) return IE_INVAL;
10885 #if HAVE_LIBCURL
10886 /* Do request and check for success */
10887 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10888 BAD_CAST "GetMessageAuthor",
10889 message_id, &response, NULL, NULL, &code, &status_message);
10890 if (err) goto leave;
10892 /* Extract data */
10893 xpath_ctx = xmlXPathNewContext(response);
10894 if (!xpath_ctx) {
10895 err = IE_ERROR;
10896 goto leave;
10898 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10899 err = IE_ERROR;
10900 goto leave;
10902 result = xmlXPathEvalExpression(
10903 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10904 if (!result) {
10905 err = IE_ERROR;
10906 goto leave;
10908 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10909 isds_log_message(context,
10910 _("Missing GetMessageAuthorResponse element"));
10911 err = IE_ISDS;
10912 goto leave;
10914 if (result->nodesetval->nodeNr > 1) {
10915 isds_log_message(context,
10916 _("Multiple GetMessageAuthorResponse element"));
10917 err = IE_ISDS;
10918 goto leave;
10920 xpath_ctx->node = result->nodesetval->nodeTab[0];
10921 xmlXPathFreeObject(result); result = NULL;
10923 /* Fill output arguments in */
10924 EXTRACT_STRING("isds:userType", type_string);
10925 if (NULL != type_string) {
10926 if (NULL != sender_type) {
10927 *sender_type = calloc(1, sizeof(**sender_type));
10928 if (NULL == *sender_type) {
10929 err = IE_NOMEM;
10930 goto leave;
10933 err = string2isds_sender_type((xmlChar *)type_string,
10934 *sender_type);
10935 if (err) {
10936 zfree(*sender_type);
10937 if (err == IE_ENUM) {
10938 err = IE_SUCCESS;
10939 char *type_string_locale = _isds_utf82locale(type_string);
10940 isds_log(ILF_ISDS, ILL_WARNING,
10941 _("Unknown isds:userType value: %s"),
10942 type_string_locale);
10943 free(type_string_locale);
10948 if (NULL != sender_name)
10949 EXTRACT_STRING("isds:authorName", *sender_name);
10951 leave:
10952 if (err) {
10953 if (NULL != sender_type) zfree(*sender_type);
10954 zfree(type_string);
10955 if (NULL != sender_name) zfree(*sender_name);
10957 if (NULL != raw_sender_type) *raw_sender_type = type_string;
10959 xmlXPathFreeObject(result);
10960 xmlXPathFreeContext(xpath_ctx);
10962 free(code);
10963 free(status_message);
10964 xmlFreeDoc(response);
10966 if (!err)
10967 isds_log(ILF_ISDS, ILL_DEBUG,
10968 _("GetMessageAuthor request processed by server "
10969 "successfully.\n"));
10970 #else /* not HAVE_LIBCURL */
10971 err = IE_NOTSUP;
10972 #endif
10973 return err;
10977 /* Retrieve hash of message identified by ID stored in ISDS.
10978 * @context is session context
10979 * @message_id is message identifier
10980 * @hash is automatically reallocated message hash downloaded from ISDS.
10981 * Message must exist in system and must not be deleted. */
10982 isds_error isds_download_message_hash(struct isds_ctx *context,
10983 const char *message_id, struct isds_hash **hash) {
10985 isds_error err = IE_SUCCESS;
10986 #if HAVE_LIBCURL
10987 xmlDocPtr response = NULL;
10988 xmlChar *code = NULL, *status_message = NULL;
10989 xmlXPathContextPtr xpath_ctx = NULL;
10990 xmlXPathObjectPtr result = NULL;
10991 #endif
10993 if (!context) return IE_INVALID_CONTEXT;
10994 zfree(context->long_message);
10996 isds_hash_free(hash);
10998 #if HAVE_LIBCURL
10999 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11000 BAD_CAST "VerifyMessage", message_id,
11001 &response, NULL, NULL, &code, &status_message);
11002 if (err) goto leave;
11005 /* Extract data */
11006 xpath_ctx = xmlXPathNewContext(response);
11007 if (!xpath_ctx) {
11008 err = IE_ERROR;
11009 goto leave;
11011 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11012 err = IE_ERROR;
11013 goto leave;
11015 result = xmlXPathEvalExpression(
11016 BAD_CAST "/isds:VerifyMessageResponse",
11017 xpath_ctx);
11018 if (!result) {
11019 err = IE_ERROR;
11020 goto leave;
11022 /* Empty response */
11023 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11024 char *message_id_locale = _isds_utf82locale((char*) message_id);
11025 isds_printf_message(context,
11026 _("Server did not return any response for ID `%s' "
11027 "on VerifyMessage request"), message_id_locale);
11028 free(message_id_locale);
11029 err = IE_ISDS;
11030 goto leave;
11032 /* More responses */
11033 if (result->nodesetval->nodeNr > 1) {
11034 char *message_id_locale = _isds_utf82locale((char*) message_id);
11035 isds_printf_message(context,
11036 _("Server did return more responses for ID `%s' "
11037 "on VerifyMessage request"), message_id_locale);
11038 free(message_id_locale);
11039 err = IE_ISDS;
11040 goto leave;
11042 /* One response */
11043 xpath_ctx->node = result->nodesetval->nodeTab[0];
11045 /* Extract the hash */
11046 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11048 leave:
11049 if (err) {
11050 isds_hash_free(hash);
11053 xmlXPathFreeObject(result);
11054 xmlXPathFreeContext(xpath_ctx);
11056 free(code);
11057 free(status_message);
11058 xmlFreeDoc(response);
11060 if (!err)
11061 isds_log(ILF_ISDS, ILL_DEBUG,
11062 _("VerifyMessage request processed by server "
11063 "successfully.\n")
11065 #else /* not HAVE_LIBCURL */
11066 err = IE_NOTSUP;
11067 #endif
11068 return err;
11072 /* Erase message specified by @message_id from long term storage. Other
11073 * message cannot be erased on user request.
11074 * @context is session context
11075 * @message_id is message identifier.
11076 * @incoming is true for incoming message, false for outgoing message.
11077 * @return
11078 * IE_SUCCESS if message has ben removed
11079 * IE_INVAL if message does not exist in long term storage or message
11080 * belongs to different box
11081 * TODO: IE_NOEPRM if user has no permission to erase a message */
11082 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11083 const char *message_id, _Bool incoming) {
11084 isds_error err = IE_SUCCESS;
11085 #if HAVE_LIBCURL
11086 xmlNodePtr request = NULL, node;
11087 xmlNsPtr isds_ns = NULL;
11088 xmlDocPtr response = NULL;
11089 xmlChar *code = NULL, *status_message = NULL;
11090 #endif
11092 if (!context) return IE_INVALID_CONTEXT;
11093 zfree(context->long_message);
11094 if (NULL == message_id) return IE_INVAL;
11096 /* Check if connection is established
11097 * TODO: This check should be done downstairs. */
11098 if (!context->curl) return IE_CONNECTION_CLOSED;
11100 #if HAVE_LIBCURL
11101 /* Build request */
11102 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11103 if (!request) {
11104 isds_log_message(context,
11105 _("Could build EraseMessage request"));
11106 return IE_ERROR;
11108 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11109 if(!isds_ns) {
11110 isds_log_message(context, _("Could not create ISDS name space"));
11111 xmlFreeNode(request);
11112 return IE_ERROR;
11114 xmlSetNs(request, isds_ns);
11116 err = validate_message_id_length(context, (xmlChar *) message_id);
11117 if (err) goto leave;
11118 INSERT_STRING(request, "dmID", message_id);
11120 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11123 /* Send request */
11124 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11125 "message ID %s to ISDS\n"), message_id);
11126 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11127 xmlFreeNode(request); request = NULL;
11129 if (err) {
11130 isds_log(ILF_ISDS, ILL_DEBUG,
11131 _("Processing ISDS response on EraseMessage request "
11132 "failed\n"));
11133 goto leave;
11136 /* Check for response status */
11137 err = isds_response_status(context, SERVICE_DM_INFO, response,
11138 &code, &status_message, NULL);
11139 if (err) {
11140 isds_log(ILF_ISDS, ILL_DEBUG,
11141 _("ISDS response on EraseMessage request is missing "
11142 "status\n"));
11143 goto leave;
11146 /* Check server status code */
11147 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11148 isds_log_message(context, _("Message to erase belongs to other box"));
11149 err = IE_INVAL;
11150 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11151 isds_log_message(context, _("Message to erase is not saved in "
11152 "long term storage or the direction does not match"));
11153 err = IE_INVAL;
11154 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11155 char *code_locale = _isds_utf82locale((char*) code);
11156 char *message_locale = _isds_utf82locale((char*) status_message);
11157 isds_log(ILF_ISDS, ILL_DEBUG,
11158 _("Server refused EraseMessage request "
11159 "(code=%s, message=%s)\n"),
11160 code_locale, message_locale);
11161 isds_log_message(context, message_locale);
11162 free(code_locale);
11163 free(message_locale);
11164 err = IE_ISDS;
11165 goto leave;
11168 leave:
11169 free(code);
11170 free(status_message);
11171 xmlFreeDoc(response);
11172 xmlFreeNode(request);
11174 if (!err)
11175 isds_log(ILF_ISDS, ILL_DEBUG,
11176 _("EraseMessage request processed by server "
11177 "successfully.\n")
11179 #else /* not HAVE_LIBCURL */
11180 err = IE_NOTSUP;
11181 #endif
11182 return err;
11186 /* Mark message as read. This is a transactional commit function to acknowledge
11187 * to ISDS the message has been downloaded and processed by client properly.
11188 * @context is session context
11189 * @message_id is message identifier. */
11190 isds_error isds_mark_message_read(struct isds_ctx *context,
11191 const char *message_id) {
11193 isds_error err = IE_SUCCESS;
11194 #if HAVE_LIBCURL
11195 xmlDocPtr response = NULL;
11196 xmlChar *code = NULL, *status_message = NULL;
11197 #endif
11199 if (!context) return IE_INVALID_CONTEXT;
11200 zfree(context->long_message);
11202 #if HAVE_LIBCURL
11203 /* Do request and check for success */
11204 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11205 BAD_CAST "MarkMessageAsDownloaded", message_id,
11206 &response, NULL, NULL, &code, &status_message);
11208 free(code);
11209 free(status_message);
11210 xmlFreeDoc(response);
11212 if (!err)
11213 isds_log(ILF_ISDS, ILL_DEBUG,
11214 _("MarkMessageAsDownloaded request processed by server "
11215 "successfully.\n")
11217 #else /* not HAVE_LIBCURL */
11218 err = IE_NOTSUP;
11219 #endif
11220 return err;
11224 /* Mark message as received by recipient. This is applicable only to
11225 * commercial message. Use envelope->dmType message member to distinguish
11226 * commercial message from government message. Government message is
11227 * received automatically (by law), commercial message on recipient request.
11228 * @context is session context
11229 * @message_id is message identifier. */
11230 isds_error isds_mark_message_received(struct isds_ctx *context,
11231 const char *message_id) {
11233 isds_error err = IE_SUCCESS;
11234 #if HAVE_LIBCURL
11235 xmlDocPtr response = NULL;
11236 xmlChar *code = NULL, *status_message = NULL;
11237 #endif
11239 if (!context) return IE_INVALID_CONTEXT;
11240 zfree(context->long_message);
11242 #if HAVE_LIBCURL
11243 /* Do request and check for success */
11244 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11245 BAD_CAST "ConfirmDelivery", message_id,
11246 &response, NULL, NULL, &code, &status_message);
11248 free(code);
11249 free(status_message);
11250 xmlFreeDoc(response);
11252 if (!err)
11253 isds_log(ILF_ISDS, ILL_DEBUG,
11254 _("ConfirmDelivery request processed by server "
11255 "successfully.\n")
11257 #else /* not HAVE_LIBCURL */
11258 err = IE_NOTSUP;
11259 #endif
11260 return err;
11264 /* Send document for authorized conversion into Czech POINT system.
11265 * This is public anonymous service, no log-in necessary. Special context is
11266 * used to reuse keep-a-live HTTPS connection.
11267 * @context is Czech POINT session context. DO NOT use context connected to
11268 * ISDS server. Use new context or context used by this function previously.
11269 * @document is document to convert. Only data, data_length, dmFileDescr and
11270 * is_xml members are significant. Be ware that not all document formats can be
11271 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11272 * @id is reallocated identifier assigned by Czech POINT system to
11273 * your document on submit. Use is to tell it to Czech POINT officer.
11274 * @date is reallocated document submit date (submitted documents
11275 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11276 * value. */
11277 isds_error czp_convert_document(struct isds_ctx *context,
11278 const struct isds_document *document,
11279 char **id, struct tm **date) {
11280 isds_error err = IE_SUCCESS;
11281 #if HAVE_LIBCURL
11282 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11283 xmlNodePtr request = NULL, node;
11284 xmlDocPtr response = NULL;
11286 xmlXPathContextPtr xpath_ctx = NULL;
11287 xmlXPathObjectPtr result = NULL;
11288 long int status = -1;
11289 long int *status_ptr = &status;
11290 char *string = NULL;
11291 #endif
11294 if (!context) return IE_INVALID_CONTEXT;
11295 zfree(context->long_message);
11296 if (!document || !id || !date) return IE_INVAL;
11298 if (document->is_xml) {
11299 isds_log_message(context,
11300 _("XML documents cannot be submitted to conversion"));
11301 return IE_NOTSUP;
11304 /* Free output arguments */
11305 zfree(*id);
11306 zfree(*date);
11308 #if HAVE_LIBCURL
11309 /* Store configuration */
11310 context->type = CTX_TYPE_CZP;
11311 free(context->url);
11312 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11313 if (!(context->url))
11314 return IE_NOMEM;
11316 /* Prepare CURL handle if not yet connected */
11317 if (!context->curl) {
11318 context->curl = curl_easy_init();
11319 if (!(context->curl))
11320 return IE_ERROR;
11323 /* Build conversion request */
11324 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11325 if (!request) {
11326 isds_log_message(context,
11327 _("Could not build Czech POINT conversion request"));
11328 return IE_ERROR;
11330 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11331 if(!deposit_ns) {
11332 isds_log_message(context,
11333 _("Could not create Czech POINT deposit name space"));
11334 xmlFreeNode(request);
11335 return IE_ERROR;
11337 xmlSetNs(request, deposit_ns);
11339 /* Insert children. They are in empty namespace! */
11340 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11341 if(!empty_ns) {
11342 isds_log_message(context, _("Could not create empty name space"));
11343 err = IE_ERROR;
11344 goto leave;
11346 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11347 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11348 document->dmFileDescr);
11350 /* Document encoded in Base64 */
11351 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11352 document->data, document->data_length);
11353 if (err) goto leave;
11355 isds_log(ILF_ISDS, ILL_DEBUG,
11356 _("Submitting document for conversion into Czech POINT deposit"));
11358 /* Send conversion request */
11359 err = _czp_czpdeposit(context, request, &response);
11360 xmlFreeNode(request); request = NULL;
11362 if (err) {
11363 czp_do_close_connection(context);
11364 goto leave;
11368 /* Extract response */
11369 xpath_ctx = xmlXPathNewContext(response);
11370 if (!xpath_ctx) {
11371 err = IE_ERROR;
11372 goto leave;
11374 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11375 err = IE_ERROR;
11376 goto leave;
11378 result = xmlXPathEvalExpression(
11379 BAD_CAST "/deposit:saveDocumentResponse/return",
11380 xpath_ctx);
11381 if (!result) {
11382 err = IE_ERROR;
11383 goto leave;
11385 /* Empty response */
11386 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11387 isds_printf_message(context,
11388 _("Missing `return' element in Czech POINT deposit response"));
11389 err = IE_ISDS;
11390 goto leave;
11392 /* More responses */
11393 if (result->nodesetval->nodeNr > 1) {
11394 isds_printf_message(context,
11395 _("Multiple `return' element in Czech POINT deposit response"));
11396 err = IE_ISDS;
11397 goto leave;
11399 /* One response */
11400 xpath_ctx->node = result->nodesetval->nodeTab[0];
11402 /* Get status */
11403 EXTRACT_LONGINT("status", status_ptr, 1);
11404 if (status) {
11405 EXTRACT_STRING("statusMsg", string);
11406 char *string_locale = _isds_utf82locale(string);
11407 isds_printf_message(context,
11408 _("Czech POINT deposit refused document for conversion "
11409 "(code=%ld, message=%s)"),
11410 status, string_locale);
11411 free(string_locale);
11412 err = IE_ISDS;
11413 goto leave;
11416 /* Get document ID */
11417 EXTRACT_STRING("documentID", *id);
11419 /* Get submit date */
11420 EXTRACT_STRING("dateInserted", string);
11421 if (string) {
11422 *date = calloc(1, sizeof(**date));
11423 if (!*date) {
11424 err = IE_NOMEM;
11425 goto leave;
11427 err = _isds_datestring2tm((xmlChar *)string, *date);
11428 if (err) {
11429 if (err == IE_NOTSUP) {
11430 err = IE_ISDS;
11431 char *string_locale = _isds_utf82locale(string);
11432 isds_printf_message(context,
11433 _("Invalid dateInserted value: %s"), string_locale);
11434 free(string_locale);
11436 goto leave;
11440 leave:
11441 free(string);
11442 xmlXPathFreeObject(result);
11443 xmlXPathFreeContext(xpath_ctx);
11445 xmlFreeDoc(response);
11446 xmlFreeNode(request);
11448 if (!err) {
11449 char *id_locale = _isds_utf82locale((char *) *id);
11450 isds_log(ILF_ISDS, ILL_DEBUG,
11451 _("Document %s has been submitted for conversion "
11452 "to server successfully\n"), id_locale);
11453 free(id_locale);
11455 #else /* not HAVE_LIBCURL */
11456 err = IE_NOTSUP;
11457 #endif
11458 return err;
11462 /* Close possibly opened connection to Czech POINT document deposit.
11463 * @context is Czech POINT session context. */
11464 isds_error czp_close_connection(struct isds_ctx *context) {
11465 if (!context) return IE_INVALID_CONTEXT;
11466 zfree(context->long_message);
11467 #if HAVE_LIBCURL
11468 return czp_do_close_connection(context);
11469 #else
11470 return IE_NOTSUP;
11471 #endif
11475 /* Send request for new box creation in testing ISDS instance.
11476 * It's not possible to request for a production box currently, as it
11477 * communicates via e-mail.
11478 * XXX: This function does not work either. Server complains about invalid
11479 * e-mail address.
11480 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11481 * this function
11482 * @context is special session context for box creation request. DO NOT use
11483 * standard context as it could reveal your password. Use fresh new context or
11484 * context previously used by this function.
11485 * @box is box description to create including single primary user (in case of
11486 * FO box type). It outputs box ID assigned by ISDS in dbID element.
11487 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11488 * box, or contact address of PFO box owner). The email member is mandatory as
11489 * it will be used to deliver credentials.
11490 * @former_names is former name of box owner. Pass NULL if you don't care.
11491 * @approval is optional external approval of box manipulation
11492 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11493 * NULL, if you don't care.*/
11494 isds_error isds_request_new_testing_box(struct isds_ctx *context,
11495 struct isds_DbOwnerInfo *box, const struct isds_list *users,
11496 const char *former_names, const struct isds_approval *approval,
11497 char **refnumber) {
11498 isds_error err = IE_SUCCESS;
11499 #if HAVE_LIBCURL
11500 xmlNodePtr request = NULL;
11501 xmlDocPtr response = NULL;
11502 xmlXPathContextPtr xpath_ctx = NULL;
11503 xmlXPathObjectPtr result = NULL;
11504 #endif
11507 if (!context) return IE_INVALID_CONTEXT;
11508 zfree(context->long_message);
11509 if (!box) return IE_INVAL;
11511 #if HAVE_LIBCURL
11512 if (!box->email || box->email[0] == '\0') {
11513 isds_log_message(context, _("E-mail field is mandatory"));
11514 return IE_INVAL;
11517 /* Scratch box ID */
11518 zfree(box->dbID);
11520 /* Store configuration */
11521 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
11522 free(context->url);
11523 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
11524 if (!(context->url))
11525 return IE_NOMEM;
11527 /* Prepare CURL handle if not yet connected */
11528 if (!context->curl) {
11529 context->curl = curl_easy_init();
11530 if (!(context->curl))
11531 return IE_ERROR;
11534 /* Build CreateDataBox request */
11535 err = build_CreateDBInput_request(context,
11536 &request, BAD_CAST "CreateDataBox",
11537 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
11538 if (err) goto leave;
11540 /* Send it to server and process response */
11541 err = send_destroy_request_check_response(context,
11542 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
11543 &response, (xmlChar **) refnumber, NULL);
11544 if (err) goto leave;
11546 /* Extract box ID */
11547 xpath_ctx = xmlXPathNewContext(response);
11548 if (!xpath_ctx) {
11549 err = IE_ERROR;
11550 goto leave;
11552 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11553 err = IE_ERROR;
11554 goto leave;
11556 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
11558 leave:
11559 xmlXPathFreeObject(result);
11560 xmlXPathFreeContext(xpath_ctx);
11561 xmlFreeDoc(response);
11562 xmlFreeNode(request);
11564 if (!err) {
11565 isds_log(ILF_ISDS, ILL_DEBUG,
11566 _("CreateDataBox request processed by server successfully.\n"));
11568 #else /* not HAVE_LIBCURL */
11569 err = IE_NOTSUP;
11570 #endif
11572 return err;
11576 /* Submit CMS signed message to ISDS to verify its originality. This is
11577 * stronger form of isds_verify_message_hash() because ISDS does more checks
11578 * than simple one (potentialy old weak) hash comparison.
11579 * @context is session context
11580 * @message is memory with raw CMS signed message bit stream
11581 * @length is @message size in bytes
11582 * @return
11583 * IE_SUCCESS if message originates in ISDS
11584 * IE_NOTEQUAL if message is unknown to ISDS
11585 * other code for other errors */
11586 isds_error isds_authenticate_message(struct isds_ctx *context,
11587 const void *message, size_t length) {
11588 isds_error err = IE_SUCCESS;
11589 #if HAVE_LIBCURL
11590 xmlNsPtr isds_ns = NULL;
11591 xmlNodePtr request = NULL;
11592 xmlDocPtr response = NULL;
11593 xmlXPathContextPtr xpath_ctx = NULL;
11594 xmlXPathObjectPtr result = NULL;
11595 _Bool *authentic = NULL;
11596 #endif
11598 if (!context) return IE_INVALID_CONTEXT;
11599 zfree(context->long_message);
11600 if (!message || length == 0) return IE_INVAL;
11602 #if HAVE_LIBCURL
11603 /* Check if connection is established
11604 * TODO: This check should be done downstairs. */
11605 if (!context->curl) return IE_CONNECTION_CLOSED;
11608 /* Build AuthenticateMessage request */
11609 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11610 if (!request) {
11611 isds_log_message(context,
11612 _("Could not build AuthenticateMessage request"));
11613 return IE_ERROR;
11615 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11616 if(!isds_ns) {
11617 isds_log_message(context, _("Could not create ISDS name space"));
11618 xmlFreeNode(request);
11619 return IE_ERROR;
11621 xmlSetNs(request, isds_ns);
11623 /* Insert Base64 encoded message */
11624 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11625 message, length);
11626 if (err) goto leave;
11628 /* Send request to server and process response */
11629 err = send_destroy_request_check_response(context,
11630 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11631 &response, NULL, NULL);
11632 if (err) goto leave;
11635 /* ISDS has decided */
11636 xpath_ctx = xmlXPathNewContext(response);
11637 if (!xpath_ctx) {
11638 err = IE_ERROR;
11639 goto leave;
11641 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11642 err = IE_ERROR;
11643 goto leave;
11646 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11648 if (!authentic) {
11649 isds_log_message(context,
11650 _("Server did not return any response on "
11651 "AuthenticateMessage request"));
11652 err = IE_ISDS;
11653 goto leave;
11655 if (*authentic) {
11656 isds_log(ILF_ISDS, ILL_DEBUG,
11657 _("ISDS authenticated the message successfully\n"));
11658 } else {
11659 isds_log_message(context, _("ISDS does not know the message"));
11660 err = IE_NOTEQUAL;
11664 leave:
11665 free(authentic);
11666 xmlXPathFreeObject(result);
11667 xmlXPathFreeContext(xpath_ctx);
11669 xmlFreeDoc(response);
11670 xmlFreeNode(request);
11671 #else /* not HAVE_LIBCURL */
11672 err = IE_NOTSUP;
11673 #endif
11675 return err;
11679 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11680 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11681 * be re-signed.
11682 * @context is session context
11683 * @input_data is memory with raw CMS signed message or delivery info bit
11684 * stream to re-sign
11685 * @input_length is @input_data size in bytes
11686 * @output_data is pointer to auto-allocated memory where to store re-signed
11687 * input data blob. Caller must free it.
11688 * @output_data is pointer where to store @output_data size in bytes
11689 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11690 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11691 * @return
11692 * IE_SUCCESS if CMS blob has been re-signed successfully
11693 * other code for other errors */
11694 isds_error isds_resign_message(struct isds_ctx *context,
11695 const void *input_data, size_t input_length,
11696 void **output_data, size_t *output_length, struct tm **valid_to) {
11697 isds_error err = IE_SUCCESS;
11698 #if HAVE_LIBCURL
11699 xmlNsPtr isds_ns = NULL;
11700 xmlNodePtr request = NULL;
11701 xmlDocPtr response = NULL;
11702 xmlXPathContextPtr xpath_ctx = NULL;
11703 xmlXPathObjectPtr result = NULL;
11704 char *string = NULL;
11705 const xmlChar *codes[] = {
11706 BAD_CAST "2200",
11707 BAD_CAST "2201",
11708 BAD_CAST "2204",
11709 BAD_CAST "2207",
11710 NULL
11712 const char *meanings[] = {
11713 "Message is bad",
11714 "Message is not original",
11715 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11716 "Time stamp could not been generated in time"
11718 const isds_error errors[] = {
11719 IE_INVAL,
11720 IE_NOTUNIQ,
11721 IE_INVAL,
11722 IE_ISDS,
11724 struct code_map_isds_error map = {
11725 .codes = codes,
11726 .meanings = meanings,
11727 .errors = errors
11729 #endif
11731 if (NULL != output_data) *output_data = NULL;
11732 if (NULL != output_length) *output_length = 0;
11733 if (NULL != valid_to) *valid_to = NULL;
11735 if (NULL == context) return IE_INVALID_CONTEXT;
11736 zfree(context->long_message);
11737 if (NULL == input_data || 0 == input_length) {
11738 isds_log_message(context, _("Empty CMS blob on input"));
11739 return IE_INVAL;
11741 if (NULL == output_data || NULL == output_length) {
11742 isds_log_message(context,
11743 _("NULL pointer provided for output CMS blob"));
11744 return IE_INVAL;
11747 #if HAVE_LIBCURL
11748 /* Check if connection is established
11749 * TODO: This check should be done downstairs. */
11750 if (!context->curl) return IE_CONNECTION_CLOSED;
11753 /* Build Re-signISDSDocument request */
11754 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11755 if (!request) {
11756 isds_log_message(context,
11757 _("Could not build Re-signISDSDocument request"));
11758 return IE_ERROR;
11760 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11761 if(!isds_ns) {
11762 isds_log_message(context, _("Could not create ISDS name space"));
11763 xmlFreeNode(request);
11764 return IE_ERROR;
11766 xmlSetNs(request, isds_ns);
11768 /* Insert Base64 encoded CMS blob */
11769 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11770 input_data, input_length);
11771 if (err) goto leave;
11773 /* Send request to server and process response */
11774 err = send_destroy_request_check_response(context,
11775 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11776 &response, NULL, &map);
11777 if (err) goto leave;
11780 /* Extract re-signed data */
11781 xpath_ctx = xmlXPathNewContext(response);
11782 if (!xpath_ctx) {
11783 err = IE_ERROR;
11784 goto leave;
11786 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11787 err = IE_ERROR;
11788 goto leave;
11790 result = xmlXPathEvalExpression(
11791 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11792 if (!result) {
11793 err = IE_ERROR;
11794 goto leave;
11796 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11797 isds_log_message(context,
11798 _("Missing Re-signISDSDocumentResponse element"));
11799 err = IE_ISDS;
11800 goto leave;
11802 if (result->nodesetval->nodeNr > 1) {
11803 isds_log_message(context,
11804 _("Multiple Re-signISDSDocumentResponse element"));
11805 err = IE_ISDS;
11806 goto leave;
11808 xpath_ctx->node = result->nodesetval->nodeTab[0];
11809 xmlXPathFreeObject(result); result = NULL;
11811 EXTRACT_STRING("isds:dmResultDoc", string);
11812 /* Decode non-empty data */
11813 if (NULL != string && string[0] != '\0') {
11814 *output_length = _isds_b64decode(string, output_data);
11815 if (*output_length == (size_t) -1) {
11816 isds_log_message(context,
11817 _("Error while Base64-decoding re-signed data"));
11818 err = IE_ERROR;
11819 goto leave;
11821 } else {
11822 isds_log_message(context, _("Server did not send re-signed data"));
11823 err = IE_ISDS;
11824 goto leave;
11826 zfree(string);
11828 if (NULL != valid_to) {
11829 /* Get time stamp expiration date */
11830 EXTRACT_STRING("isds:dmValidTo", string);
11831 if (NULL != string) {
11832 *valid_to = calloc(1, sizeof(**valid_to));
11833 if (!*valid_to) {
11834 err = IE_NOMEM;
11835 goto leave;
11837 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11838 if (err) {
11839 if (err == IE_NOTSUP) {
11840 err = IE_ISDS;
11841 char *string_locale = _isds_utf82locale(string);
11842 isds_printf_message(context,
11843 _("Invalid dmValidTo value: %s"), string_locale);
11844 free(string_locale);
11846 goto leave;
11851 leave:
11852 free(string);
11854 xmlXPathFreeObject(result);
11855 xmlXPathFreeContext(xpath_ctx);
11857 xmlFreeDoc(response);
11858 xmlFreeNode(request);
11859 #else /* not HAVE_LIBCURL */
11860 err = IE_NOTSUP;
11861 #endif
11863 return err;
11866 #undef INSERT_ELEMENT
11867 #undef CHECK_FOR_STRING_LENGTH
11868 #undef INSERT_STRING_ATTRIBUTE
11869 #undef INSERT_ULONGINTNOPTR
11870 #undef INSERT_ULONGINT
11871 #undef INSERT_LONGINT
11872 #undef INSERT_BOOLEAN
11873 #undef INSERT_SCALAR_BOOLEAN
11874 #undef INSERT_STRING
11875 #undef INSERT_STRING_WITH_NS
11876 #undef EXTRACT_STRING_ATTRIBUTE
11877 #undef EXTRACT_ULONGINT
11878 #undef EXTRACT_LONGINT
11879 #undef EXTRACT_BOOLEAN
11880 #undef EXTRACT_STRING
11883 /* Compute hash of message from raw representation and store it into envelope.
11884 * Original hash structure will be destroyed in envelope.
11885 * @context is session context
11886 * @message is message carrying raw XML message blob
11887 * @algorithm is desired hash algorithm to use */
11888 isds_error isds_compute_message_hash(struct isds_ctx *context,
11889 struct isds_message *message, const isds_hash_algorithm algorithm) {
11890 isds_error err = IE_SUCCESS;
11891 const char *nsuri;
11892 void *xml_stream = NULL;
11893 size_t xml_stream_length;
11894 size_t phys_start, phys_end;
11895 char *phys_path = NULL;
11896 struct isds_hash *new_hash = NULL;
11899 if (!context) return IE_INVALID_CONTEXT;
11900 zfree(context->long_message);
11901 if (!message) return IE_INVAL;
11903 if (!message->raw) {
11904 isds_log_message(context,
11905 _("Message does not carry raw representation"));
11906 return IE_INVAL;
11909 switch (message->raw_type) {
11910 case RAWTYPE_INCOMING_MESSAGE:
11911 nsuri = ISDS_NS;
11912 xml_stream = message->raw;
11913 xml_stream_length = message->raw_length;
11914 break;
11916 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11917 nsuri = SISDS_INCOMING_NS;
11918 xml_stream = message->raw;
11919 xml_stream_length = message->raw_length;
11920 break;
11922 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11923 nsuri = SISDS_INCOMING_NS;
11924 err = _isds_extract_cms_data(context,
11925 message->raw, message->raw_length,
11926 &xml_stream, &xml_stream_length);
11927 if (err) goto leave;
11928 break;
11930 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11931 nsuri = SISDS_OUTGOING_NS;
11932 xml_stream = message->raw;
11933 xml_stream_length = message->raw_length;
11934 break;
11936 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11937 nsuri = SISDS_OUTGOING_NS;
11938 err = _isds_extract_cms_data(context,
11939 message->raw, message->raw_length,
11940 &xml_stream, &xml_stream_length);
11941 if (err) goto leave;
11942 break;
11944 default:
11945 isds_log_message(context, _("Bad raw representation type"));
11946 return IE_INVAL;
11947 break;
11951 /* XXX: Hash is computed from original string representing isds:dmDm
11952 * subtree. That means no encoding, white space, xmlns attributes changes.
11953 * In other words, input for hash can be invalid XML stream. */
11954 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
11955 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11956 PHYSXML_ELEMENT_SEPARATOR,
11957 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
11958 PHYSXML_ELEMENT_SEPARATOR
11959 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
11960 err = IE_NOMEM;
11961 goto leave;
11963 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11964 phys_path, &phys_start, &phys_end);
11965 zfree(phys_path);
11966 if (err) {
11967 isds_log_message(context,
11968 _("Substring with isds:dmDM element could not be located "
11969 "in raw message"));
11970 goto leave;
11974 /* Compute hash */
11975 new_hash = calloc(1, sizeof(*new_hash));
11976 if (!new_hash) {
11977 err = IE_NOMEM;
11978 goto leave;
11980 new_hash->algorithm = algorithm;
11981 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
11982 new_hash);
11983 if (err) {
11984 isds_log_message(context, _("Could not compute message hash"));
11985 goto leave;
11988 /* Save computed hash */
11989 if (!message->envelope) {
11990 message->envelope = calloc(1, sizeof(*message->envelope));
11991 if (!message->envelope) {
11992 err = IE_NOMEM;
11993 goto leave;
11996 isds_hash_free(&message->envelope->hash);
11997 message->envelope->hash = new_hash;
11999 leave:
12000 if (err) {
12001 isds_hash_free(&new_hash);
12004 free(phys_path);
12005 if (xml_stream != message->raw) free(xml_stream);
12006 return err;
12010 /* Compare two hashes.
12011 * @h1 is first hash
12012 * @h2 is another hash
12013 * @return
12014 * IE_SUCCESS if hashes equal
12015 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12016 * IE_ENUM if not comparable, but both structures defined
12017 * IE_INVAL if some of the structures are undefined (NULL)
12018 * IE_ERROR if internal error occurs */
12019 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
12020 if (h1 == NULL || h2 == NULL) return IE_INVAL;
12021 if (h1->algorithm != h2->algorithm) return IE_ENUM;
12022 if (h1->length != h2->length) return IE_ERROR;
12023 if (h1->length > 0 && !h1->value) return IE_ERROR;
12024 if (h2->length > 0 && !h2->value) return IE_ERROR;
12026 for (size_t i = 0; i < h1->length; i++) {
12027 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12028 return IE_NOTEQUAL;
12030 return IE_SUCCESS;
12034 /* Check message has gone through ISDS by comparing message hash stored in
12035 * ISDS and locally computed hash. You must provide message with valid raw
12036 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12037 * This is convenient wrapper for isds_download_message_hash(),
12038 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12039 * @context is session context
12040 * @message is message with valid raw and envelope member; envelope->hash
12041 * member will be changed during function run. Use envelope on heap only.
12042 * @return
12043 * IE_SUCCESS if message originates in ISDS
12044 * IE_NOTEQUAL if message is unknown to ISDS
12045 * other code for other errors */
12046 isds_error isds_verify_message_hash(struct isds_ctx *context,
12047 struct isds_message *message) {
12048 isds_error err = IE_SUCCESS;
12049 struct isds_hash *downloaded_hash = NULL;
12051 if (!context) return IE_INVALID_CONTEXT;
12052 zfree(context->long_message);
12053 if (!message) return IE_INVAL;
12055 if (!message->envelope) {
12056 isds_log_message(context,
12057 _("Given message structure is missing envelope"));
12058 return IE_INVAL;
12060 if (!message->raw) {
12061 isds_log_message(context,
12062 _("Given message structure is missing raw representation"));
12063 return IE_INVAL;
12066 err = isds_download_message_hash(context, message->envelope->dmID,
12067 &downloaded_hash);
12068 if (err) goto leave;
12070 err = isds_compute_message_hash(context, message,
12071 downloaded_hash->algorithm);
12072 if (err) goto leave;
12074 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12076 leave:
12077 isds_hash_free(&downloaded_hash);
12078 return err;
12082 /* Search for document by document ID in list of documents. IDs are compared
12083 * as UTF-8 string.
12084 * @documents is list of isds_documents
12085 * @id is document identifier
12086 * @return first matching document or NULL. */
12087 const struct isds_document *isds_find_document_by_id(
12088 const struct isds_list *documents, const char *id) {
12089 const struct isds_list *item;
12090 const struct isds_document *document;
12092 for (item = documents; item; item = item->next) {
12093 document = (struct isds_document *) item->data;
12094 if (!document) continue;
12096 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12097 return document;
12100 return NULL;
12104 /* Normalize @mime_type to be proper MIME type.
12105 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12106 * guess regular MIME type (e.g. "application/pdf").
12107 * @mime_type is UTF-8 encoded MIME type to fix
12108 * @return original @mime_type if no better interpretation exists, or
12109 * constant static UTF-8 encoded string with proper MIME type. */
12110 const char *isds_normalize_mime_type(const char *mime_type) {
12111 if (!mime_type) return NULL;
12113 for (size_t offset = 0;
12114 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12115 offset += 2) {
12116 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12117 extension_map_mime[offset]))
12118 return (const char *) extension_map_mime[offset + 1];
12121 return mime_type;
12125 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12126 struct isds_message **message);
12127 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12128 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12129 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12130 struct isds_address **address);
12132 int isds_message_free(struct isds_message **message);
12133 int isds_address_free(struct isds_address **address);
12137 /* Makes known all relevant namespaces to given XPath context
12138 * @xpath_ctx is XPath context
12139 * @message_ns selects proper message name space. Unsigned and signed
12140 * messages and delivery info's differ in prefix and URI. */
12141 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12142 const message_ns_type message_ns) {
12143 const xmlChar *message_namespace = NULL;
12145 if (!xpath_ctx) return IE_ERROR;
12147 switch(message_ns) {
12148 case MESSAGE_NS_1:
12149 message_namespace = BAD_CAST ISDS1_NS; break;
12150 case MESSAGE_NS_UNSIGNED:
12151 message_namespace = BAD_CAST ISDS_NS; break;
12152 case MESSAGE_NS_SIGNED_INCOMING:
12153 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12154 case MESSAGE_NS_SIGNED_OUTGOING:
12155 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12156 case MESSAGE_NS_SIGNED_DELIVERY:
12157 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12158 default:
12159 return IE_ENUM;
12162 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12163 return IE_ERROR;
12164 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12165 return IE_ERROR;
12166 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12167 return IE_ERROR;
12168 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12169 return IE_ERROR;
12170 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12171 return IE_ERROR;
12172 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12173 return IE_ERROR;
12174 return IE_SUCCESS;