Format tv_usec as int32_t in timeval2timestring()
[libisds.git] / src / isds.c
blobcee85f086d8e74eda0eebb489e9c38e53ea02b89
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 /* XXX: Do not format time->tv_usec as intmax_t because %jd is not
2244 * supported and PRIdMAX is broken on MingGW. We can use int32_t because
2245 * of the range check above. */
2246 if (-1 == isds_asprintf((char **) string,
2247 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRId32,
2248 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2249 broken.tm_hour, broken.tm_min, broken.tm_sec,
2250 (int32_t)time->tv_usec))
2251 return IE_ERROR;
2253 return IE_SUCCESS;
2255 #endif /* HAVE_LIBCURL */
2258 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2259 * It respects microseconds too. Microseconds are rounded half up.
2260 * In case of error, @time will be freed. */
2261 static isds_error timestring2timeval(const xmlChar *string,
2262 struct timeval **time) {
2263 struct tm broken;
2264 char *offset, *delim, *endptr;
2265 const int subsecond_resolution = 6;
2266 char subseconds[subsecond_resolution + 1];
2267 _Bool round_up = 0;
2268 int offset_hours, offset_minutes;
2269 int i;
2270 long int long_number;
2271 #ifdef _WIN32
2272 int tmp;
2273 #endif
2275 if (!time) return IE_INVAL;
2276 if (!string) {
2277 zfree(*time);
2278 return IE_INVAL;
2281 memset(&broken, 0, sizeof(broken));
2283 if (!*time) {
2284 *time = calloc(1, sizeof(**time));
2285 if (!*time) return IE_NOMEM;
2286 } else {
2287 memset(*time, 0, sizeof(**time));
2291 /* xsd:date is ISO 8601 string, thus ASCII */
2292 /*TODO: negative year */
2294 #ifdef _WIN32
2295 i = 0;
2296 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2297 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2298 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2299 &i)) < 6) {
2300 zfree(*time);
2301 return IE_DATE;
2304 broken.tm_year -= 1900;
2305 broken.tm_mon--;
2306 broken.tm_isdst = -1;
2307 offset = (char*)string + i;
2308 #else
2309 /* Parse date and time without subseconds and offset */
2310 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2311 if (!offset) {
2312 zfree(*time);
2313 return IE_DATE;
2315 #endif
2317 /* Get subseconds */
2318 if (*offset == '.' ) {
2319 offset++;
2321 /* Copy first 6 digits, pad it with zeros.
2322 * Current server implementation uses only millisecond resolution. */
2323 /* TODO: isdigit() is locale sensitive */
2324 for (i = 0;
2325 i < subsecond_resolution && isdigit(*offset);
2326 i++, offset++) {
2327 subseconds[i] = *offset;
2329 if (subsecond_resolution == i && isdigit(*offset)) {
2330 /* Check 7th digit for rounding */
2331 if (*offset >= '5') round_up = 1;
2332 offset++;
2334 for (; i < subsecond_resolution; i++) {
2335 subseconds[i] = '0';
2337 subseconds[subsecond_resolution] = '\0';
2339 /* Convert it into integer */
2340 long_number = strtol(subseconds, &endptr, 10);
2341 if (*endptr != '\0' || long_number == LONG_MIN ||
2342 long_number == LONG_MAX) {
2343 zfree(*time);
2344 return IE_DATE;
2346 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2347 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2348 * microseconds" and "the type shall be a signed integer capable of
2349 * storing values at least in the range [-1, 1000000]. */
2350 if (long_number < -1 || long_number >= 1000000) {
2351 zfree(*time);
2352 return IE_DATE;
2354 (*time)->tv_usec = long_number;
2356 /* Round the subseconds */
2357 if (round_up) {
2358 if (999999 == (*time)->tv_usec) {
2359 (*time)->tv_usec = 0;
2360 broken.tm_sec++;
2361 } else {
2362 (*time)->tv_usec++;
2366 /* move to the zone offset delimiter or signal NULL*/
2367 delim = strchr(offset, '-');
2368 if (!delim)
2369 delim = strchr(offset, '+');
2370 if (!delim)
2371 delim = strchr(offset, 'Z');
2372 offset = delim;
2375 /* Get zone offset */
2376 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2377 * "" equals to "Z" and it means UTC zone. */
2378 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2379 * colon separator */
2380 if (offset && (*offset == '-' || *offset == '+')) {
2381 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2382 zfree(*time);
2383 return IE_DATE;
2385 if (*offset == '+') {
2386 broken.tm_hour -= offset_hours;
2387 broken.tm_min -= offset_minutes;
2388 } else {
2389 broken.tm_hour += offset_hours;
2390 broken.tm_min += offset_minutes;
2394 /* Convert to time_t */
2395 (*time)->tv_sec = _isds_timegm(&broken);
2396 if ((*time)->tv_sec == (time_t) -1) {
2397 zfree(*time);
2398 return IE_DATE;
2401 return IE_SUCCESS;
2405 /* Convert unsigned int into isds_message_status.
2406 * @context is session context
2407 * @number is pointer to number value. NULL will be treated as invalid value.
2408 * @status is automatically reallocated status
2409 * @return IE_SUCCESS, or error code and free status */
2410 static isds_error uint2isds_message_status(struct isds_ctx *context,
2411 const unsigned long int *number, isds_message_status **status) {
2412 if (!context) return IE_INVALID_CONTEXT;
2413 if (!status) return IE_INVAL;
2415 free(*status); *status = NULL;
2416 if (!number) return IE_INVAL;
2418 if (*number < 1 || *number > 10) {
2419 isds_printf_message(context, _("Invalid message status value: %lu"),
2420 *number);
2421 return IE_ENUM;
2424 *status = malloc(sizeof(**status));
2425 if (!*status) return IE_NOMEM;
2427 **status = 1 << *number;
2428 return IE_SUCCESS;
2432 /* Convert event description string into isds_event members type and
2433 * description
2434 * @string is raw event description starting with event prefix
2435 * @event is structure where to store type and stripped description to
2436 * @return standard error code, unknown prefix is not classified as an error.
2437 * */
2438 static isds_error eventstring2event(const xmlChar *string,
2439 struct isds_event* event) {
2440 const xmlChar *known_prefixes[] = {
2441 BAD_CAST "EV0:",
2442 BAD_CAST "EV1:",
2443 BAD_CAST "EV2:",
2444 BAD_CAST "EV3:",
2445 BAD_CAST "EV4:",
2446 BAD_CAST "EV5:",
2447 BAD_CAST "EV11:",
2448 BAD_CAST "EV12:",
2449 BAD_CAST "EV13:"
2451 const isds_event_type types[] = {
2452 EVENT_ENTERED_SYSTEM,
2453 EVENT_ACCEPTED_BY_RECIPIENT,
2454 EVENT_ACCEPTED_BY_FICTION,
2455 EVENT_UNDELIVERABLE,
2456 EVENT_COMMERCIAL_ACCEPTED,
2457 EVENT_DELIVERED,
2458 EVENT_PRIMARY_LOGIN,
2459 EVENT_ENTRUSTED_LOGIN,
2460 EVENT_SYSCERT_LOGIN
2462 unsigned int index;
2463 size_t length;
2465 if (!string || !event) return IE_INVAL;
2467 if (!event->type) {
2468 event->type = malloc(sizeof(*event->type));
2469 if (!(event->type)) return IE_NOMEM;
2471 zfree(event->description);
2473 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2474 index++) {
2475 length = xmlUTF8Strlen(known_prefixes[index]);
2477 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2478 /* Prefix is known */
2479 *event->type = types[index];
2481 /* Strip prefix from description and spaces */
2482 /* TODO: Recognize all white spaces from UCS blank class and
2483 * operate on UTF-8 chars. */
2484 for (; string[length] != '\0' && string[length] == ' '; length++);
2485 event->description = strdup((char *) (string + length));
2486 if (!(event->description)) return IE_NOMEM;
2488 return IE_SUCCESS;
2492 /* Unknown event prefix.
2493 * XSD allows any string */
2494 char *string_locale = _isds_utf82locale((char *) string);
2495 isds_log(ILF_ISDS, ILL_WARNING,
2496 _("Unknown delivery info event prefix: %s\n"), string_locale);
2497 free(string_locale);
2499 *event->type = EVENT_UKNOWN;
2500 event->description = strdup((char *) string);
2501 if (!(event->description)) return IE_NOMEM;
2503 return IE_SUCCESS;
2507 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2508 * and leave label */
2509 #define EXTRACT_STRING(element, string) { \
2510 xmlXPathFreeObject(result); \
2511 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2512 if (NULL == (result)) { \
2513 err = IE_ERROR; \
2514 goto leave; \
2516 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2517 if (result->nodesetval->nodeNr > 1) { \
2518 isds_printf_message(context, _("Multiple %s element"), element); \
2519 err = IE_ERROR; \
2520 goto leave; \
2522 (string) = (char *) \
2523 xmlXPathCastNodeSetToString(result->nodesetval); \
2524 if (NULL == (string)) { \
2525 err = IE_ERROR; \
2526 goto leave; \
2531 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2533 char *string = NULL; \
2534 EXTRACT_STRING(element, string); \
2536 if (string) { \
2537 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2538 if (!(booleanPtr)) { \
2539 free(string); \
2540 err = IE_NOMEM; \
2541 goto leave; \
2544 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2545 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2546 *(booleanPtr) = 1; \
2547 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2548 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2549 *(booleanPtr) = 0; \
2550 else { \
2551 char *string_locale = _isds_utf82locale((char*)string); \
2552 isds_printf_message(context, \
2553 _("%s value is not valid boolean: %s"), \
2554 element, string_locale); \
2555 free(string_locale); \
2556 free(string); \
2557 err = IE_ERROR; \
2558 goto leave; \
2561 free(string); \
2565 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2567 char *string = NULL; \
2568 EXTRACT_STRING(element, string); \
2570 if (NULL == string) { \
2571 isds_printf_message(context, _("%s element is empty"), element); \
2572 err = IE_ERROR; \
2573 goto leave; \
2575 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2576 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2577 (boolean) = 1; \
2578 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2579 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2580 (boolean) = 0; \
2581 else { \
2582 char *string_locale = _isds_utf82locale((char*)string); \
2583 isds_printf_message(context, \
2584 _("%s value is not valid boolean: %s"), \
2585 element, string_locale); \
2586 free(string_locale); \
2587 free(string); \
2588 err = IE_ERROR; \
2589 goto leave; \
2592 free(string); \
2595 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2597 char *string = NULL; \
2598 EXTRACT_STRING(element, string); \
2599 if (string) { \
2600 long int number; \
2601 char *endptr; \
2603 number = strtol((char*)string, &endptr, 10); \
2605 if (*endptr != '\0') { \
2606 char *string_locale = _isds_utf82locale((char *)string); \
2607 isds_printf_message(context, \
2608 _("%s is not valid integer: %s"), \
2609 element, string_locale); \
2610 free(string_locale); \
2611 free(string); \
2612 err = IE_ISDS; \
2613 goto leave; \
2616 if (number == LONG_MIN || number == LONG_MAX) { \
2617 char *string_locale = _isds_utf82locale((char *)string); \
2618 isds_printf_message(context, \
2619 _("%s value out of range of long int: %s"), \
2620 element, string_locale); \
2621 free(string_locale); \
2622 free(string); \
2623 err = IE_ERROR; \
2624 goto leave; \
2627 free(string); string = NULL; \
2629 if (!(preallocated)) { \
2630 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2631 if (!(longintPtr)) { \
2632 err = IE_NOMEM; \
2633 goto leave; \
2636 *(longintPtr) = number; \
2640 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2642 char *string = NULL; \
2643 EXTRACT_STRING(element, string); \
2644 if (string) { \
2645 long int number; \
2646 char *endptr; \
2648 number = strtol((char*)string, &endptr, 10); \
2650 if (*endptr != '\0') { \
2651 char *string_locale = _isds_utf82locale((char *)string); \
2652 isds_printf_message(context, \
2653 _("%s is not valid integer: %s"), \
2654 element, string_locale); \
2655 free(string_locale); \
2656 free(string); \
2657 err = IE_ISDS; \
2658 goto leave; \
2661 if (number == LONG_MIN || number == LONG_MAX) { \
2662 char *string_locale = _isds_utf82locale((char *)string); \
2663 isds_printf_message(context, \
2664 _("%s value out of range of long int: %s"), \
2665 element, string_locale); \
2666 free(string_locale); \
2667 free(string); \
2668 err = IE_ERROR; \
2669 goto leave; \
2672 free(string); string = NULL; \
2673 if (number < 0) { \
2674 isds_printf_message(context, \
2675 _("%s value is negative: %ld"), element, number); \
2676 err = IE_ERROR; \
2677 goto leave; \
2680 if (!(preallocated)) { \
2681 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2682 if (!(ulongintPtr)) { \
2683 err = IE_NOMEM; \
2684 goto leave; \
2687 *(ulongintPtr) = number; \
2691 #define EXTRACT_DATE(element, tmPtr) { \
2692 char *string = NULL; \
2693 EXTRACT_STRING(element, string); \
2694 if (NULL != string) { \
2695 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2696 if (NULL == (tmPtr)) { \
2697 free(string); \
2698 err = IE_NOMEM; \
2699 goto leave; \
2701 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2702 if (err) { \
2703 if (err == IE_NOTSUP) { \
2704 err = IE_ISDS; \
2705 char *string_locale = _isds_utf82locale(string); \
2706 char *element_locale = _isds_utf82locale(element); \
2707 isds_printf_message(context, _("Invalid %s value: %s"), \
2708 element_locale, string_locale); \
2709 free(string_locale); \
2710 free(element_locale); \
2712 free(string); \
2713 goto leave; \
2715 free(string); \
2719 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2720 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2721 NULL); \
2722 if ((required) && (!string)) { \
2723 char *attribute_locale = _isds_utf82locale(attribute); \
2724 char *element_locale = \
2725 _isds_utf82locale((char *)xpath_ctx->node->name); \
2726 isds_printf_message(context, \
2727 _("Could not extract required %s attribute value from " \
2728 "%s element"), attribute_locale, element_locale); \
2729 free(element_locale); \
2730 free(attribute_locale); \
2731 err = IE_ERROR; \
2732 goto leave; \
2737 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2739 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2740 (xmlChar *) (string)); \
2741 if (!node) { \
2742 isds_printf_message(context, \
2743 _("Could not add %s child to %s element"), \
2744 element, (parent)->name); \
2745 err = IE_ERROR; \
2746 goto leave; \
2750 #define INSERT_STRING(parent, element, string) \
2751 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2753 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2755 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2756 else { INSERT_STRING(parent, element, "false"); } \
2759 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2761 if (booleanPtr) { \
2762 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2763 } else { \
2764 INSERT_STRING(parent, element, NULL); \
2768 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2769 if ((longintPtr)) { \
2770 /* FIXME: locale sensitive */ \
2771 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2772 err = IE_NOMEM; \
2773 goto leave; \
2775 INSERT_STRING(parent, element, buffer) \
2776 free(buffer); (buffer) = NULL; \
2777 } else { INSERT_STRING(parent, element, NULL) } \
2780 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2781 if ((ulongintPtr)) { \
2782 /* FIXME: locale sensitive */ \
2783 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2784 err = IE_NOMEM; \
2785 goto leave; \
2787 INSERT_STRING(parent, element, buffer) \
2788 free(buffer); (buffer) = NULL; \
2789 } else { INSERT_STRING(parent, element, NULL) } \
2792 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2794 /* FIXME: locale sensitive */ \
2795 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2796 err = IE_NOMEM; \
2797 goto leave; \
2799 INSERT_STRING(parent, element, buffer) \
2800 free(buffer); (buffer) = NULL; \
2803 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2804 * new attribute. */
2805 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2807 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2808 (xmlChar *) (string)); \
2809 if (!attribute_node) { \
2810 isds_printf_message(context, _("Could not add %s " \
2811 "attribute to %s element"), \
2812 (attribute), (parent)->name); \
2813 err = IE_ERROR; \
2814 goto leave; \
2818 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2819 if (string) { \
2820 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2821 if (length > (maximum)) { \
2822 isds_printf_message(context, \
2823 ngettext("%s has more than %d characters", \
2824 "%s has more than %d characters", (maximum)), \
2825 (name), (maximum)); \
2826 err = IE_2BIG; \
2827 goto leave; \
2829 if (length < (minimum)) { \
2830 isds_printf_message(context, \
2831 ngettext("%s has less than %d characters", \
2832 "%s has less than %d characters", (minimum)), \
2833 (name), (minimum)); \
2834 err = IE_2SMALL; \
2835 goto leave; \
2840 #define INSERT_ELEMENT(child, parent, element) \
2842 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2843 if (!(child)) { \
2844 isds_printf_message(context, \
2845 _("Could not add %s child to %s element"), \
2846 (element), (parent)->name); \
2847 err = IE_ERROR; \
2848 goto leave; \
2853 /* Find child element by name in given XPath context and switch context onto
2854 * it. The child must be uniq and must exist. Otherwise fails.
2855 * @context is ISDS context
2856 * @child is child element name
2857 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2858 * into it child. In error case, the @xpath_ctx keeps original value. */
2859 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2860 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2861 isds_error err = IE_SUCCESS;
2862 xmlXPathObjectPtr result = NULL;
2864 if (!context) return IE_INVALID_CONTEXT;
2865 if (!child || !xpath_ctx) return IE_INVAL;
2867 /* Find child */
2868 result = xmlXPathEvalExpression(child, xpath_ctx);
2869 if (!result) {
2870 err = IE_XML;
2871 goto leave;
2874 /* No match */
2875 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2876 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2877 char *child_locale = _isds_utf82locale((char*) child);
2878 isds_printf_message(context,
2879 _("%s element does not contain %s child"),
2880 parent_locale, child_locale);
2881 free(child_locale);
2882 free(parent_locale);
2883 err = IE_NOEXIST;
2884 goto leave;
2887 /* More matches */
2888 if (result->nodesetval->nodeNr > 1) {
2889 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2890 char *child_locale = _isds_utf82locale((char*) child);
2891 isds_printf_message(context,
2892 _("%s element contains multiple %s children"),
2893 parent_locale, child_locale);
2894 free(child_locale);
2895 free(parent_locale);
2896 err = IE_NOTUNIQ;
2897 goto leave;
2900 /* Switch context */
2901 xpath_ctx->node = result->nodesetval->nodeTab[0];
2903 leave:
2904 xmlXPathFreeObject(result);
2905 return err;
2910 #if HAVE_LIBCURL
2911 /* Find and convert XSD:gPersonName group in current node into structure
2912 * @context is ISDS context
2913 * @personName is automatically reallocated person name structure. If no member
2914 * value is found, will be freed.
2915 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2916 * elements
2917 * In case of error @personName will be freed. */
2918 static isds_error extract_gPersonName(struct isds_ctx *context,
2919 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2920 isds_error err = IE_SUCCESS;
2921 xmlXPathObjectPtr result = NULL;
2923 if (!context) return IE_INVALID_CONTEXT;
2924 if (!personName) return IE_INVAL;
2925 isds_PersonName_free(personName);
2926 if (!xpath_ctx) return IE_INVAL;
2929 *personName = calloc(1, sizeof(**personName));
2930 if (!*personName) {
2931 err = IE_NOMEM;
2932 goto leave;
2935 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2936 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2937 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2938 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2940 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2941 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2942 isds_PersonName_free(personName);
2944 leave:
2945 if (err) isds_PersonName_free(personName);
2946 xmlXPathFreeObject(result);
2947 return err;
2951 /* Find and convert XSD:gAddress group in current node into structure
2952 * @context is ISDS context
2953 * @address is automatically reallocated address structure. If no member
2954 * value is found, will be freed.
2955 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2956 * elements
2957 * In case of error @address will be freed. */
2958 static isds_error extract_gAddress(struct isds_ctx *context,
2959 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2960 isds_error err = IE_SUCCESS;
2961 xmlXPathObjectPtr result = NULL;
2963 if (!context) return IE_INVALID_CONTEXT;
2964 if (!address) return IE_INVAL;
2965 isds_Address_free(address);
2966 if (!xpath_ctx) return IE_INVAL;
2969 *address = calloc(1, sizeof(**address));
2970 if (!*address) {
2971 err = IE_NOMEM;
2972 goto leave;
2975 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2976 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2977 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2978 EXTRACT_STRING("isds:adNumberInMunicipality",
2979 (*address)->adNumberInMunicipality);
2980 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2981 EXTRACT_STRING("isds:adState", (*address)->adState);
2983 if (!(*address)->adCity && !(*address)->adStreet &&
2984 !(*address)->adNumberInStreet &&
2985 !(*address)->adNumberInMunicipality &&
2986 !(*address)->adZipCode && !(*address)->adState)
2987 isds_Address_free(address);
2989 leave:
2990 if (err) isds_Address_free(address);
2991 xmlXPathFreeObject(result);
2992 return err;
2996 /* Find and convert isds:biDate element in current node into structure
2997 * @context is ISDS context
2998 * @biDate is automatically reallocated birth date structure. If no member
2999 * value is found, will be freed.
3000 * @xpath_ctx is XPath context with current node as parent for isds:biDate
3001 * element
3002 * In case of error @biDate will be freed. */
3003 static isds_error extract_BiDate(struct isds_ctx *context,
3004 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
3005 isds_error err = IE_SUCCESS;
3006 xmlXPathObjectPtr result = NULL;
3007 char *string = NULL;
3009 if (!context) return IE_INVALID_CONTEXT;
3010 if (!biDate) return IE_INVAL;
3011 zfree(*biDate);
3012 if (!xpath_ctx) return IE_INVAL;
3014 EXTRACT_STRING("isds:biDate", string);
3015 if (string) {
3016 *biDate = calloc(1, sizeof(**biDate));
3017 if (!*biDate) {
3018 err = IE_NOMEM;
3019 goto leave;
3021 err = _isds_datestring2tm((xmlChar *)string, *biDate);
3022 if (err) {
3023 if (err == IE_NOTSUP) {
3024 err = IE_ISDS;
3025 char *string_locale = _isds_utf82locale(string);
3026 isds_printf_message(context,
3027 _("Invalid isds:biDate value: %s"), string_locale);
3028 free(string_locale);
3030 goto leave;
3034 leave:
3035 if (err) zfree(*biDate);
3036 free(string);
3037 xmlXPathFreeObject(result);
3038 return err;
3042 /* Convert isds:dBOwnerInfo XML tree into structure
3043 * @context is ISDS context
3044 * @db_owner_info is automatically reallocated box owner info structure
3045 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
3046 * In case of error @db_owner_info will be freed. */
3047 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
3048 struct isds_DbOwnerInfo **db_owner_info,
3049 xmlXPathContextPtr xpath_ctx) {
3050 isds_error err = IE_SUCCESS;
3051 xmlXPathObjectPtr result = NULL;
3052 char *string = NULL;
3054 if (!context) return IE_INVALID_CONTEXT;
3055 if (!db_owner_info) return IE_INVAL;
3056 isds_DbOwnerInfo_free(db_owner_info);
3057 if (!xpath_ctx) return IE_INVAL;
3060 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3061 if (!*db_owner_info) {
3062 err = IE_NOMEM;
3063 goto leave;
3066 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
3068 EXTRACT_STRING("isds:dbType", string);
3069 if (string) {
3070 (*db_owner_info)->dbType =
3071 calloc(1, sizeof(*((*db_owner_info)->dbType)));
3072 if (!(*db_owner_info)->dbType) {
3073 err = IE_NOMEM;
3074 goto leave;
3076 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
3077 if (err) {
3078 zfree((*db_owner_info)->dbType);
3079 if (err == IE_ENUM) {
3080 err = IE_ISDS;
3081 char *string_locale = _isds_utf82locale(string);
3082 isds_printf_message(context, _("Unknown isds:dbType: %s"),
3083 string_locale);
3084 free(string_locale);
3086 goto leave;
3088 zfree(string);
3091 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
3093 err = extract_gPersonName(context, &(*db_owner_info)->personName,
3094 xpath_ctx);
3095 if (err) goto leave;
3097 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
3099 (*db_owner_info)->birthInfo =
3100 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
3101 if (!(*db_owner_info)->birthInfo) {
3102 err = IE_NOMEM;
3103 goto leave;
3105 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
3106 xpath_ctx);
3107 if (err) goto leave;
3108 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3109 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3110 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3111 if (!(*db_owner_info)->birthInfo->biDate &&
3112 !(*db_owner_info)->birthInfo->biCity &&
3113 !(*db_owner_info)->birthInfo->biCounty &&
3114 !(*db_owner_info)->birthInfo->biState)
3115 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3117 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3118 if (err) goto leave;
3120 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3121 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3122 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3123 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3124 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3126 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3128 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3129 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3130 (*db_owner_info)->dbOpenAddressing);
3132 leave:
3133 if (err) isds_DbOwnerInfo_free(db_owner_info);
3134 free(string);
3135 xmlXPathFreeObject(result);
3136 return err;
3140 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3141 * @context is session context
3142 * @owner is libisds structure with box description
3143 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3144 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3145 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
3147 isds_error err = IE_SUCCESS;
3148 xmlNodePtr node;
3149 xmlChar *string = NULL;
3151 if (!context) return IE_INVALID_CONTEXT;
3152 if (!owner || !db_owner_info) return IE_INVAL;
3155 /* Build XSD:tDbOwnerInfo */
3156 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3157 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3159 /* dbType */
3160 if (owner->dbType) {
3161 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
3162 if (!type_string) {
3163 isds_printf_message(context, _("Invalid dbType value: %d"),
3164 *(owner->dbType));
3165 err = IE_ENUM;
3166 goto leave;
3168 INSERT_STRING(db_owner_info, "dbType", type_string);
3170 INSERT_STRING(db_owner_info, "ic", owner->ic);
3171 if (owner->personName) {
3172 INSERT_STRING(db_owner_info, "pnFirstName",
3173 owner->personName->pnFirstName);
3174 INSERT_STRING(db_owner_info, "pnMiddleName",
3175 owner->personName->pnMiddleName);
3176 INSERT_STRING(db_owner_info, "pnLastName",
3177 owner->personName->pnLastName);
3178 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3179 owner->personName->pnLastNameAtBirth);
3181 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3182 if (owner->birthInfo) {
3183 if (owner->birthInfo->biDate) {
3184 if (!tm2datestring(owner->birthInfo->biDate, &string))
3185 INSERT_STRING(db_owner_info, "biDate", string);
3186 free(string); string = NULL;
3188 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
3189 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
3190 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
3192 if (owner->address) {
3193 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
3194 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
3195 INSERT_STRING(db_owner_info, "adNumberInStreet",
3196 owner->address->adNumberInStreet);
3197 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3198 owner->address->adNumberInMunicipality);
3199 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
3200 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
3202 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3203 INSERT_STRING(db_owner_info, "email", owner->email);
3204 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3206 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3207 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3209 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3210 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3212 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3214 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3215 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3216 owner->dbOpenAddressing);
3218 leave:
3219 free(string);
3220 return err;
3224 /* Convert XSD:tDbUserInfo XML tree into structure
3225 * @context is ISDS context
3226 * @db_user_info is automatically reallocated user info structure
3227 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3228 * In case of error @db_user_info will be freed. */
3229 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3230 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3231 isds_error err = IE_SUCCESS;
3232 xmlXPathObjectPtr result = NULL;
3233 char *string = NULL;
3235 if (!context) return IE_INVALID_CONTEXT;
3236 if (!db_user_info) return IE_INVAL;
3237 isds_DbUserInfo_free(db_user_info);
3238 if (!xpath_ctx) return IE_INVAL;
3241 *db_user_info = calloc(1, sizeof(**db_user_info));
3242 if (!*db_user_info) {
3243 err = IE_NOMEM;
3244 goto leave;
3247 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3249 EXTRACT_STRING("isds:userType", string);
3250 if (string) {
3251 (*db_user_info)->userType =
3252 calloc(1, sizeof(*((*db_user_info)->userType)));
3253 if (!(*db_user_info)->userType) {
3254 err = IE_NOMEM;
3255 goto leave;
3257 err = string2isds_UserType((xmlChar *)string,
3258 (*db_user_info)->userType);
3259 if (err) {
3260 zfree((*db_user_info)->userType);
3261 if (err == IE_ENUM) {
3262 err = IE_ISDS;
3263 char *string_locale = _isds_utf82locale(string);
3264 isds_printf_message(context,
3265 _("Unknown isds:userType value: %s"), string_locale);
3266 free(string_locale);
3268 goto leave;
3270 zfree(string);
3273 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3275 (*db_user_info)->personName =
3276 calloc(1, sizeof(*((*db_user_info)->personName)));
3277 if (!(*db_user_info)->personName) {
3278 err = IE_NOMEM;
3279 goto leave;
3282 err = extract_gPersonName(context, &(*db_user_info)->personName,
3283 xpath_ctx);
3284 if (err) goto leave;
3286 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3287 if (err) goto leave;
3289 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3290 if (err) goto leave;
3292 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3293 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3295 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3296 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3297 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3299 /* ???: Default value is "CZ" according specification. Should we provide
3300 * it? */
3301 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3303 leave:
3304 if (err) isds_DbUserInfo_free(db_user_info);
3305 free(string);
3306 xmlXPathFreeObject(result);
3307 return err;
3311 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3312 * @context is session context
3313 * @user is libisds structure with user description
3314 * @db_user_info is XML element of XSD:tDbUserInfo */
3315 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3316 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3318 isds_error err = IE_SUCCESS;
3319 xmlNodePtr node;
3320 xmlChar *string = NULL;
3322 if (!context) return IE_INVALID_CONTEXT;
3323 if (!user || !db_user_info) return IE_INVAL;
3325 /* Build XSD:tDbUserInfo */
3326 if (user->personName) {
3327 INSERT_STRING(db_user_info, "pnFirstName",
3328 user->personName->pnFirstName);
3329 INSERT_STRING(db_user_info, "pnMiddleName",
3330 user->personName->pnMiddleName);
3331 INSERT_STRING(db_user_info, "pnLastName",
3332 user->personName->pnLastName);
3333 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3334 user->personName->pnLastNameAtBirth);
3336 if (user->address) {
3337 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3338 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3339 INSERT_STRING(db_user_info, "adNumberInStreet",
3340 user->address->adNumberInStreet);
3341 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3342 user->address->adNumberInMunicipality);
3343 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3344 INSERT_STRING(db_user_info, "adState", user->address->adState);
3346 if (user->biDate) {
3347 if (!tm2datestring(user->biDate, &string))
3348 INSERT_STRING(db_user_info, "biDate", string);
3349 zfree(string);
3351 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3352 INSERT_STRING(db_user_info, "userID", user->userID);
3354 /* userType */
3355 if (user->userType) {
3356 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3357 if (!type_string) {
3358 isds_printf_message(context, _("Invalid userType value: %d"),
3359 *(user->userType));
3360 err = IE_ENUM;
3361 goto leave;
3363 INSERT_STRING(db_user_info, "userType", type_string);
3366 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3367 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3368 INSERT_STRING(db_user_info, "ic", user->ic);
3369 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3370 INSERT_STRING(db_user_info, "firmName", user->firmName);
3371 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3372 INSERT_STRING(db_user_info, "caCity", user->caCity);
3373 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3374 INSERT_STRING(db_user_info, "caState", user->caState);
3376 leave:
3377 free(string);
3378 return err;
3382 /* Convert XSD:tPDZRec XML tree into structure
3383 * @context is ISDS context
3384 * @permission is automatically reallocated commercial permission structure
3385 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3386 * In case of error @permission will be freed. */
3387 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3388 struct isds_commercial_permission **permission,
3389 xmlXPathContextPtr xpath_ctx) {
3390 isds_error err = IE_SUCCESS;
3391 xmlXPathObjectPtr result = NULL;
3392 char *string = NULL;
3394 if (!context) return IE_INVALID_CONTEXT;
3395 if (!permission) return IE_INVAL;
3396 isds_commercial_permission_free(permission);
3397 if (!xpath_ctx) return IE_INVAL;
3400 *permission = calloc(1, sizeof(**permission));
3401 if (!*permission) {
3402 err = IE_NOMEM;
3403 goto leave;
3406 EXTRACT_STRING("isds:PDZType", string);
3407 if (string) {
3408 err = string2isds_payment_type((xmlChar *)string,
3409 &(*permission)->type);
3410 if (err) {
3411 if (err == IE_ENUM) {
3412 err = IE_ISDS;
3413 char *string_locale = _isds_utf82locale(string);
3414 isds_printf_message(context,
3415 _("Unknown isds:PDZType value: %s"), string_locale);
3416 free(string_locale);
3418 goto leave;
3420 zfree(string);
3423 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3424 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3426 EXTRACT_STRING("isds:PDZExpire", string);
3427 if (string) {
3428 err = timestring2timeval((xmlChar *) string,
3429 &((*permission)->expiration));
3430 if (err) {
3431 char *string_locale = _isds_utf82locale(string);
3432 if (err == IE_DATE) err = IE_ISDS;
3433 isds_printf_message(context,
3434 _("Could not convert PDZExpire as ISO time: %s"),
3435 string_locale);
3436 free(string_locale);
3437 goto leave;
3439 zfree(string);
3442 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3443 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3445 leave:
3446 if (err) isds_commercial_permission_free(permission);
3447 free(string);
3448 xmlXPathFreeObject(result);
3449 return err;
3453 /* Convert XSD:tCiRecord XML tree into structure
3454 * @context is ISDS context
3455 * @event is automatically reallocated commercial credit event structure
3456 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3457 * In case of error @event will be freed. */
3458 static isds_error extract_CiRecord(struct isds_ctx *context,
3459 struct isds_credit_event **event,
3460 xmlXPathContextPtr xpath_ctx) {
3461 isds_error err = IE_SUCCESS;
3462 xmlXPathObjectPtr result = NULL;
3463 char *string = NULL;
3464 long int *number_ptr;
3466 if (!context) return IE_INVALID_CONTEXT;
3467 if (!event) return IE_INVAL;
3468 isds_credit_event_free(event);
3469 if (!xpath_ctx) return IE_INVAL;
3472 *event = calloc(1, sizeof(**event));
3473 if (!*event) {
3474 err = IE_NOMEM;
3475 goto leave;
3478 EXTRACT_STRING("isds:ciEventTime", string);
3479 if (string) {
3480 err = timestring2timeval((xmlChar *) string,
3481 &(*event)->time);
3482 if (err) {
3483 char *string_locale = _isds_utf82locale(string);
3484 if (err == IE_DATE) err = IE_ISDS;
3485 isds_printf_message(context,
3486 _("Could not convert ciEventTime as ISO time: %s"),
3487 string_locale);
3488 free(string_locale);
3489 goto leave;
3491 zfree(string);
3494 EXTRACT_STRING("isds:ciEventType", string);
3495 if (string) {
3496 err = string2isds_credit_event_type((xmlChar *)string,
3497 &(*event)->type);
3498 if (err) {
3499 if (err == IE_ENUM) {
3500 err = IE_ISDS;
3501 char *string_locale = _isds_utf82locale(string);
3502 isds_printf_message(context,
3503 _("Unknown isds:ciEventType value: %s"), string_locale);
3504 free(string_locale);
3506 goto leave;
3508 zfree(string);
3511 number_ptr = &((*event)->credit_change);
3512 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3513 number_ptr = &(*event)->new_credit;
3514 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3516 switch((*event)->type) {
3517 case ISDS_CREDIT_CHARGED:
3518 EXTRACT_STRING("isds:ciTransID",
3519 (*event)->details.charged.transaction);
3520 break;
3521 case ISDS_CREDIT_DISCHARGED:
3522 EXTRACT_STRING("isds:ciTransID",
3523 (*event)->details.discharged.transaction);
3524 break;
3525 case ISDS_CREDIT_MESSAGE_SENT:
3526 EXTRACT_STRING("isds:ciRecipientID",
3527 (*event)->details.message_sent.recipient);
3528 EXTRACT_STRING("isds:ciPDZID",
3529 (*event)->details.message_sent.message_id);
3530 break;
3531 case ISDS_CREDIT_STORAGE_SET:
3532 number_ptr = &((*event)->details.storage_set.new_capacity);
3533 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3534 EXTRACT_DATE("isds:ciNewFrom",
3535 (*event)->details.storage_set.new_valid_from);
3536 EXTRACT_DATE("isds:ciNewTo",
3537 (*event)->details.storage_set.new_valid_to);
3538 EXTRACT_LONGINT("isds:ciOldCapacity",
3539 (*event)->details.storage_set.old_capacity, 0);
3540 EXTRACT_DATE("isds:ciOldFrom",
3541 (*event)->details.storage_set.old_valid_from);
3542 EXTRACT_DATE("isds:ciOldTo",
3543 (*event)->details.storage_set.old_valid_to);
3544 EXTRACT_STRING("isds:ciDoneBy",
3545 (*event)->details.storage_set.initiator);
3546 break;
3547 case ISDS_CREDIT_EXPIRED:
3548 break;
3551 leave:
3552 if (err) isds_credit_event_free(event);
3553 free(string);
3554 xmlXPathFreeObject(result);
3555 return err;
3559 #endif /* HAVE_LIBCURL */
3562 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3563 * isds_envelope structure. The envelope is automatically allocated but not
3564 * reallocated. The date are just appended into envelope structure.
3565 * @context is ISDS context
3566 * @envelope is automatically allocated message envelope structure
3567 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3568 * In case of error @envelope will be freed. */
3569 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3570 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3571 isds_error err = IE_SUCCESS;
3572 xmlXPathObjectPtr result = NULL;
3574 if (!context) return IE_INVALID_CONTEXT;
3575 if (!envelope) return IE_INVAL;
3576 if (!xpath_ctx) return IE_INVAL;
3579 if (!*envelope) {
3580 /* Allocate envelope */
3581 *envelope = calloc(1, sizeof(**envelope));
3582 if (!*envelope) {
3583 err = IE_NOMEM;
3584 goto leave;
3586 } else {
3587 /* Else free former data */
3588 zfree((*envelope)->dmSenderOrgUnit);
3589 zfree((*envelope)->dmSenderOrgUnitNum);
3590 zfree((*envelope)->dbIDRecipient);
3591 zfree((*envelope)->dmRecipientOrgUnit);
3592 zfree((*envelope)->dmRecipientOrgUnitNum);
3593 zfree((*envelope)->dmToHands);
3594 zfree((*envelope)->dmAnnotation);
3595 zfree((*envelope)->dmRecipientRefNumber);
3596 zfree((*envelope)->dmSenderRefNumber);
3597 zfree((*envelope)->dmRecipientIdent);
3598 zfree((*envelope)->dmSenderIdent);
3599 zfree((*envelope)->dmLegalTitleLaw);
3600 zfree((*envelope)->dmLegalTitleYear);
3601 zfree((*envelope)->dmLegalTitleSect);
3602 zfree((*envelope)->dmLegalTitlePar);
3603 zfree((*envelope)->dmLegalTitlePoint);
3604 zfree((*envelope)->dmPersonalDelivery);
3605 zfree((*envelope)->dmAllowSubstDelivery);
3608 /* Extract envelope elements added by sender or ISDS
3609 * (XSD: gMessageEnvelopeSub type) */
3610 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3611 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3612 (*envelope)->dmSenderOrgUnitNum, 0);
3613 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3614 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3615 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3616 (*envelope)->dmRecipientOrgUnitNum, 0);
3617 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3618 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3619 EXTRACT_STRING("isds:dmRecipientRefNumber",
3620 (*envelope)->dmRecipientRefNumber);
3621 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3622 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3623 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3625 /* Extract envelope elements regarding law reference */
3626 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3627 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3628 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3629 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3630 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3632 /* Extract envelope other elements */
3633 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3634 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3635 (*envelope)->dmAllowSubstDelivery);
3637 leave:
3638 if (err) isds_envelope_free(envelope);
3639 xmlXPathFreeObject(result);
3640 return err;
3645 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3646 * isds_envelope structure. The envelope is automatically allocated but not
3647 * reallocated. The date are just appended into envelope structure.
3648 * @context is ISDS context
3649 * @envelope is automatically allocated message envelope structure
3650 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3651 * In case of error @envelope will be freed. */
3652 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3653 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3654 isds_error err = IE_SUCCESS;
3655 xmlXPathObjectPtr result = NULL;
3657 if (!context) return IE_INVALID_CONTEXT;
3658 if (!envelope) return IE_INVAL;
3659 if (!xpath_ctx) return IE_INVAL;
3662 if (!*envelope) {
3663 /* Allocate envelope */
3664 *envelope = calloc(1, sizeof(**envelope));
3665 if (!*envelope) {
3666 err = IE_NOMEM;
3667 goto leave;
3669 } else {
3670 /* Else free former data */
3671 zfree((*envelope)->dmID);
3672 zfree((*envelope)->dbIDSender);
3673 zfree((*envelope)->dmSender);
3674 zfree((*envelope)->dmSenderAddress);
3675 zfree((*envelope)->dmSenderType);
3676 zfree((*envelope)->dmRecipient);
3677 zfree((*envelope)->dmRecipientAddress);
3678 zfree((*envelope)->dmAmbiguousRecipient);
3681 /* Extract envelope elements added by ISDS
3682 * (XSD: gMessageEnvelope type) */
3683 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3684 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3685 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3686 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3687 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3688 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3689 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3690 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3691 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3692 (*envelope)->dmAmbiguousRecipient);
3694 /* Extract envelope elements added by sender and ISDS
3695 * (XSD: gMessageEnvelope type) */
3696 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3697 if (err) goto leave;
3699 leave:
3700 if (err) isds_envelope_free(envelope);
3701 xmlXPathFreeObject(result);
3702 return err;
3706 /* Convert other envelope elements from XML tree into isds_envelope structure:
3707 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3708 * The envelope is automatically allocated but not reallocated.
3709 * The data are just appended into envelope structure.
3710 * @context is ISDS context
3711 * @envelope is automatically allocated message envelope structure
3712 * @xpath_ctx is XPath context with current node as parent desired elements
3713 * In case of error @envelope will be freed. */
3714 static isds_error append_status_size_times(struct isds_ctx *context,
3715 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3716 isds_error err = IE_SUCCESS;
3717 xmlXPathObjectPtr result = NULL;
3718 char *string = NULL;
3719 unsigned long int *unumber = NULL;
3721 if (!context) return IE_INVALID_CONTEXT;
3722 if (!envelope) return IE_INVAL;
3723 if (!xpath_ctx) return IE_INVAL;
3726 if (!*envelope) {
3727 /* Allocate new */
3728 *envelope = calloc(1, sizeof(**envelope));
3729 if (!*envelope) {
3730 err = IE_NOMEM;
3731 goto leave;
3733 } else {
3734 /* Free old data */
3735 zfree((*envelope)->dmMessageStatus);
3736 zfree((*envelope)->dmAttachmentSize);
3737 zfree((*envelope)->dmDeliveryTime);
3738 zfree((*envelope)->dmAcceptanceTime);
3742 /* dmMessageStatus element is mandatory */
3743 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3744 if (!unumber) {
3745 isds_log_message(context,
3746 _("Missing mandatory sisds:dmMessageStatus integer"));
3747 err = IE_ISDS;
3748 goto leave;
3750 err = uint2isds_message_status(context, unumber,
3751 &((*envelope)->dmMessageStatus));
3752 if (err) {
3753 if (err == IE_ENUM) err = IE_ISDS;
3754 goto leave;
3756 free(unumber); unumber = NULL;
3758 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3761 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3762 if (string) {
3763 err = timestring2timeval((xmlChar *) string,
3764 &((*envelope)->dmDeliveryTime));
3765 if (err) {
3766 char *string_locale = _isds_utf82locale(string);
3767 if (err == IE_DATE) err = IE_ISDS;
3768 isds_printf_message(context,
3769 _("Could not convert dmDeliveryTime as ISO time: %s"),
3770 string_locale);
3771 free(string_locale);
3772 goto leave;
3774 zfree(string);
3777 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3778 if (string) {
3779 err = timestring2timeval((xmlChar *) string,
3780 &((*envelope)->dmAcceptanceTime));
3781 if (err) {
3782 char *string_locale = _isds_utf82locale(string);
3783 if (err == IE_DATE) err = IE_ISDS;
3784 isds_printf_message(context,
3785 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3786 string_locale);
3787 free(string_locale);
3788 goto leave;
3790 zfree(string);
3793 leave:
3794 if (err) isds_envelope_free(envelope);
3795 free(unumber);
3796 free(string);
3797 xmlXPathFreeObject(result);
3798 return err;
3802 /* Convert message type attribute of current element into isds_envelope
3803 * structure.
3804 * TODO: This function can be incorporated into append_status_size_times() as
3805 * they are called always together.
3806 * The envelope is automatically allocated but not reallocated.
3807 * The data are just appended into envelope structure.
3808 * @context is ISDS context
3809 * @envelope is automatically allocated message envelope structure
3810 * @xpath_ctx is XPath context with current node as parent of attribute
3811 * carrying message type
3812 * In case of error @envelope will be freed. */
3813 static isds_error append_message_type(struct isds_ctx *context,
3814 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3815 isds_error err = IE_SUCCESS;
3817 if (!context) return IE_INVALID_CONTEXT;
3818 if (!envelope) return IE_INVAL;
3819 if (!xpath_ctx) return IE_INVAL;
3822 if (!*envelope) {
3823 /* Allocate new */
3824 *envelope = calloc(1, sizeof(**envelope));
3825 if (!*envelope) {
3826 err = IE_NOMEM;
3827 goto leave;
3829 } else {
3830 /* Free old data */
3831 zfree((*envelope)->dmType);
3835 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3837 if (!(*envelope)->dmType) {
3838 /* Use default value */
3839 (*envelope)->dmType = strdup("V");
3840 if (!(*envelope)->dmType) {
3841 err = IE_NOMEM;
3842 goto leave;
3844 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3845 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3846 isds_printf_message(context,
3847 _("Message type in dmType attribute is not 1 character long: "
3848 "%s"),
3849 type_locale);
3850 free(type_locale);
3851 err = IE_ISDS;
3852 goto leave;
3855 leave:
3856 if (err) isds_envelope_free(envelope);
3857 return err;
3861 #if HAVE_LIBCURL
3862 /* Convert dmType isds_envelope member into XML attribute and append it to
3863 * current node.
3864 * @context is ISDS context
3865 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3866 * @dm_envelope is XML element the resulting attribute will be appended to.
3867 * @return error code, in case of error context' message is filled. */
3868 static isds_error insert_message_type(struct isds_ctx *context,
3869 const char *type, xmlNodePtr dm_envelope) {
3870 isds_error err = IE_SUCCESS;
3871 xmlAttrPtr attribute_node;
3873 if (!context) return IE_INVALID_CONTEXT;
3874 if (!dm_envelope) return IE_INVAL;
3876 /* Insert optional message type */
3877 if (type) {
3878 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3879 char *type_locale = _isds_utf82locale(type);
3880 isds_printf_message(context,
3881 _("Message type in envelope is not 1 character long: %s"),
3882 type_locale);
3883 free(type_locale);
3884 err = IE_INVAL;
3885 goto leave;
3887 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3890 leave:
3891 return err;
3893 #endif /* HAVE_LIBCURL */
3896 /* Extract message document into reallocated document structure
3897 * @context is ISDS context
3898 * @document is automatically reallocated message documents structure
3899 * @xpath_ctx is XPath context with current node as isds:dmFile
3900 * In case of error @document will be freed. */
3901 static isds_error extract_document(struct isds_ctx *context,
3902 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3903 isds_error err = IE_SUCCESS;
3904 xmlXPathObjectPtr result = NULL;
3905 xmlNodePtr file_node;
3906 char *string = NULL;
3908 if (!context) return IE_INVALID_CONTEXT;
3909 if (!document) return IE_INVAL;
3910 isds_document_free(document);
3911 if (!xpath_ctx) return IE_INVAL;
3912 file_node = xpath_ctx->node;
3914 *document = calloc(1, sizeof(**document));
3915 if (!*document) {
3916 err = IE_NOMEM;
3917 goto leave;
3920 /* Extract document meta data */
3921 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3922 if (context->normalize_mime_type) {
3923 const char *normalized_type =
3924 isds_normalize_mime_type((*document)->dmMimeType);
3925 if (NULL != normalized_type &&
3926 normalized_type != (*document)->dmMimeType) {
3927 char *new_type = strdup(normalized_type);
3928 if (NULL == new_type) {
3929 isds_printf_message(context,
3930 _("Not enough memory to normalize document MIME type"));
3931 err = IE_NOMEM;
3932 goto leave;
3934 free((*document)->dmMimeType);
3935 (*document)->dmMimeType = new_type;
3939 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3940 err = string2isds_FileMetaType((xmlChar*)string,
3941 &((*document)->dmFileMetaType));
3942 if (err) {
3943 char *meta_type_locale = _isds_utf82locale(string);
3944 isds_printf_message(context,
3945 _("Document has invalid dmFileMetaType attribute value: %s"),
3946 meta_type_locale);
3947 free(meta_type_locale);
3948 err = IE_ISDS;
3949 goto leave;
3951 zfree(string);
3953 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3954 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3955 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3956 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3959 /* Extract document data.
3960 * Base64 encoded blob or XML subtree must be presented. */
3962 /* Check for dmEncodedContent */
3963 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3964 xpath_ctx);
3965 if (!result) {
3966 err = IE_XML;
3967 goto leave;
3970 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3971 /* Here we have Base64 blob */
3972 (*document)->is_xml = 0;
3974 if (result->nodesetval->nodeNr > 1) {
3975 isds_printf_message(context,
3976 _("Document has more dmEncodedContent elements"));
3977 err = IE_ISDS;
3978 goto leave;
3981 xmlXPathFreeObject(result); result = NULL;
3982 EXTRACT_STRING("isds:dmEncodedContent", string);
3984 /* Decode non-empty document */
3985 if (string && string[0] != '\0') {
3986 (*document)->data_length =
3987 _isds_b64decode(string, &((*document)->data));
3988 if ((*document)->data_length == (size_t) -1) {
3989 isds_printf_message(context,
3990 _("Error while Base64-decoding document content"));
3991 err = IE_ERROR;
3992 goto leave;
3995 } else {
3996 /* No Base64 blob, try XML document */
3997 xmlXPathFreeObject(result); result = NULL;
3998 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3999 xpath_ctx);
4000 if (!result) {
4001 err = IE_XML;
4002 goto leave;
4005 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4006 /* Here we have XML document */
4007 (*document)->is_xml = 1;
4009 if (result->nodesetval->nodeNr > 1) {
4010 isds_printf_message(context,
4011 _("Document has more dmXMLContent elements"));
4012 err = IE_ISDS;
4013 goto leave;
4016 /* XXX: We cannot serialize the content simply because:
4017 * - XML document may point out of its scope (e.g. to message
4018 * envelope)
4019 * - isds:dmXMLContent can contain more elements, no element,
4020 * a text node only
4021 * - it's not the XML way
4022 * Thus we provide the only right solution: XML DOM. Let's
4023 * application to cope with this hot potato :) */
4024 (*document)->xml_node_list =
4025 result->nodesetval->nodeTab[0]->children;
4026 } else {
4027 /* No base64 blob, nor XML document */
4028 isds_printf_message(context,
4029 _("Document has no dmEncodedContent, nor dmXMLContent "
4030 "element"));
4031 err = IE_ISDS;
4032 goto leave;
4037 leave:
4038 if (err) isds_document_free(document);
4039 free(string);
4040 xmlXPathFreeObject(result);
4041 xpath_ctx->node = file_node;
4042 return err;
4047 /* Extract message documents into reallocated list of documents
4048 * @context is ISDS context
4049 * @documents is automatically reallocated message documents list structure
4050 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4051 * In case of error @documents will be freed. */
4052 static isds_error extract_documents(struct isds_ctx *context,
4053 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
4054 isds_error err = IE_SUCCESS;
4055 xmlXPathObjectPtr result = NULL;
4056 xmlNodePtr files_node;
4057 struct isds_list *document, *prev_document = NULL;
4059 if (!context) return IE_INVALID_CONTEXT;
4060 if (!documents) return IE_INVAL;
4061 isds_list_free(documents);
4062 if (!xpath_ctx) return IE_INVAL;
4063 files_node = xpath_ctx->node;
4065 /* Find documents */
4066 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
4067 if (!result) {
4068 err = IE_XML;
4069 goto leave;
4072 /* No match */
4073 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4074 isds_printf_message(context,
4075 _("Message does not contain any document"));
4076 err = IE_ISDS;
4077 goto leave;
4081 /* Iterate over documents */
4082 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4084 /* Allocate and append list item */
4085 document = calloc(1, sizeof(*document));
4086 if (!document) {
4087 err = IE_NOMEM;
4088 goto leave;
4090 document->destructor = (void (*)(void **))isds_document_free;
4091 if (i == 0) *documents = document;
4092 else prev_document->next = document;
4093 prev_document = document;
4095 /* Extract document */
4096 xpath_ctx->node = result->nodesetval->nodeTab[i];
4097 err = extract_document(context,
4098 (struct isds_document **) &(document->data), xpath_ctx);
4099 if (err) goto leave;
4103 leave:
4104 if (err) isds_list_free(documents);
4105 xmlXPathFreeObject(result);
4106 xpath_ctx->node = files_node;
4107 return err;
4111 #if HAVE_LIBCURL
4112 /* Convert isds:dmRecord XML tree into structure
4113 * @context is ISDS context
4114 * @envelope is automatically reallocated message envelope structure
4115 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4116 * In case of error @envelope will be freed. */
4117 static isds_error extract_DmRecord(struct isds_ctx *context,
4118 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4119 isds_error err = IE_SUCCESS;
4120 xmlXPathObjectPtr result = NULL;
4122 if (!context) return IE_INVALID_CONTEXT;
4123 if (!envelope) return IE_INVAL;
4124 isds_envelope_free(envelope);
4125 if (!xpath_ctx) return IE_INVAL;
4128 *envelope = calloc(1, sizeof(**envelope));
4129 if (!*envelope) {
4130 err = IE_NOMEM;
4131 goto leave;
4135 /* Extract tRecord data */
4136 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4138 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4139 * dmAcceptanceTime. */
4140 err = append_status_size_times(context, envelope, xpath_ctx);
4141 if (err) goto leave;
4143 /* Extract envelope elements added by sender and ISDS
4144 * (XSD: gMessageEnvelope type) */
4145 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4146 if (err) goto leave;
4148 /* Get message type */
4149 err = append_message_type(context, envelope, xpath_ctx);
4150 if (err) goto leave;
4153 leave:
4154 if (err) isds_envelope_free(envelope);
4155 xmlXPathFreeObject(result);
4156 return err;
4160 /* Convert XSD:tStateChangesRecord type XML tree into structure
4161 * @context is ISDS context
4162 * @changed_status is automatically reallocated message state change structure
4163 * @xpath_ctx is XPath context with current node as element of
4164 * XSD:tStateChangesRecord type
4165 * In case of error @changed_status will be freed. */
4166 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4167 struct isds_message_status_change **changed_status,
4168 xmlXPathContextPtr xpath_ctx) {
4169 isds_error err = IE_SUCCESS;
4170 xmlXPathObjectPtr result = NULL;
4171 unsigned long int *unumber = NULL;
4172 char *string = NULL;
4174 if (!context) return IE_INVALID_CONTEXT;
4175 if (!changed_status) return IE_INVAL;
4176 isds_message_status_change_free(changed_status);
4177 if (!xpath_ctx) return IE_INVAL;
4180 *changed_status = calloc(1, sizeof(**changed_status));
4181 if (!*changed_status) {
4182 err = IE_NOMEM;
4183 goto leave;
4187 /* Extract tGetStateChangesInput data */
4188 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4190 /* dmEventTime is mandatory */
4191 EXTRACT_STRING("isds:dmEventTime", string);
4192 if (string) {
4193 err = timestring2timeval((xmlChar *) string,
4194 &((*changed_status)->time));
4195 if (err) {
4196 char *string_locale = _isds_utf82locale(string);
4197 if (err == IE_DATE) err = IE_ISDS;
4198 isds_printf_message(context,
4199 _("Could not convert dmEventTime as ISO time: %s"),
4200 string_locale);
4201 free(string_locale);
4202 goto leave;
4204 zfree(string);
4207 /* dmMessageStatus element is mandatory */
4208 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4209 if (!unumber) {
4210 isds_log_message(context,
4211 _("Missing mandatory isds:dmMessageStatus integer"));
4212 err = IE_ISDS;
4213 goto leave;
4215 err = uint2isds_message_status(context, unumber,
4216 &((*changed_status)->dmMessageStatus));
4217 if (err) {
4218 if (err == IE_ENUM) err = IE_ISDS;
4219 goto leave;
4221 zfree(unumber);
4224 leave:
4225 free(unumber);
4226 free(string);
4227 if (err) isds_message_status_change_free(changed_status);
4228 xmlXPathFreeObject(result);
4229 return err;
4231 #endif /* HAVE_LIBCURL */
4234 /* Find and convert isds:dmHash XML tree into structure
4235 * @context is ISDS context
4236 * @envelope is automatically reallocated message hash structure
4237 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4238 * In case of error @hash will be freed. */
4239 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4240 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4241 isds_error err = IE_SUCCESS;
4242 xmlNodePtr old_ctx_node;
4243 xmlXPathObjectPtr result = NULL;
4244 char *string = NULL;
4246 if (!context) return IE_INVALID_CONTEXT;
4247 if (!hash) return IE_INVAL;
4248 isds_hash_free(hash);
4249 if (!xpath_ctx) return IE_INVAL;
4251 old_ctx_node = xpath_ctx->node;
4253 *hash = calloc(1, sizeof(**hash));
4254 if (!*hash) {
4255 err = IE_NOMEM;
4256 goto leave;
4259 /* Locate dmHash */
4260 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4261 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4262 err = IE_ISDS;
4263 goto leave;
4265 if (err) {
4266 err = IE_ERROR;
4267 goto leave;
4270 /* Get hash algorithm */
4271 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4272 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4273 if (err) {
4274 if (err == IE_ENUM) {
4275 char *string_locale = _isds_utf82locale(string);
4276 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4277 string_locale);
4278 free(string_locale);
4280 goto leave;
4282 zfree(string);
4284 /* Get hash value */
4285 EXTRACT_STRING(".", string);
4286 if (!string) {
4287 isds_printf_message(context,
4288 _("sisds:dmHash element is missing hash value"));
4289 err = IE_ISDS;
4290 goto leave;
4292 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4293 if ((*hash)->length == (size_t) -1) {
4294 isds_printf_message(context,
4295 _("Error while Base64-decoding hash value"));
4296 err = IE_ERROR;
4297 goto leave;
4300 leave:
4301 if (err) isds_hash_free(hash);
4302 free(string);
4303 xmlXPathFreeObject(result);
4304 xpath_ctx->node = old_ctx_node;
4305 return err;
4309 /* Find and append isds:dmQTimestamp XML tree into envelope.
4310 * Because one service is allowed to miss time-stamp content, and we think
4311 * other could too (flaw in specification), this function is deliberated and
4312 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4313 * @context is ISDS context
4314 * @envelope is automatically allocated envelope structure
4315 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4316 * child
4317 * In case of error @envelope will be freed. */
4318 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4319 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4320 isds_error err = IE_SUCCESS;
4321 xmlXPathObjectPtr result = NULL;
4322 char *string = NULL;
4324 if (!context) return IE_INVALID_CONTEXT;
4325 if (!envelope) return IE_INVAL;
4326 if (!xpath_ctx) {
4327 isds_envelope_free(envelope);
4328 return IE_INVAL;
4331 if (!*envelope) {
4332 *envelope = calloc(1, sizeof(**envelope));
4333 if (!*envelope) {
4334 err = IE_NOMEM;
4335 goto leave;
4337 } else {
4338 zfree((*envelope)->timestamp);
4339 (*envelope)->timestamp_length = 0;
4342 /* Get dmQTimestamp */
4343 EXTRACT_STRING("sisds:dmQTimestamp", string);
4344 if (!string) {
4345 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4346 goto leave;
4348 (*envelope)->timestamp_length =
4349 _isds_b64decode(string, &((*envelope)->timestamp));
4350 if ((*envelope)->timestamp_length == (size_t) -1) {
4351 isds_printf_message(context,
4352 _("Error while Base64-decoding time stamp value"));
4353 err = IE_ERROR;
4354 goto leave;
4357 leave:
4358 if (err) isds_envelope_free(envelope);
4359 free(string);
4360 xmlXPathFreeObject(result);
4361 return err;
4365 /* Convert XSD tReturnedMessage XML tree into message structure.
4366 * It does not store serialized XML tree into message->raw.
4367 * It does store (pointer to) parsed XML tree into message->xml if needed.
4368 * @context is ISDS context
4369 * @include_documents Use true if documents must be extracted
4370 * (tReturnedMessage XSD type), use false if documents shall be omitted
4371 * (tReturnedMessageEnvelope).
4372 * @message is automatically reallocated message structure
4373 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4374 * type
4375 * In case of error @message will be freed. */
4376 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4377 const _Bool include_documents, struct isds_message **message,
4378 xmlXPathContextPtr xpath_ctx) {
4379 isds_error err = IE_SUCCESS;
4380 xmlNodePtr message_node;
4382 if (!context) return IE_INVALID_CONTEXT;
4383 if (!message) return IE_INVAL;
4384 isds_message_free(message);
4385 if (!xpath_ctx) return IE_INVAL;
4388 *message = calloc(1, sizeof(**message));
4389 if (!*message) {
4390 err = IE_NOMEM;
4391 goto leave;
4394 /* Save message XPATH context node */
4395 message_node = xpath_ctx->node;
4398 /* Extract dmDM */
4399 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4400 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4401 if (err) { err = IE_ERROR; goto leave; }
4402 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4403 if (err) goto leave;
4405 if (include_documents) {
4406 struct isds_list *item;
4408 /* Extract dmFiles */
4409 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4410 xpath_ctx);
4411 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4412 err = IE_ISDS; goto leave;
4414 if (err) { err = IE_ERROR; goto leave; }
4415 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4416 if (err) goto leave;
4418 /* Store xmlDoc of this message if needed */
4419 /* Only if we got a XML document in all the documents. */
4420 for (item = (*message)->documents; item; item = item->next) {
4421 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4422 (*message)->xml = xpath_ctx->doc;
4423 break;
4429 /* Restore context to message */
4430 xpath_ctx->node = message_node;
4432 /* Extract dmHash */
4433 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4434 xpath_ctx);
4435 if (err) goto leave;
4437 /* Extract dmQTimestamp, */
4438 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4439 xpath_ctx);
4440 if (err) goto leave;
4442 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4443 * dmAcceptanceTime. */
4444 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4445 if (err) goto leave;
4447 /* Get message type */
4448 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4449 if (err) goto leave;
4451 leave:
4452 if (err) isds_message_free(message);
4453 return err;
4457 /* Extract message event into reallocated isds_event structure
4458 * @context is ISDS context
4459 * @event is automatically reallocated message event structure
4460 * @xpath_ctx is XPath context with current node as isds:dmEvent
4461 * In case of error @event will be freed. */
4462 static isds_error extract_event(struct isds_ctx *context,
4463 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4464 isds_error err = IE_SUCCESS;
4465 xmlXPathObjectPtr result = NULL;
4466 xmlNodePtr event_node;
4467 char *string = NULL;
4469 if (!context) return IE_INVALID_CONTEXT;
4470 if (!event) return IE_INVAL;
4471 isds_event_free(event);
4472 if (!xpath_ctx) return IE_INVAL;
4473 event_node = xpath_ctx->node;
4475 *event = calloc(1, sizeof(**event));
4476 if (!*event) {
4477 err = IE_NOMEM;
4478 goto leave;
4481 /* Extract event data.
4482 * All elements are optional according XSD. That's funny. */
4483 EXTRACT_STRING("sisds:dmEventTime", string);
4484 if (string) {
4485 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4486 if (err) {
4487 char *string_locale = _isds_utf82locale(string);
4488 if (err == IE_DATE) err = IE_ISDS;
4489 isds_printf_message(context,
4490 _("Could not convert dmEventTime as ISO time: %s"),
4491 string_locale);
4492 free(string_locale);
4493 goto leave;
4495 zfree(string);
4498 /* dmEventDescr element has prefix and the rest */
4499 EXTRACT_STRING("sisds:dmEventDescr", string);
4500 if (string) {
4501 err = eventstring2event((xmlChar *) string, *event);
4502 if (err) goto leave;
4503 zfree(string);
4506 leave:
4507 if (err) isds_event_free(event);
4508 free(string);
4509 xmlXPathFreeObject(result);
4510 xpath_ctx->node = event_node;
4511 return err;
4515 /* Convert element of XSD tEventsArray type from XML tree into
4516 * isds_list of isds_event's structure. The list is automatically reallocated.
4517 * @context is ISDS context
4518 * @events is automatically reallocated list of event structures
4519 * @xpath_ctx is XPath context with current node as tEventsArray
4520 * In case of error @events will be freed. */
4521 static isds_error extract_events(struct isds_ctx *context,
4522 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4523 isds_error err = IE_SUCCESS;
4524 xmlXPathObjectPtr result = NULL;
4525 xmlNodePtr events_node;
4526 struct isds_list *event, *prev_event = NULL;
4528 if (!context) return IE_INVALID_CONTEXT;
4529 if (!events) return IE_INVAL;
4530 if (!xpath_ctx) return IE_INVAL;
4531 events_node = xpath_ctx->node;
4533 /* Free old list */
4534 isds_list_free(events);
4536 /* Find events */
4537 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4538 if (!result) {
4539 err = IE_XML;
4540 goto leave;
4543 /* No match */
4544 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4545 isds_printf_message(context,
4546 _("Delivery info does not contain any event"));
4547 err = IE_ISDS;
4548 goto leave;
4552 /* Iterate over events */
4553 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4555 /* Allocate and append list item */
4556 event = calloc(1, sizeof(*event));
4557 if (!event) {
4558 err = IE_NOMEM;
4559 goto leave;
4561 event->destructor = (void (*)(void **))isds_event_free;
4562 if (i == 0) *events = event;
4563 else prev_event->next = event;
4564 prev_event = event;
4566 /* Extract event */
4567 xpath_ctx->node = result->nodesetval->nodeTab[i];
4568 err = extract_event(context,
4569 (struct isds_event **) &(event->data), xpath_ctx);
4570 if (err) goto leave;
4574 leave:
4575 if (err) isds_list_free(events);
4576 xmlXPathFreeObject(result);
4577 xpath_ctx->node = events_node;
4578 return err;
4582 #if HAVE_LIBCURL
4583 /* Insert Base64 encoded data as element with text child.
4584 * @context is session context
4585 * @parent is XML node to append @element with @data as child
4586 * @ns is XML namespace of @element, use NULL to inherit from @parent
4587 * @element is UTF-8 encoded name of new element
4588 * @data is bit stream to encode into @element
4589 * @length is size of @data in bytes
4590 * @return standard error code and fill long error message if needed */
4591 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4592 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4593 const void *data, size_t length) {
4594 isds_error err = IE_SUCCESS;
4595 xmlNodePtr node;
4597 if (!context) return IE_INVALID_CONTEXT;
4598 if (!data && length > 0) return IE_INVAL;
4599 if (!parent || !element) return IE_INVAL;
4601 xmlChar *base64data = NULL;
4602 base64data = (xmlChar *) _isds_b64encode(data, length);
4603 if (!base64data) {
4604 isds_printf_message(context,
4605 ngettext("Not enough memory to encode %zd byte into Base64",
4606 "Not enough memory to encode %zd bytes into Base64",
4607 length),
4608 length);
4609 err = IE_NOMEM;
4610 goto leave;
4612 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4614 leave:
4615 free(base64data);
4616 return err;
4620 /* Convert isds_document structure into XML tree and append to dmFiles node.
4621 * @context is session context
4622 * @document is ISDS document
4623 * @dm_files is XML element the resulting tree will be appended to as a child.
4624 * @return error code, in case of error context' message is filled. */
4625 static isds_error insert_document(struct isds_ctx *context,
4626 struct isds_document *document, xmlNodePtr dm_files) {
4627 isds_error err = IE_SUCCESS;
4628 xmlNodePtr new_file = NULL, file = NULL, node;
4629 xmlAttrPtr attribute_node;
4631 if (!context) return IE_INVALID_CONTEXT;
4632 if (!document || !dm_files) return IE_INVAL;
4634 /* Allocate new dmFile */
4635 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4636 if (!new_file) {
4637 isds_printf_message(context, _("Could not allocate main dmFile"));
4638 err = IE_ERROR;
4639 goto leave;
4641 /* Append the new dmFile.
4642 * XXX: Main document must go first */
4643 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4644 file = xmlAddPrevSibling(dm_files->children, new_file);
4645 else
4646 file = xmlAddChild(dm_files, new_file);
4648 if (!file) {
4649 xmlFreeNode(new_file); new_file = NULL;
4650 isds_printf_message(context, _("Could not add dmFile child to "
4651 "%s element"), dm_files->name);
4652 err = IE_ERROR;
4653 goto leave;
4656 /* @dmMimeType is required */
4657 if (!document->dmMimeType) {
4658 isds_log_message(context,
4659 _("Document is missing mandatory MIME type definition"));
4660 err = IE_INVAL;
4661 goto leave;
4663 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4665 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4666 if (!string) {
4667 isds_printf_message(context,
4668 _("Document has unknown dmFileMetaType: %ld"),
4669 document->dmFileMetaType);
4670 err = IE_ENUM;
4671 goto leave;
4673 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4675 if (document->dmFileGuid) {
4676 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4678 if (document->dmUpFileGuid) {
4679 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4682 /* @dmFileDescr is required */
4683 if (!document->dmFileDescr) {
4684 isds_log_message(context,
4685 _("Document is missing mandatory description (title)"));
4686 err = IE_INVAL;
4687 goto leave;
4689 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4691 if (document->dmFormat) {
4692 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4696 /* Insert content (body) of the document. */
4697 if (document->is_xml) {
4698 /* XML document requested */
4700 /* Allocate new dmXMLContent */
4701 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4702 if (!xmlcontent) {
4703 isds_printf_message(context,
4704 _("Could not allocate dmXMLContent element"));
4705 err = IE_ERROR;
4706 goto leave;
4708 /* Append it */
4709 node = xmlAddChild(file, xmlcontent);
4710 if (!node) {
4711 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4712 isds_printf_message(context,
4713 _("Could not add dmXMLContent child to %s element"),
4714 file->name);
4715 err = IE_ERROR;
4716 goto leave;
4719 /* Copy non-empty node list */
4720 if (document->xml_node_list) {
4721 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4722 document->xml_node_list);
4723 if (!content) {
4724 isds_printf_message(context,
4725 _("Not enough memory to copy XML document"));
4726 err = IE_NOMEM;
4727 goto leave;
4730 if (!xmlAddChildList(node, content)) {
4731 xmlFreeNodeList(content);
4732 isds_printf_message(context,
4733 _("Error while adding XML document into dmXMLContent"));
4734 err = IE_XML;
4735 goto leave;
4737 /* XXX: We cannot free the content here because it's part of node's
4738 * document since now. It will be freed with it automatically. */
4740 } else {
4741 /* Binary document requested */
4742 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4743 document->data, document->data_length);
4744 if (err) goto leave;
4747 leave:
4748 return err;
4752 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4753 * The copy must be preallocated, the date are just appended into structure.
4754 * @context is ISDS context
4755 * @copy is message copy structure
4756 * @xpath_ctx is XPath context with current node as tMStatus */
4757 static isds_error append_TMStatus(struct isds_ctx *context,
4758 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4759 isds_error err = IE_SUCCESS;
4760 xmlXPathObjectPtr result = NULL;
4761 char *code = NULL, *message = NULL;
4763 if (!context) return IE_INVALID_CONTEXT;
4764 if (!copy || !xpath_ctx) return IE_INVAL;
4766 /* Free old values */
4767 zfree(copy->dmStatus);
4768 zfree(copy->dmID);
4770 /* Get error specific to this copy */
4771 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4772 if (!code) {
4773 isds_log_message(context,
4774 _("Missing isds:dmStatusCode under "
4775 "XSD:tMStatus type element"));
4776 err = IE_ISDS;
4777 goto leave;
4780 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4781 /* This copy failed */
4782 copy->error = IE_ISDS;
4783 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4784 if (message) {
4785 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4786 if (!copy->dmStatus) {
4787 copy->dmStatus = code;
4788 code = NULL;
4790 } else {
4791 copy->dmStatus = code;
4792 code = NULL;
4794 } else {
4795 /* This copy succeeded. In this case only, message ID is valid */
4796 copy->error = IE_SUCCESS;
4798 EXTRACT_STRING("isds:dmID", copy->dmID);
4799 if (!copy->dmID) {
4800 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4801 "but did not returned assigned message ID\n"));
4802 err = IE_ISDS;
4806 leave:
4807 free(code);
4808 free(message);
4809 xmlXPathFreeObject(result);
4810 return err;
4814 /* Insert struct isds_approval data (box approval) into XML tree
4815 * @context is session context
4816 * @approval is libisds structure with approval description. NULL is
4817 * acceptable.
4818 * @parent is XML element to append @approval to */
4819 static isds_error insert_GExtApproval(struct isds_ctx *context,
4820 const struct isds_approval *approval, xmlNodePtr parent) {
4822 isds_error err = IE_SUCCESS;
4823 xmlNodePtr node;
4825 if (!context) return IE_INVALID_CONTEXT;
4826 if (!parent) return IE_INVAL;
4828 if (!approval) return IE_SUCCESS;
4830 /* Build XSD:gExtApproval */
4831 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4832 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4834 leave:
4835 return err;
4839 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4840 * code
4841 * @context is session context
4842 * @service_name is name of SERVICE_DB_ACCESS
4843 * @response is reallocated server SOAP body response as XML document
4844 * @raw_response is reallocated bit stream with response body. Use
4845 * NULL if you don't care
4846 * @raw_response_length is size of @raw_response in bytes
4847 * @code is reallocated ISDS status code
4848 * @status_message is reallocated ISDS status message
4849 * @return error coded from lower layer, context message will be set up
4850 * appropriately. */
4851 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4852 const xmlChar *service_name,
4853 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4854 xmlChar **code, xmlChar **status_message) {
4856 isds_error err = IE_SUCCESS;
4857 char *service_name_locale = NULL;
4858 xmlNodePtr request = NULL, node;
4859 xmlNsPtr isds_ns = NULL;
4861 if (!context) return IE_INVALID_CONTEXT;
4862 if (!service_name) return IE_INVAL;
4863 if (!response || !code || !status_message) return IE_INVAL;
4864 if (!raw_response_length && raw_response) return IE_INVAL;
4866 /* Free output argument */
4867 xmlFreeDoc(*response); *response = NULL;
4868 if (raw_response) zfree(*raw_response);
4869 zfree(*code);
4870 zfree(*status_message);
4873 /* Check if connection is established
4874 * TODO: This check should be done downstairs. */
4875 if (!context->curl) return IE_CONNECTION_CLOSED;
4877 service_name_locale = _isds_utf82locale((char*)service_name);
4878 if (!service_name_locale) {
4879 err = IE_NOMEM;
4880 goto leave;
4883 /* Build request */
4884 request = xmlNewNode(NULL, service_name);
4885 if (!request) {
4886 isds_printf_message(context,
4887 _("Could not build %s request"), service_name_locale);
4888 err = IE_ERROR;
4889 goto leave;
4891 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4892 if(!isds_ns) {
4893 isds_log_message(context, _("Could not create ISDS name space"));
4894 err = IE_ERROR;
4895 goto leave;
4897 xmlSetNs(request, isds_ns);
4900 /* Add XSD:tDummyInput child */
4901 INSERT_STRING(request, "dbDummy", NULL);
4904 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4905 service_name_locale);
4907 /* Send request */
4908 err = _isds(context, SERVICE_DB_ACCESS, request, response,
4909 raw_response, raw_response_length);
4910 xmlFreeNode(request); request = NULL;
4912 if (err) {
4913 isds_log(ILF_ISDS, ILL_DEBUG,
4914 _("Processing ISDS response on %s request failed\n"),
4915 service_name_locale);
4916 goto leave;
4919 /* Check for response status */
4920 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4921 code, status_message, NULL);
4922 if (err) {
4923 isds_log(ILF_ISDS, ILL_DEBUG,
4924 _("ISDS response on %s request is missing status\n"),
4925 service_name_locale);
4926 goto leave;
4929 /* Request processed, but nothing found */
4930 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4931 char *code_locale = _isds_utf82locale((char*) *code);
4932 char *status_message_locale =
4933 _isds_utf82locale((char*) *status_message);
4934 isds_log(ILF_ISDS, ILL_DEBUG,
4935 _("Server refused %s request (code=%s, message=%s)\n"),
4936 service_name_locale, code_locale, status_message_locale);
4937 isds_log_message(context, status_message_locale);
4938 free(code_locale);
4939 free(status_message_locale);
4940 err = IE_ISDS;
4941 goto leave;
4944 leave:
4945 free(service_name_locale);
4946 xmlFreeNode(request);
4947 return err;
4949 #endif
4952 /* Get data about logged in user and his box. */
4953 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4954 struct isds_DbOwnerInfo **db_owner_info) {
4955 isds_error err = IE_SUCCESS;
4956 #if HAVE_LIBCURL
4957 xmlDocPtr response = NULL;
4958 xmlChar *code = NULL, *message = NULL;
4959 xmlXPathContextPtr xpath_ctx = NULL;
4960 xmlXPathObjectPtr result = NULL;
4961 char *string = NULL;
4962 #endif
4964 if (!context) return IE_INVALID_CONTEXT;
4965 zfree(context->long_message);
4966 if (!db_owner_info) return IE_INVAL;
4967 isds_DbOwnerInfo_free(db_owner_info);
4969 #if HAVE_LIBCURL
4970 /* Check if connection is established */
4971 if (!context->curl) return IE_CONNECTION_CLOSED;
4974 /* Do request and check for success */
4975 err = build_send_check_dbdummy_request(context,
4976 BAD_CAST "GetOwnerInfoFromLogin",
4977 &response, NULL, NULL, &code, &message);
4978 if (err) goto leave;
4981 /* Extract data */
4982 /* Prepare structure */
4983 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4984 if (!*db_owner_info) {
4985 err = IE_NOMEM;
4986 goto leave;
4988 xpath_ctx = xmlXPathNewContext(response);
4989 if (!xpath_ctx) {
4990 err = IE_ERROR;
4991 goto leave;
4993 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4994 err = IE_ERROR;
4995 goto leave;
4998 /* Set context node */
4999 result = xmlXPathEvalExpression(BAD_CAST
5000 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
5001 if (!result) {
5002 err = IE_ERROR;
5003 goto leave;
5005 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5006 isds_log_message(context, _("Missing dbOwnerInfo element"));
5007 err = IE_ISDS;
5008 goto leave;
5010 if (result->nodesetval->nodeNr > 1) {
5011 isds_log_message(context, _("Multiple dbOwnerInfo element"));
5012 err = IE_ISDS;
5013 goto leave;
5015 xpath_ctx->node = result->nodesetval->nodeTab[0];
5016 xmlXPathFreeObject(result); result = NULL;
5018 /* Extract it */
5019 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
5022 leave:
5023 if (err) {
5024 isds_DbOwnerInfo_free(db_owner_info);
5027 free(string);
5028 xmlXPathFreeObject(result);
5029 xmlXPathFreeContext(xpath_ctx);
5031 free(code);
5032 free(message);
5033 xmlFreeDoc(response);
5035 if (!err)
5036 isds_log(ILF_ISDS, ILL_DEBUG,
5037 _("GetOwnerInfoFromLogin request processed by server "
5038 "successfully.\n"));
5039 #else /* not HAVE_LIBCURL */
5040 err = IE_NOTSUP;
5041 #endif
5043 return err;
5047 /* Get data about logged in user. */
5048 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
5049 struct isds_DbUserInfo **db_user_info) {
5050 isds_error err = IE_SUCCESS;
5051 #if HAVE_LIBCURL
5052 xmlDocPtr response = NULL;
5053 xmlChar *code = NULL, *message = NULL;
5054 xmlXPathContextPtr xpath_ctx = NULL;
5055 xmlXPathObjectPtr result = NULL;
5056 #endif
5058 if (!context) return IE_INVALID_CONTEXT;
5059 zfree(context->long_message);
5060 if (!db_user_info) return IE_INVAL;
5061 isds_DbUserInfo_free(db_user_info);
5063 #if HAVE_LIBCURL
5064 /* Check if connection is established */
5065 if (!context->curl) return IE_CONNECTION_CLOSED;
5068 /* Do request and check for success */
5069 err = build_send_check_dbdummy_request(context,
5070 BAD_CAST "GetUserInfoFromLogin",
5071 &response, NULL, NULL, &code, &message);
5072 if (err) goto leave;
5075 /* Extract data */
5076 /* Prepare structure */
5077 *db_user_info = calloc(1, sizeof(**db_user_info));
5078 if (!*db_user_info) {
5079 err = IE_NOMEM;
5080 goto leave;
5082 xpath_ctx = xmlXPathNewContext(response);
5083 if (!xpath_ctx) {
5084 err = IE_ERROR;
5085 goto leave;
5087 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5088 err = IE_ERROR;
5089 goto leave;
5092 /* Set context node */
5093 result = xmlXPathEvalExpression(BAD_CAST
5094 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
5095 if (!result) {
5096 err = IE_ERROR;
5097 goto leave;
5099 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5100 isds_log_message(context, _("Missing dbUserInfo element"));
5101 err = IE_ISDS;
5102 goto leave;
5104 if (result->nodesetval->nodeNr > 1) {
5105 isds_log_message(context, _("Multiple dbUserInfo element"));
5106 err = IE_ISDS;
5107 goto leave;
5109 xpath_ctx->node = result->nodesetval->nodeTab[0];
5110 xmlXPathFreeObject(result); result = NULL;
5112 /* Extract it */
5113 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5115 leave:
5116 if (err) {
5117 isds_DbUserInfo_free(db_user_info);
5120 xmlXPathFreeObject(result);
5121 xmlXPathFreeContext(xpath_ctx);
5123 free(code);
5124 free(message);
5125 xmlFreeDoc(response);
5127 if (!err)
5128 isds_log(ILF_ISDS, ILL_DEBUG,
5129 _("GetUserInfoFromLogin request processed by server "
5130 "successfully.\n"));
5131 #else /* not HAVE_LIBCURL */
5132 err = IE_NOTSUP;
5133 #endif
5135 return err;
5139 /* Get expiration time of current password
5140 * @context is session context
5141 * @expiration is automatically reallocated time when password expires. If
5142 * password expiration is disabled, NULL will be returned. In case of error
5143 * it will be nulled too. */
5144 isds_error isds_get_password_expiration(struct isds_ctx *context,
5145 struct timeval **expiration) {
5146 isds_error err = IE_SUCCESS;
5147 #if HAVE_LIBCURL
5148 xmlDocPtr response = NULL;
5149 xmlChar *code = NULL, *message = NULL;
5150 xmlXPathContextPtr xpath_ctx = NULL;
5151 xmlXPathObjectPtr result = NULL;
5152 char *string = NULL;
5153 #endif
5155 if (!context) return IE_INVALID_CONTEXT;
5156 zfree(context->long_message);
5157 if (!expiration) return IE_INVAL;
5158 zfree(*expiration);
5160 #if HAVE_LIBCURL
5161 /* Check if connection is established */
5162 if (!context->curl) return IE_CONNECTION_CLOSED;
5165 /* Do request and check for success */
5166 err = build_send_check_dbdummy_request(context,
5167 BAD_CAST "GetPasswordInfo",
5168 &response, NULL, NULL, &code, &message);
5169 if (err) goto leave;
5172 /* Extract data */
5173 xpath_ctx = xmlXPathNewContext(response);
5174 if (!xpath_ctx) {
5175 err = IE_ERROR;
5176 goto leave;
5178 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5179 err = IE_ERROR;
5180 goto leave;
5183 /* Set context node */
5184 result = xmlXPathEvalExpression(BAD_CAST
5185 "/isds:GetPasswordInfoResponse", xpath_ctx);
5186 if (!result) {
5187 err = IE_ERROR;
5188 goto leave;
5190 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5191 isds_log_message(context,
5192 _("Missing GetPasswordInfoResponse element"));
5193 err = IE_ISDS;
5194 goto leave;
5196 if (result->nodesetval->nodeNr > 1) {
5197 isds_log_message(context,
5198 _("Multiple GetPasswordInfoResponse element"));
5199 err = IE_ISDS;
5200 goto leave;
5202 xpath_ctx->node = result->nodesetval->nodeTab[0];
5203 xmlXPathFreeObject(result); result = NULL;
5205 /* Extract expiration date */
5206 EXTRACT_STRING("isds:pswExpDate", string);
5207 if (string) {
5208 /* And convert it if any returned. Otherwise expiration is disabled. */
5209 err = timestring2timeval((xmlChar *) string, expiration);
5210 if (err) {
5211 char *string_locale = _isds_utf82locale(string);
5212 if (err == IE_DATE) err = IE_ISDS;
5213 isds_printf_message(context,
5214 _("Could not convert pswExpDate as ISO time: %s"),
5215 string_locale);
5216 free(string_locale);
5217 goto leave;
5221 leave:
5222 if (err) {
5223 if (*expiration) {
5224 zfree(*expiration);
5228 free(string);
5229 xmlXPathFreeObject(result);
5230 xmlXPathFreeContext(xpath_ctx);
5232 free(code);
5233 free(message);
5234 xmlFreeDoc(response);
5236 if (!err)
5237 isds_log(ILF_ISDS, ILL_DEBUG,
5238 _("GetPasswordInfo request processed by server "
5239 "successfully.\n"));
5240 #else /* not HAVE_LIBCURL */
5241 err = IE_NOTSUP;
5242 #endif
5244 return err;
5248 #if HAVE_LIBCURL
5249 /* Request delivering new TOTP code from ISDS through side channel before
5250 * changing password.
5251 * @context is session context
5252 * @password is current password.
5253 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5254 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5255 * function for more details.
5256 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5257 * NULL, if you don't care.
5258 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5259 * error code. */
5260 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5261 const char *password, struct isds_otp *otp, char **refnumber) {
5262 isds_error err = IE_SUCCESS;
5263 char *saved_url = NULL; /* No copy */
5264 #if HAVE_CURL_REAUTHORIZATION_BUG
5265 CURL *saved_curl = NULL; /* No copy */
5266 #endif
5267 xmlNsPtr isds_ns = NULL;
5268 xmlNodePtr request = NULL;
5269 xmlDocPtr response = NULL;
5270 xmlChar *code = NULL, *message = NULL;
5271 const xmlChar *codes[] = {
5272 BAD_CAST "2300",
5273 BAD_CAST "2301",
5274 BAD_CAST "2302"
5276 const char *meanings[] = {
5277 N_("Unexpected error"),
5278 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5279 N_("One-time code could not been sent. Try later again.")
5281 const isds_otp_resolution resolutions[] = {
5282 OTP_RESOLUTION_UNKNOWN,
5283 OTP_RESOLUTION_TO_FAST,
5284 OTP_RESOLUTION_TOTP_NOT_SENT
5287 if (NULL == context) return IE_INVALID_CONTEXT;
5288 zfree(context->long_message);
5289 if (NULL == password) {
5290 isds_log_message(context,
5291 _("Second argument (password) of isds_change_password() "
5292 "is NULL"));
5293 return IE_INVAL;
5296 /* Check if connection is established
5297 * TODO: This check should be done downstairs. */
5298 if (!context->curl) return IE_CONNECTION_CLOSED;
5300 if (!context->otp) {
5301 isds_log_message(context, _("This function requires OTP-authenticated "
5302 "context"));
5303 return IE_INVALID_CONTEXT;
5305 if (NULL == otp) {
5306 isds_log_message(context, _("If one-time password authentication "
5307 "method is in use, requesting new OTP code requires "
5308 "one-time credentials argument either"));
5309 return IE_INVAL;
5311 if (otp->method != OTP_TIME) {
5312 isds_log_message(context, _("Requesting new time-based OTP code from "
5313 "server requires one-time password authentication "
5314 "method"));
5315 return IE_INVAL;
5317 if (otp->otp_code != NULL) {
5318 isds_log_message(context, _("Requesting new time-based OTP code from "
5319 "server requires undefined OTP code member in "
5320 "one-time credentials argument"));
5321 return IE_INVAL;
5325 /* Build request */
5326 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5327 if (!request) {
5328 isds_log_message(context, _("Could not build SendSMSCode request"));
5329 return IE_ERROR;
5331 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5332 if(!isds_ns) {
5333 isds_log_message(context, _("Could not create ISDS name space"));
5334 xmlFreeNode(request);
5335 return IE_ERROR;
5337 xmlSetNs(request, isds_ns);
5339 /* Change URL temporarily for sending this request only */
5341 char *new_url = NULL;
5342 if ((err = _isds_build_url_from_context(context,
5343 "%1$.*2$sasws/changePassword", &new_url))) {
5344 goto leave;
5346 saved_url = context->url;
5347 context->url = new_url;
5350 /* Store credentials for sending this request only */
5351 context->otp_credentials = otp;
5352 _isds_discard_credentials(context, 0);
5353 if ((err = _isds_store_credentials(context, context->saved_username,
5354 password, NULL))) {
5355 _isds_discard_credentials(context, 0);
5356 goto leave;
5358 #if HAVE_CURL_REAUTHORIZATION_BUG
5359 saved_curl = context->curl;
5360 context->curl = curl_easy_init();
5361 if (NULL == context->curl) {
5362 err = IE_ERROR;
5363 goto leave;
5365 if (context->timeout) {
5366 err = isds_set_timeout(context, context->timeout);
5367 if (err) goto leave;
5369 #endif
5371 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5373 /* Sent request */
5374 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5376 /* Remove temporal credentials */
5377 _isds_discard_credentials(context, 0);
5378 /* Detach pointer to OTP credentials from context */
5379 context->otp_credentials = NULL;
5380 /* Keep context->otp true to keep signaling this is OTP session */
5382 /* Destroy request */
5383 xmlFreeNode(request); request = NULL;
5385 if (err) {
5386 isds_log(ILF_ISDS, ILL_DEBUG,
5387 _("Processing ISDS response on SendSMSCode request failed\n"));
5388 goto leave;
5391 /* Check for response status */
5392 err = isds_response_status(context, SERVICE_ASWS, response,
5393 &code, &message, (xmlChar **)refnumber);
5394 if (err) {
5395 isds_log(ILF_ISDS, ILL_DEBUG,
5396 _("ISDS response on SendSMSCode request is missing "
5397 "status\n"));
5398 goto leave;
5401 /* Check for error */
5402 if (xmlStrcmp(code, BAD_CAST "0000")) {
5403 char *code_locale = _isds_utf82locale((char*)code);
5404 char *message_locale = _isds_utf82locale((char*)message);
5405 size_t i;
5406 isds_log(ILF_ISDS, ILL_DEBUG,
5407 _("Server refused to send new code on SendSMSCode "
5408 "request (code=%s, message=%s)\n"),
5409 code_locale, message_locale);
5411 /* Check for known error codes */
5412 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5413 if (!xmlStrcmp(code, codes[i])) break;
5415 if (i < sizeof(codes)/sizeof(*codes)) {
5416 isds_log_message(context, _(meanings[i]));
5417 /* Mimic otp->resolution according to the code, specification does
5418 * prescribe OTP header to be available. */
5419 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5420 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5421 otp->resolution = resolutions[i];
5422 } else
5423 isds_log_message(context, message_locale);
5425 free(code_locale);
5426 free(message_locale);
5428 err = IE_ISDS;
5429 goto leave;
5432 /* Otherwise new code sent successfully */
5433 /* Mimic otp->resolution according to the code, specification does
5434 * prescribe OTP header to be available. */
5435 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5436 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5438 leave:
5439 if (NULL != saved_url) {
5440 /* Revert URL to original one */
5441 zfree(context->url);
5442 context->url = saved_url;
5444 #if HAVE_CURL_REAUTHORIZATION_BUG
5445 if (NULL != saved_curl) {
5446 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5447 context->curl = saved_curl;
5449 #endif
5451 free(code);
5452 free(message);
5453 xmlFreeDoc(response);
5454 xmlFreeNode(request);
5456 if (!err)
5457 isds_log(ILF_ISDS, ILL_DEBUG,
5458 _("New OTP code has been sent successfully on SendSMSCode "
5459 "request.\n"));
5460 return err;
5464 /* Convert response status code to isds_error code and set long message
5465 * @context is context to save long message to
5466 * @map is mapping from codes to errors and messages. Pass NULL for generic
5467 * handling.
5468 * @code is status code to translate
5469 * @message is non-localized status message to put into long message in case
5470 * of uknown error. It can be NULL if server did not provide any.
5471 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5472 * invalid invocation. */
5473 static isds_error statuscode2isds_error(struct isds_ctx *context,
5474 const struct code_map_isds_error *map,
5475 const xmlChar *code, const xmlChar *message) {
5476 if (NULL == code) {
5477 isds_log_message(context,
5478 _("NULL status code passed to statuscode2isds_error()"));
5479 return IE_INVAL;
5482 if (NULL != map) {
5483 /* Check for known error codes */
5484 for (int i=0; map->codes[i] != NULL; i++) {
5485 if (!xmlStrcmp(code, map->codes[i])) {
5486 isds_log_message(context, _(map->meanings[i]));
5487 return map->errors[i];
5492 /* Other error */
5493 if (xmlStrcmp(code, BAD_CAST "0000")) {
5494 char *message_locale = _isds_utf82locale((char*)message);
5495 if (NULL == message_locale)
5496 isds_log_message(context, _("ISDS server returned unknown error"));
5497 else
5498 isds_log_message(context, message_locale);
5499 free(message_locale);
5500 return IE_ISDS;
5503 return IE_SUCCESS;
5505 #endif
5508 /* Change user password in ISDS.
5509 * User must supply old password, new password will takes effect after some
5510 * time, current session can continue. Password must fulfill some constraints.
5511 * @context is session context
5512 * @old_password is current password.
5513 * @new_password is requested new password
5514 * @otp auxiliary data required if one-time password authentication is in use,
5515 * defines OTP code (if known) and returns fine grade resolution of OTP
5516 * procedure. Pass NULL, if one-time password authentication is not needed.
5517 * Please note the @otp argument must match OTP method used at log-in time. See
5518 * isds_login() function for more details.
5519 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5520 * NULL, if you don't care.
5521 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5522 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5523 * awaiting OTP code that has been delivered by side channel to the user. */
5524 isds_error isds_change_password(struct isds_ctx *context,
5525 const char *old_password, const char *new_password,
5526 struct isds_otp *otp, char **refnumber) {
5527 isds_error err = IE_SUCCESS;
5528 #if HAVE_LIBCURL
5529 char *saved_url = NULL; /* No copy */
5530 #if HAVE_CURL_REAUTHORIZATION_BUG
5531 CURL *saved_curl = NULL; /* No copy */
5532 #endif
5533 xmlNsPtr isds_ns = NULL;
5534 xmlNodePtr request = NULL, node;
5535 xmlDocPtr response = NULL;
5536 xmlChar *code = NULL, *message = NULL;
5537 const xmlChar *codes[] = {
5538 BAD_CAST "1066",
5539 BAD_CAST "1067",
5540 BAD_CAST "1079",
5541 BAD_CAST "1080",
5542 BAD_CAST "1081",
5543 BAD_CAST "1082",
5544 BAD_CAST "1083",
5545 BAD_CAST "1090",
5546 BAD_CAST "1091",
5547 BAD_CAST "2300",
5548 BAD_CAST "9204"
5550 const char *meanings[] = {
5551 N_("Password length must be between 8 and 32 characters"),
5552 N_("Password cannot be reused"), /* Server does not distinguish 1067
5553 and 1091 on ChangePasswordOTP */
5554 N_("Password contains forbidden character"),
5555 N_("Password must contain at least one upper-case letter, "
5556 "one lower-case, and one digit"),
5557 N_("Password cannot contain sequence of three identical characters"),
5558 N_("Password cannot contain user identifier"),
5559 N_("Password is too simmple"),
5560 N_("Old password is not valid"),
5561 N_("Password cannot be reused"),
5562 N_("Unexpected error"),
5563 N_("LDAP update error")
5565 #endif
5567 if (!context) return IE_INVALID_CONTEXT;
5568 zfree(context->long_message);
5569 if (NULL != refnumber)
5570 zfree(*refnumber);
5571 if (NULL == old_password) {
5572 isds_log_message(context,
5573 _("Second argument (old password) of isds_change_password() "
5574 "is NULL"));
5575 return IE_INVAL;
5577 if (NULL == otp && NULL == new_password) {
5578 isds_log_message(context,
5579 _("Third argument (new password) of isds_change_password() "
5580 "is NULL"));
5581 return IE_INVAL;
5584 #if HAVE_LIBCURL
5585 /* Check if connection is established
5586 * TODO: This check should be done downstairs. */
5587 if (!context->curl) return IE_CONNECTION_CLOSED;
5589 if (context->otp && NULL == otp) {
5590 isds_log_message(context, _("If one-time password authentication "
5591 "method is in use, changing password requires one-time "
5592 "credentials either"));
5593 return IE_INVAL;
5596 /* Build ChangeISDSPassword request */
5597 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5598 BAD_CAST "ChangePasswordOTP");
5599 if (!request) {
5600 isds_log_message(context, (NULL == otp) ?
5601 _("Could not build ChangeISDSPassword request") :
5602 _("Could not build ChangePasswordOTP request"));
5603 return IE_ERROR;
5605 isds_ns = xmlNewNs(request,
5606 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5607 NULL);
5608 if(!isds_ns) {
5609 isds_log_message(context, _("Could not create ISDS name space"));
5610 xmlFreeNode(request);
5611 return IE_ERROR;
5613 xmlSetNs(request, isds_ns);
5615 INSERT_STRING(request, "dbOldPassword", old_password);
5616 INSERT_STRING(request, "dbNewPassword", new_password);
5618 if (NULL != otp) {
5619 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5620 switch (otp->method) {
5621 case OTP_HMAC:
5622 isds_log(ILF_SEC, ILL_INFO,
5623 _("Selected authentication method: "
5624 "HMAC-based one-time password\n"));
5625 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5626 break;
5627 case OTP_TIME:
5628 isds_log(ILF_SEC, ILL_INFO,
5629 _("Selected authentication method: "
5630 "Time-based one-time password\n"));
5631 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5632 if (otp->otp_code == NULL) {
5633 isds_log(ILF_SEC, ILL_INFO,
5634 _("OTP code has not been provided by "
5635 "application, requesting server for "
5636 "new one.\n"));
5637 err = _isds_request_totp_code(context, old_password, otp,
5638 refnumber);
5639 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5640 goto leave;
5642 } else {
5643 isds_log(ILF_SEC, ILL_INFO,
5644 _("OTP code has been provided by "
5645 "application, not requesting server "
5646 "for new one.\n"));
5648 break;
5649 default:
5650 isds_log_message(context,
5651 _("Unknown one-time password authentication "
5652 "method requested by application"));
5653 err = IE_ENUM;
5654 goto leave;
5657 /* Change URL temporarily for sending this request only */
5659 char *new_url = NULL;
5660 if ((err = _isds_build_url_from_context(context,
5661 "%1$.*2$sasws/changePassword", &new_url))) {
5662 goto leave;
5664 saved_url = context->url;
5665 context->url = new_url;
5668 /* Store credentials for sending this request only */
5669 context->otp_credentials = otp;
5670 _isds_discard_credentials(context, 0);
5671 if ((err = _isds_store_credentials(context, context->saved_username,
5672 old_password, NULL))) {
5673 _isds_discard_credentials(context, 0);
5674 goto leave;
5676 #if HAVE_CURL_REAUTHORIZATION_BUG
5677 saved_curl = context->curl;
5678 context->curl = curl_easy_init();
5679 if (NULL == context->curl) {
5680 err = IE_ERROR;
5681 goto leave;
5683 if (context->timeout) {
5684 err = isds_set_timeout(context, context->timeout);
5685 if (err) goto leave;
5687 #endif
5690 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5691 _("Sending ChangeISDSPassword request to ISDS\n") :
5692 _("Sending ChangePasswordOTP request to ISDS\n"));
5694 /* Sent request */
5695 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5696 request, &response, NULL, NULL);
5698 if (otp) {
5699 /* Remove temporal credentials */
5700 _isds_discard_credentials(context, 0);
5701 /* Detach pointer to OTP credentials from context */
5702 context->otp_credentials = NULL;
5703 /* Keep context->otp true to keep signaling this is OTP session */
5706 /* Destroy request */
5707 xmlFreeNode(request); request = NULL;
5709 if (err) {
5710 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5711 _("Processing ISDS response on ChangeISDSPassword "
5712 "request failed\n") :
5713 _("Processing ISDS response on ChangePasswordOTP "
5714 "request failed\n"));
5715 goto leave;
5718 /* Check for response status */
5719 err = isds_response_status(context,
5720 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5721 &code, &message, (xmlChar **)refnumber);
5722 if (err) {
5723 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5724 _("ISDS response on ChangeISDSPassword request is missing "
5725 "status\n") :
5726 _("ISDS response on ChangePasswordOTP request is missing "
5727 "status\n"));
5728 goto leave;
5731 /* Check for known error codes */
5732 for (size_t i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5733 if (!xmlStrcmp(code, codes[i])) {
5734 char *code_locale = _isds_utf82locale((char*)code);
5735 char *message_locale = _isds_utf82locale((char*)message);
5736 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5737 _("Server refused to change password on ChangeISDSPassword "
5738 "request (code=%s, message=%s)\n") :
5739 _("Server refused to change password on ChangePasswordOTP "
5740 "request (code=%s, message=%s)\n"),
5741 code_locale, message_locale);
5742 free(code_locale);
5743 free(message_locale);
5744 isds_log_message(context, _(meanings[i]));
5745 err = IE_INVAL;
5746 goto leave;
5750 /* Other error */
5751 if (xmlStrcmp(code, BAD_CAST "0000")) {
5752 char *code_locale = _isds_utf82locale((char*)code);
5753 char *message_locale = _isds_utf82locale((char*)message);
5754 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5755 _("Server refused to change password on ChangeISDSPassword "
5756 "request (code=%s, message=%s)\n") :
5757 _("Server refused to change password on ChangePasswordOTP "
5758 "request (code=%s, message=%s)\n"),
5759 code_locale, message_locale);
5760 isds_log_message(context, message_locale);
5761 free(code_locale);
5762 free(message_locale);
5763 err = IE_ISDS;
5764 goto leave;
5767 /* Otherwise password changed successfully */
5769 leave:
5770 if (NULL != saved_url) {
5771 /* Revert URL to original one */
5772 zfree(context->url);
5773 context->url = saved_url;
5775 #if HAVE_CURL_REAUTHORIZATION_BUG
5776 if (NULL != saved_curl) {
5777 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5778 context->curl = saved_curl;
5780 #endif
5782 free(code);
5783 free(message);
5784 xmlFreeDoc(response);
5785 xmlFreeNode(request);
5787 if (!err)
5788 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5789 _("Password changed successfully on ChangeISDSPassword "
5790 "request.\n") :
5791 _("Password changed successfully on ChangePasswordOTP "
5792 "request.\n"));
5793 #else /* not HAVE_LIBCURL */
5794 err = IE_NOTSUP;
5795 #endif
5797 return err;
5801 #if HAVE_LIBCURL
5802 /* Generic middle part with request sending and response check.
5803 * It sends prepared request and checks for error code.
5804 * @context is ISDS session context.
5805 * @service is ISDS service handler
5806 * @service_name is name in scope of given @service
5807 * @request is XML tree with request. Will be freed to save memory.
5808 * @response is XML document outputting ISDS response.
5809 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5810 * @map is mapping from status code to library error. Pass NULL if no special
5811 * handling is requested.
5812 * NULL, if you don't care. */
5813 static isds_error send_destroy_request_check_response(
5814 struct isds_ctx *context,
5815 const isds_service service, const xmlChar *service_name,
5816 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5817 const struct code_map_isds_error *map) {
5818 isds_error err = IE_SUCCESS;
5819 char *service_name_locale = NULL;
5820 xmlChar *code = NULL, *message = NULL;
5823 if (!context) return IE_INVALID_CONTEXT;
5824 if (!service_name || *service_name == '\0' || !request || !*request ||
5825 !response)
5826 return IE_INVAL;
5828 /* Check if connection is established
5829 * TODO: This check should be done downstairs. */
5830 if (!context->curl) return IE_CONNECTION_CLOSED;
5832 service_name_locale = _isds_utf82locale((char*) service_name);
5833 if (!service_name_locale) {
5834 err = IE_NOMEM;
5835 goto leave;
5838 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5839 service_name_locale);
5841 /* Send request */
5842 err = _isds(context, service, *request, response, NULL, NULL);
5843 xmlFreeNode(*request); *request = NULL;
5845 if (err) {
5846 isds_log(ILF_ISDS, ILL_DEBUG,
5847 _("Processing ISDS response on %s request failed\n"),
5848 service_name_locale);
5849 goto leave;
5852 /* Check for response status */
5853 err = isds_response_status(context, service, *response,
5854 &code, &message, refnumber);
5855 if (err) {
5856 isds_log(ILF_ISDS, ILL_DEBUG,
5857 _("ISDS response on %s request is missing status\n"),
5858 service_name_locale);
5859 goto leave;
5862 err = statuscode2isds_error(context, map, code, message);
5864 /* Request processed, but server failed */
5865 if (xmlStrcmp(code, BAD_CAST "0000")) {
5866 char *code_locale = _isds_utf82locale((char*) code);
5867 char *message_locale = _isds_utf82locale((char*) message);
5868 isds_log(ILF_ISDS, ILL_DEBUG,
5869 _("Server refused %s request (code=%s, message=%s)\n"),
5870 service_name_locale, code_locale, message_locale);
5871 free(code_locale);
5872 free(message_locale);
5873 goto leave;
5877 leave:
5878 free(code);
5879 free(message);
5880 if (err && *response) {
5881 xmlFreeDoc(*response);
5882 *response = NULL;
5884 if (*request) {
5885 xmlFreeNode(*request);
5886 *request = NULL;
5888 free(service_name_locale);
5890 return err;
5894 /* Generic bottom half with request sending.
5895 * It sends prepared request, checks for error code, destroys response and
5896 * request and log success or failure.
5897 * @context is ISDS session context.
5898 * @service is ISDS service handler
5899 * @service_name is name in scope of given @service
5900 * @request is XML tree with request. Will be freed to save memory.
5901 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5902 * NULL, if you don't care. */
5903 static isds_error send_request_check_drop_response(
5904 struct isds_ctx *context,
5905 const isds_service service, const xmlChar *service_name,
5906 xmlNodePtr *request, xmlChar **refnumber) {
5907 isds_error err = IE_SUCCESS;
5908 xmlDocPtr response = NULL;
5911 if (!context) return IE_INVALID_CONTEXT;
5912 if (!service_name || *service_name == '\0' || !request || !*request)
5913 return IE_INVAL;
5915 /* Send request and check response*/
5916 err = send_destroy_request_check_response(context,
5917 service, service_name, request, &response, refnumber, NULL);
5919 xmlFreeDoc(response);
5921 if (*request) {
5922 xmlFreeNode(*request);
5923 *request = NULL;
5926 if (!err) {
5927 char *service_name_locale = _isds_utf82locale((char *) service_name);
5928 isds_log(ILF_ISDS, ILL_DEBUG,
5929 _("%s request processed by server successfully.\n"),
5930 service_name_locale);
5931 free(service_name_locale);
5934 return err;
5938 /* Insert isds_credentials_delivery structure into XML request if not NULL
5939 * @context is session context
5940 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5941 * credentials delivery. The email field is passed.
5942 * @parent is XML element where to insert */
5943 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5944 const struct isds_credentials_delivery *credentials_delivery,
5945 xmlNodePtr parent) {
5946 isds_error err = IE_SUCCESS;
5947 xmlNodePtr node;
5949 if (!context) return IE_INVALID_CONTEXT;
5950 if (!parent) return IE_INVAL;
5952 if (credentials_delivery) {
5953 /* Following elements are valid only for services:
5954 * NewAccessData, AddDataBoxUser, CreateDataBox */
5955 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5956 INSERT_STRING(parent, "email", credentials_delivery->email);
5959 leave:
5960 return err;
5964 /* Extract credentials delivery from ISDS response.
5965 * @context is session context
5966 * @credentials_delivery is pointer to valid structure to fill in returned
5967 * user's password (and new log-in name). If NULL, do not extract the data.
5968 * @response is pointer to XML document with ISDS response
5969 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5970 * @return IE_SUCCESS even if new user name has not been found because it's not
5971 * clear whether it's returned always. */
5972 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5973 struct isds_credentials_delivery *credentials_delivery,
5974 xmlDocPtr response, const char *request_name) {
5975 isds_error err = IE_SUCCESS;
5976 xmlXPathContextPtr xpath_ctx = NULL;
5977 xmlXPathObjectPtr result = NULL;
5978 char *xpath_query = NULL;
5980 if (!context) return IE_INVALID_CONTEXT;
5981 if (credentials_delivery) {
5982 zfree(credentials_delivery->token);
5983 zfree(credentials_delivery->new_user_name);
5985 if (!response || !request_name || !*request_name) return IE_INVAL;
5988 /* Extract optional token */
5989 if (credentials_delivery) {
5990 xpath_ctx = xmlXPathNewContext(response);
5991 if (!xpath_ctx) {
5992 err = IE_ERROR;
5993 goto leave;
5995 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5996 err = IE_ERROR;
5997 goto leave;
6000 /* Verify root element */
6001 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
6002 request_name)) {
6003 err = IE_NOMEM;
6004 goto leave;
6006 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
6007 if (!result) {
6008 err = IE_ERROR;
6009 goto leave;
6011 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6012 char *request_name_locale = _isds_utf82locale(request_name);
6013 isds_log(ILF_ISDS, ILL_WARNING,
6014 _("Wrong element in ISDS response for %s request "
6015 "while extracting credentials delivery details\n"),
6016 request_name_locale);
6017 free(request_name_locale);
6018 err = IE_ERROR;
6019 goto leave;
6021 xpath_ctx->node = result->nodesetval->nodeTab[0];
6024 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6025 * optional. */
6026 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
6028 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
6029 if (!credentials_delivery->token) {
6030 char *request_name_locale = _isds_utf82locale(request_name);
6031 isds_log(ILF_ISDS, ILL_ERR,
6032 _("ISDS did not return token on %s request "
6033 "even if requested\n"), request_name_locale);
6034 free(request_name_locale);
6035 err = IE_ERROR;
6039 leave:
6040 free(xpath_query);
6041 xmlXPathFreeObject(result);
6042 xmlXPathFreeContext(xpath_ctx);
6044 return err;
6048 /* Build XSD:tCreateDBInput request type for box creating.
6049 * @context is session context
6050 * @request outputs built XML tree
6051 * @service_name is request name of SERVICE_DB_MANIPULATION service
6052 * @box is box description to create including single primary user (in case of
6053 * FO box type)
6054 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6055 * box, or contact address of PFO box owner)
6056 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6057 * @upper_box_id is optional ID of supper box if currently created box is
6058 * subordinated.
6059 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6060 * don't care.
6061 * @credentials_delivery is valid pointer if ISDS should return token that box
6062 * owner can use to obtain his new credentials in on-line way. Then valid email
6063 * member value should be supplied.
6064 * @approval is optional external approval of box manipulation */
6065 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
6066 xmlNodePtr *request, const xmlChar *service_name,
6067 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6068 const xmlChar *former_names, const xmlChar *upper_box_id,
6069 const xmlChar *ceo_label,
6070 const struct isds_credentials_delivery *credentials_delivery,
6071 const struct isds_approval *approval) {
6072 isds_error err = IE_SUCCESS;
6073 xmlNsPtr isds_ns = NULL;
6074 xmlNodePtr node, dbPrimaryUsers;
6075 xmlChar *string = NULL;
6076 const struct isds_list *item;
6079 if (!context) return IE_INVALID_CONTEXT;
6080 if (!request || !service_name || service_name[0] == '\0' || !box)
6081 return IE_INVAL;
6084 /* Build CreateDataBox-similar request */
6085 *request = xmlNewNode(NULL, service_name);
6086 if (!*request) {
6087 char *service_name_locale = _isds_utf82locale((char*) service_name);
6088 isds_printf_message(context, _("Could build %s request"),
6089 service_name_locale);
6090 free(service_name_locale);
6091 return IE_ERROR;
6093 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
6094 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
6095 if (!isds_ns) {
6096 isds_log_message(context, _("Could not create ISDS1 name space"));
6097 xmlFreeNode(*request);
6098 return IE_ERROR;
6100 } else {
6101 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6102 if (!isds_ns) {
6103 isds_log_message(context, _("Could not create ISDS name space"));
6104 xmlFreeNode(*request);
6105 return IE_ERROR;
6108 xmlSetNs(*request, isds_ns);
6110 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6111 err = insert_DbOwnerInfo(context, box, node);
6112 if (err) goto leave;
6114 /* Insert users */
6115 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6116 * verbose documentation allows none dbUserInfo */
6117 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6118 for (item = users; item; item = item->next) {
6119 if (item->data) {
6120 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6121 err = insert_DbUserInfo(context,
6122 (struct isds_DbUserInfo *) item->data, node);
6123 if (err) goto leave;
6127 INSERT_STRING(*request, "dbFormerNames", former_names);
6128 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6129 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6131 err = insert_credentials_delivery(context, credentials_delivery, *request);
6132 if (err) goto leave;
6134 err = insert_GExtApproval(context, approval, *request);
6135 if (err) goto leave;
6137 leave:
6138 if (err) {
6139 xmlFreeNode(*request);
6140 *request = NULL;
6142 free(string);
6143 return err;
6145 #endif /* HAVE_LIBCURL */
6148 /* Create new box.
6149 * @context is session context
6150 * @box is box description to create including single primary user (in case of
6151 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6152 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6153 * box, or contact address of PFO box owner)
6154 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6155 * @upper_box_id is optional ID of supper box if currently created box is
6156 * subordinated.
6157 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6158 * @credentials_delivery is NULL if new password should be delivered off-line
6159 * to box owner. It is valid pointer if owner should obtain new password on-line
6160 * on dedicated web server. Then input @credentials_delivery.email value is
6161 * his e-mail address he must provide to dedicated web server together
6162 * with output reallocated @credentials_delivery.token member. Output
6163 * member @credentials_delivery.new_user_name is unused up on this call.
6164 * @approval is optional external approval of box manipulation
6165 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6166 * NULL, if you don't care.*/
6167 isds_error isds_add_box(struct isds_ctx *context,
6168 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6169 const char *former_names, const char *upper_box_id,
6170 const char *ceo_label,
6171 struct isds_credentials_delivery *credentials_delivery,
6172 const struct isds_approval *approval, char **refnumber) {
6173 isds_error err = IE_SUCCESS;
6174 #if HAVE_LIBCURL
6175 xmlNodePtr request = NULL;
6176 xmlDocPtr response = NULL;
6177 xmlXPathContextPtr xpath_ctx = NULL;
6178 xmlXPathObjectPtr result = NULL;
6179 #endif
6182 if (!context) return IE_INVALID_CONTEXT;
6183 zfree(context->long_message);
6184 if (credentials_delivery) {
6185 zfree(credentials_delivery->token);
6186 zfree(credentials_delivery->new_user_name);
6188 if (!box) return IE_INVAL;
6190 #if HAVE_LIBCURL
6191 /* Scratch box ID */
6192 zfree(box->dbID);
6194 /* Build CreateDataBox request */
6195 err = build_CreateDBInput_request(context,
6196 &request, BAD_CAST "CreateDataBox",
6197 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6198 (xmlChar *) ceo_label, credentials_delivery, approval);
6199 if (err) goto leave;
6201 /* Send it to server and process response */
6202 err = send_destroy_request_check_response(context,
6203 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6204 &response, (xmlChar **) refnumber, NULL);
6206 /* Extract box ID */
6207 xpath_ctx = xmlXPathNewContext(response);
6208 if (!xpath_ctx) {
6209 err = IE_ERROR;
6210 goto leave;
6212 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6213 err = IE_ERROR;
6214 goto leave;
6216 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6218 /* Extract optional token */
6219 err = extract_credentials_delivery(context, credentials_delivery, response,
6220 "CreateDataBox");
6222 leave:
6223 xmlXPathFreeObject(result);
6224 xmlXPathFreeContext(xpath_ctx);
6225 xmlFreeDoc(response);
6226 xmlFreeNode(request);
6228 if (!err) {
6229 isds_log(ILF_ISDS, ILL_DEBUG,
6230 _("CreateDataBox request processed by server successfully.\n"));
6232 #else /* not HAVE_LIBCURL */
6233 err = IE_NOTSUP;
6234 #endif
6236 return err;
6240 /* Notify ISDS about new PFO entity.
6241 * This function has no real effect.
6242 * @context is session context
6243 * @box is PFO description including single primary user.
6244 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6245 * @former_names is optional undocumented string. Pass NULL if you don't care.
6246 * @upper_box_id is optional ID of supper box if currently created box is
6247 * subordinated.
6248 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6249 * @approval is optional external approval of box manipulation
6250 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6251 * NULL, if you don't care.*/
6252 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6253 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6254 const char *former_names, const char *upper_box_id,
6255 const char *ceo_label, const struct isds_approval *approval,
6256 char **refnumber) {
6257 isds_error err = IE_SUCCESS;
6258 #if HAVE_LIBCURL
6259 xmlNodePtr request = NULL;
6260 #endif
6262 if (!context) return IE_INVALID_CONTEXT;
6263 zfree(context->long_message);
6264 if (!box) return IE_INVAL;
6266 #if HAVE_LIBCURL
6267 /* Build CreateDataBoxPFOInfo request */
6268 err = build_CreateDBInput_request(context,
6269 &request, BAD_CAST "CreateDataBoxPFOInfo",
6270 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6271 (xmlChar *) ceo_label, NULL, approval);
6272 if (err) goto leave;
6274 /* Send it to server and process response */
6275 err = send_request_check_drop_response(context,
6276 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6277 (xmlChar **) refnumber);
6278 /* XXX: XML Schema names output dbID element but textual documentation
6279 * states no box identifier is returned. */
6280 leave:
6281 xmlFreeNode(request);
6282 #else /* not HAVE_LIBCURL */
6283 err = IE_NOTSUP;
6284 #endif
6285 return err;
6289 /* Common implementation for removing given box.
6290 * @context is session context
6291 * @service_name is UTF-8 encoded name fo ISDS service
6292 * @box is box description to delete
6293 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6294 * carry sane value. If NULL, do not inject this information into request.
6295 * @approval is optional external approval of box manipulation
6296 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6297 * NULL, if you don't care.*/
6298 isds_error _isds_delete_box_common(struct isds_ctx *context,
6299 const xmlChar *service_name,
6300 const struct isds_DbOwnerInfo *box, const struct tm *since,
6301 const struct isds_approval *approval, char **refnumber) {
6302 isds_error err = IE_SUCCESS;
6303 #if HAVE_LIBCURL
6304 xmlNsPtr isds_ns = NULL;
6305 xmlNodePtr request = NULL;
6306 xmlNodePtr node;
6307 xmlChar *string = NULL;
6308 #endif
6311 if (!context) return IE_INVALID_CONTEXT;
6312 zfree(context->long_message);
6313 if (!service_name || !*service_name || !box) return IE_INVAL;
6316 #if HAVE_LIBCURL
6317 /* Build DeleteDataBox(Promptly) request */
6318 request = xmlNewNode(NULL, service_name);
6319 if (!request) {
6320 char *service_name_locale = _isds_utf82locale((char*)service_name);
6321 isds_printf_message(context,
6322 _("Could build %s request"), service_name_locale);
6323 free(service_name_locale);
6324 return IE_ERROR;
6326 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6327 if(!isds_ns) {
6328 isds_log_message(context, _("Could not create ISDS name space"));
6329 xmlFreeNode(request);
6330 return IE_ERROR;
6332 xmlSetNs(request, isds_ns);
6334 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6335 err = insert_DbOwnerInfo(context, box, node);
6336 if (err) goto leave;
6338 if (since) {
6339 err = tm2datestring(since, &string);
6340 if (err) {
6341 isds_log_message(context,
6342 _("Could not convert `since' argument to ISO date string"));
6343 goto leave;
6345 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6346 zfree(string);
6349 err = insert_GExtApproval(context, approval, request);
6350 if (err) goto leave;
6353 /* Send it to server and process response */
6354 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6355 service_name, &request, (xmlChar **) refnumber);
6357 leave:
6358 xmlFreeNode(request);
6359 free(string);
6360 #else /* not HAVE_LIBCURL */
6361 err = IE_NOTSUP;
6362 #endif
6363 return err;
6367 /* Remove given box permanently.
6368 * @context is session context
6369 * @box is box description to delete
6370 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6371 * carry sane value.
6372 * @approval is optional external approval of box manipulation
6373 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6374 * NULL, if you don't care.*/
6375 isds_error isds_delete_box(struct isds_ctx *context,
6376 const struct isds_DbOwnerInfo *box, const struct tm *since,
6377 const struct isds_approval *approval, char **refnumber) {
6378 if (!context) return IE_INVALID_CONTEXT;
6379 zfree(context->long_message);
6380 if (!box || !since) return IE_INVAL;
6382 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6383 box, since, approval, refnumber);
6387 /* Undocumented function.
6388 * @context is session context
6389 * @box is box description to delete
6390 * @approval is optional external approval of box manipulation
6391 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6392 * NULL, if you don't care.*/
6393 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6394 const struct isds_DbOwnerInfo *box,
6395 const struct isds_approval *approval, char **refnumber) {
6396 if (!context) return IE_INVALID_CONTEXT;
6397 zfree(context->long_message);
6398 if (!box) return IE_INVAL;
6400 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6401 box, NULL, approval, refnumber);
6405 /* Update data about given box.
6406 * @context is session context
6407 * @old_box current box description
6408 * @new_box are updated data about @old_box
6409 * @approval is optional external approval of box manipulation
6410 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6411 * NULL, if you don't care.*/
6412 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6413 const struct isds_DbOwnerInfo *old_box,
6414 const struct isds_DbOwnerInfo *new_box,
6415 const struct isds_approval *approval, char **refnumber) {
6416 isds_error err = IE_SUCCESS;
6417 #if HAVE_LIBCURL
6418 xmlNsPtr isds_ns = NULL;
6419 xmlNodePtr request = NULL;
6420 xmlNodePtr node;
6421 #endif
6424 if (!context) return IE_INVALID_CONTEXT;
6425 zfree(context->long_message);
6426 if (!old_box || !new_box) return IE_INVAL;
6429 #if HAVE_LIBCURL
6430 /* Build UpdateDataBoxDescr request */
6431 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6432 if (!request) {
6433 isds_log_message(context,
6434 _("Could build UpdateDataBoxDescr request"));
6435 return IE_ERROR;
6437 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6438 if(!isds_ns) {
6439 isds_log_message(context, _("Could not create ISDS name space"));
6440 xmlFreeNode(request);
6441 return IE_ERROR;
6443 xmlSetNs(request, isds_ns);
6445 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6446 err = insert_DbOwnerInfo(context, old_box, node);
6447 if (err) goto leave;
6449 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6450 err = insert_DbOwnerInfo(context, new_box, node);
6451 if (err) goto leave;
6453 err = insert_GExtApproval(context, approval, request);
6454 if (err) goto leave;
6457 /* Send it to server and process response */
6458 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6459 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6461 leave:
6462 xmlFreeNode(request);
6463 #else /* not HAVE_LIBCURL */
6464 err = IE_NOTSUP;
6465 #endif
6467 return err;
6471 #if HAVE_LIBCURL
6472 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6473 * code
6474 * @context is session context
6475 * @service is SOAP service
6476 * @service_name is name of request in @service
6477 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6478 * @box_id is box ID of interest
6479 * @approval is optional external approval of box manipulation
6480 * @response is server SOAP body response as XML document
6481 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6482 * NULL, if you don't care.
6483 * @return error coded from lower layer, context message will be set up
6484 * appropriately. */
6485 static isds_error build_send_dbid_request_check_response(
6486 struct isds_ctx *context, const isds_service service,
6487 const xmlChar *service_name, const xmlChar *box_id_element,
6488 const xmlChar *box_id, const struct isds_approval *approval,
6489 xmlDocPtr *response, xmlChar **refnumber) {
6491 isds_error err = IE_SUCCESS;
6492 char *service_name_locale = NULL, *box_id_locale = NULL;
6493 xmlNodePtr request = NULL, node;
6494 xmlNsPtr isds_ns = NULL;
6496 if (!context) return IE_INVALID_CONTEXT;
6497 if (!service_name || !box_id) return IE_INVAL;
6498 if (!response) return IE_INVAL;
6500 /* Free output argument */
6501 xmlFreeDoc(*response); *response = NULL;
6503 /* Prepare strings */
6504 service_name_locale = _isds_utf82locale((char*)service_name);
6505 if (!service_name_locale) {
6506 err = IE_NOMEM;
6507 goto leave;
6509 box_id_locale = _isds_utf82locale((char*)box_id);
6510 if (!box_id_locale) {
6511 err = IE_NOMEM;
6512 goto leave;
6515 /* Build request */
6516 request = xmlNewNode(NULL, service_name);
6517 if (!request) {
6518 isds_printf_message(context,
6519 _("Could not build %s request for %s box"), service_name_locale,
6520 box_id_locale);
6521 err = IE_ERROR;
6522 goto leave;
6524 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6525 if(!isds_ns) {
6526 isds_log_message(context, _("Could not create ISDS name space"));
6527 err = IE_ERROR;
6528 goto leave;
6530 xmlSetNs(request, isds_ns);
6532 /* Add XSD:tIdDbInput children */
6533 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6534 INSERT_STRING(request, box_id_element, box_id);
6535 err = insert_GExtApproval(context, approval, request);
6536 if (err) goto leave;
6538 /* Send request and check response*/
6539 err = send_destroy_request_check_response(context,
6540 service, service_name, &request, response, refnumber, NULL);
6542 leave:
6543 free(service_name_locale);
6544 free(box_id_locale);
6545 xmlFreeNode(request);
6546 return err;
6548 #endif /* HAVE_LIBCURL */
6551 /* Get data about all users assigned to given box.
6552 * @context is session context
6553 * @box_id is box ID
6554 * @users is automatically reallocated list of struct isds_DbUserInfo */
6555 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6556 struct isds_list **users) {
6557 isds_error err = IE_SUCCESS;
6558 #if HAVE_LIBCURL
6559 xmlDocPtr response = NULL;
6560 xmlXPathContextPtr xpath_ctx = NULL;
6561 xmlXPathObjectPtr result = NULL;
6562 int i;
6563 struct isds_list *item, *prev_item = NULL;
6564 #endif
6566 if (!context) return IE_INVALID_CONTEXT;
6567 zfree(context->long_message);
6568 if (!users || !box_id) return IE_INVAL;
6569 isds_list_free(users);
6572 #if HAVE_LIBCURL
6573 /* Do request and check for success */
6574 err = build_send_dbid_request_check_response(context,
6575 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6576 BAD_CAST box_id, NULL, &response, NULL);
6577 if (err) goto leave;
6580 /* Extract data */
6581 /* Prepare structure */
6582 xpath_ctx = xmlXPathNewContext(response);
6583 if (!xpath_ctx) {
6584 err = IE_ERROR;
6585 goto leave;
6587 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6588 err = IE_ERROR;
6589 goto leave;
6592 /* Set context node */
6593 result = xmlXPathEvalExpression(BAD_CAST
6594 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6595 xpath_ctx);
6596 if (!result) {
6597 err = IE_ERROR;
6598 goto leave;
6600 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6601 /* Iterate over all users */
6602 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6604 /* Prepare structure */
6605 item = calloc(1, sizeof(*item));
6606 if (!item) {
6607 err = IE_NOMEM;
6608 goto leave;
6610 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6611 if (i == 0) *users = item;
6612 else prev_item->next = item;
6613 prev_item = item;
6615 /* Extract it */
6616 xpath_ctx->node = result->nodesetval->nodeTab[i];
6617 err = extract_DbUserInfo(context,
6618 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6619 if (err) goto leave;
6623 leave:
6624 if (err) {
6625 isds_list_free(users);
6628 xmlXPathFreeObject(result);
6629 xmlXPathFreeContext(xpath_ctx);
6630 xmlFreeDoc(response);
6632 if (!err)
6633 isds_log(ILF_ISDS, ILL_DEBUG,
6634 _("GetDataBoxUsers request processed by server "
6635 "successfully.\n"));
6636 #else /* not HAVE_LIBCURL */
6637 err = IE_NOTSUP;
6638 #endif
6640 return err;
6644 /* Update data about user assigned to given box.
6645 * @context is session context
6646 * @box is box identification
6647 * @old_user identifies user to update
6648 * @new_user are updated data about @old_user
6649 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6650 * NULL, if you don't care.*/
6651 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6652 const struct isds_DbOwnerInfo *box,
6653 const struct isds_DbUserInfo *old_user,
6654 const struct isds_DbUserInfo *new_user,
6655 char **refnumber) {
6656 isds_error err = IE_SUCCESS;
6657 #if HAVE_LIBCURL
6658 xmlNsPtr isds_ns = NULL;
6659 xmlNodePtr request = NULL;
6660 xmlNodePtr node;
6661 #endif
6664 if (!context) return IE_INVALID_CONTEXT;
6665 zfree(context->long_message);
6666 if (!box || !old_user || !new_user) return IE_INVAL;
6669 #if HAVE_LIBCURL
6670 /* Build UpdateDataBoxUser request */
6671 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6672 if (!request) {
6673 isds_log_message(context,
6674 _("Could build UpdateDataBoxUser request"));
6675 return IE_ERROR;
6677 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6678 if(!isds_ns) {
6679 isds_log_message(context, _("Could not create ISDS name space"));
6680 xmlFreeNode(request);
6681 return IE_ERROR;
6683 xmlSetNs(request, isds_ns);
6685 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6686 err = insert_DbOwnerInfo(context, box, node);
6687 if (err) goto leave;
6689 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6690 err = insert_DbUserInfo(context, old_user, node);
6691 if (err) goto leave;
6693 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6694 err = insert_DbUserInfo(context, new_user, node);
6695 if (err) goto leave;
6697 /* Send it to server and process response */
6698 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6699 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6701 leave:
6702 xmlFreeNode(request);
6703 #else /* not HAVE_LIBCURL */
6704 err = IE_NOTSUP;
6705 #endif
6707 return err;
6711 /* Undocumented function.
6712 * @context is session context
6713 * @box_id is UTF-8 encoded box identifier
6714 * @token is UTF-8 encoded temporary password
6715 * @user_id outputs UTF-8 encoded reallocated user identifier
6716 * @password outpus UTF-8 encoded reallocated user password
6717 * Output arguments will be nulled in case of error */
6718 isds_error isds_activate(struct isds_ctx *context,
6719 const char *box_id, const char *token,
6720 char **user_id, char **password) {
6721 isds_error err = IE_SUCCESS;
6722 #if HAVE_LIBCURL
6723 xmlNsPtr isds_ns = NULL;
6724 xmlNodePtr request = NULL, node;
6725 xmlDocPtr response = NULL;
6726 xmlXPathContextPtr xpath_ctx = NULL;
6727 xmlXPathObjectPtr result = NULL;
6728 #endif
6731 if (!context) return IE_INVALID_CONTEXT;
6732 zfree(context->long_message);
6734 if (user_id) zfree(*user_id);
6735 if (password) zfree(*password);
6737 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6740 #if HAVE_LIBCURL
6741 /* Build Activate request */
6742 request = xmlNewNode(NULL, BAD_CAST "Activate");
6743 if (!request) {
6744 isds_log_message(context, _("Could build Activate request"));
6745 return IE_ERROR;
6747 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6748 if(!isds_ns) {
6749 isds_log_message(context, _("Could not create ISDS name space"));
6750 xmlFreeNode(request);
6751 return IE_ERROR;
6753 xmlSetNs(request, isds_ns);
6755 INSERT_STRING(request, "dbAccessDataId", token);
6756 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6757 INSERT_STRING(request, "dbID", box_id);
6760 /* Send request and check response*/
6761 err = send_destroy_request_check_response(context,
6762 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6763 &response, NULL, NULL);
6764 if (err) goto leave;
6767 /* Extract data */
6768 xpath_ctx = xmlXPathNewContext(response);
6769 if (!xpath_ctx) {
6770 err = IE_ERROR;
6771 goto leave;
6773 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6774 err = IE_ERROR;
6775 goto leave;
6777 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6778 xpath_ctx);
6779 if (!result) {
6780 err = IE_ERROR;
6781 goto leave;
6783 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6784 isds_log_message(context, _("Missing ActivateResponse element"));
6785 err = IE_ISDS;
6786 goto leave;
6788 if (result->nodesetval->nodeNr > 1) {
6789 isds_log_message(context, _("Multiple ActivateResponse element"));
6790 err = IE_ISDS;
6791 goto leave;
6793 xpath_ctx->node = result->nodesetval->nodeTab[0];
6794 xmlXPathFreeObject(result); result = NULL;
6796 EXTRACT_STRING("isds:userId", *user_id);
6797 if (!*user_id)
6798 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6799 "but did not return `userId' element.\n"));
6801 EXTRACT_STRING("isds:password", *password);
6802 if (!*password)
6803 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6804 "but did not return `password' element.\n"));
6806 leave:
6807 xmlXPathFreeObject(result);
6808 xmlXPathFreeContext(xpath_ctx);
6809 xmlFreeDoc(response);
6810 xmlFreeNode(request);
6812 if (!err)
6813 isds_log(ILF_ISDS, ILL_DEBUG,
6814 _("Activate request processed by server successfully.\n"));
6815 #else /* not HAVE_LIBCURL */
6816 err = IE_NOTSUP;
6817 #endif
6819 return err;
6823 /* Reset credentials of user assigned to given box.
6824 * @context is session context
6825 * @box is box identification
6826 * @user identifies user to reset password
6827 * @fee_paid is true if fee has been paid, false otherwise
6828 * @approval is optional external approval of box manipulation
6829 * @credentials_delivery is NULL if new password should be delivered off-line
6830 * to the user. It is valid pointer if user should obtain new password on-line
6831 * on dedicated web server. Then input @credentials_delivery.email value is
6832 * user's e-mail address user must provide to dedicated web server together
6833 * with @credentials_delivery.token. The output reallocated token user needs
6834 * to use to authorize on the web server to view his new password. Output
6835 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6836 * ISDS changed up on this call. (No reason why server could change the name
6837 * is known now.)
6838 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6839 * NULL, if you don't care.*/
6840 isds_error isds_reset_password(struct isds_ctx *context,
6841 const struct isds_DbOwnerInfo *box,
6842 const struct isds_DbUserInfo *user,
6843 const _Bool fee_paid, const struct isds_approval *approval,
6844 struct isds_credentials_delivery *credentials_delivery,
6845 char **refnumber) {
6846 isds_error err = IE_SUCCESS;
6847 #if HAVE_LIBCURL
6848 xmlNsPtr isds_ns = NULL;
6849 xmlNodePtr request = NULL, node;
6850 xmlDocPtr response = NULL;
6851 #endif
6854 if (!context) return IE_INVALID_CONTEXT;
6855 zfree(context->long_message);
6857 if (credentials_delivery) {
6858 zfree(credentials_delivery->token);
6859 zfree(credentials_delivery->new_user_name);
6861 if (!box || !user) return IE_INVAL;
6864 #if HAVE_LIBCURL
6865 /* Build NewAccessData request */
6866 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6867 if (!request) {
6868 isds_log_message(context,
6869 _("Could build NewAccessData request"));
6870 return IE_ERROR;
6872 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6873 if(!isds_ns) {
6874 isds_log_message(context, _("Could not create ISDS name space"));
6875 xmlFreeNode(request);
6876 return IE_ERROR;
6878 xmlSetNs(request, isds_ns);
6880 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6881 err = insert_DbOwnerInfo(context, box, node);
6882 if (err) goto leave;
6884 INSERT_ELEMENT(node, request, "dbUserInfo");
6885 err = insert_DbUserInfo(context, user, node);
6886 if (err) goto leave;
6888 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6890 err = insert_credentials_delivery(context, credentials_delivery, request);
6891 if (err) goto leave;
6893 err = insert_GExtApproval(context, approval, request);
6894 if (err) goto leave;
6896 /* Send request and check response*/
6897 err = send_destroy_request_check_response(context,
6898 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6899 &response, (xmlChar **) refnumber, NULL);
6900 if (err) goto leave;
6903 /* Extract optional token */
6904 err = extract_credentials_delivery(context, credentials_delivery,
6905 response, "NewAccessData");
6907 leave:
6908 xmlFreeDoc(response);
6909 xmlFreeNode(request);
6911 if (!err)
6912 isds_log(ILF_ISDS, ILL_DEBUG,
6913 _("NewAccessData request processed by server "
6914 "successfully.\n"));
6915 #else /* not HAVE_LIBCURL */
6916 err = IE_NOTSUP;
6917 #endif
6919 return err;
6923 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6924 * code, destroy response and log success.
6925 * @context is ISDS session context.
6926 * @service_name is name of SERVICE_DB_MANIPULATION service
6927 * @box is box identification
6928 * @user identifies user to remove
6929 * @credentials_delivery is NULL if new user's password should be delivered
6930 * off-line to the user. It is valid pointer if user should obtain new
6931 * password on-line on dedicated web server. Then input
6932 * @credentials_delivery.email value is user's e-mail address user must
6933 * provide to dedicated web server together with @credentials_delivery.token.
6934 * The output reallocated token user needs to use to authorize on the web
6935 * server to view his new password. Output reallocated
6936 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6937 * assingned or changed up on this call.
6938 * @approval is optional external approval of box manipulation
6939 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6940 * NULL, if you don't care. */
6941 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6942 struct isds_ctx *context, const xmlChar *service_name,
6943 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6944 struct isds_credentials_delivery *credentials_delivery,
6945 const struct isds_approval *approval, xmlChar **refnumber) {
6946 isds_error err = IE_SUCCESS;
6947 #if HAVE_LIBCURL
6948 xmlNsPtr isds_ns = NULL;
6949 xmlNodePtr request = NULL, node;
6950 xmlDocPtr response = NULL;
6951 #endif
6954 if (!context) return IE_INVALID_CONTEXT;
6955 zfree(context->long_message);
6956 if (credentials_delivery) {
6957 zfree(credentials_delivery->token);
6958 zfree(credentials_delivery->new_user_name);
6960 if (!service_name || service_name[0] == '\0' || !box || !user)
6961 return IE_INVAL;
6964 #if HAVE_LIBCURL
6965 /* Build NewAccessData or similar request */
6966 request = xmlNewNode(NULL, service_name);
6967 if (!request) {
6968 char *service_name_locale = _isds_utf82locale((char *) service_name);
6969 isds_printf_message(context, _("Could not build %s request"),
6970 service_name_locale);
6971 free(service_name_locale);
6972 return IE_ERROR;
6974 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6975 if(!isds_ns) {
6976 isds_log_message(context, _("Could not create ISDS name space"));
6977 xmlFreeNode(request);
6978 return IE_ERROR;
6980 xmlSetNs(request, isds_ns);
6982 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6983 err = insert_DbOwnerInfo(context, box, node);
6984 if (err) goto leave;
6986 INSERT_ELEMENT(node, request, "dbUserInfo");
6987 err = insert_DbUserInfo(context, user, node);
6988 if (err) goto leave;
6990 err = insert_credentials_delivery(context, credentials_delivery, request);
6991 if (err) goto leave;
6993 err = insert_GExtApproval(context, approval, request);
6994 if (err) goto leave;
6997 /* Send request and check response*/
6998 err = send_destroy_request_check_response(context,
6999 SERVICE_DB_MANIPULATION, service_name, &request, &response,
7000 refnumber, NULL);
7002 xmlFreeNode(request);
7003 request = NULL;
7005 /* Pick up credentials_delivery if requested */
7006 err = extract_credentials_delivery(context, credentials_delivery, response,
7007 (char *)service_name);
7009 leave:
7010 xmlFreeDoc(response);
7011 if (request) xmlFreeNode(request);
7013 if (!err) {
7014 char *service_name_locale = _isds_utf82locale((char *) service_name);
7015 isds_log(ILF_ISDS, ILL_DEBUG,
7016 _("%s request processed by server successfully.\n"),
7017 service_name_locale);
7018 free(service_name_locale);
7020 #else /* not HAVE_LIBCURL */
7021 err = IE_NOTSUP;
7022 #endif
7024 return err;
7028 /* Assign new user to given box.
7029 * @context is session context
7030 * @box is box identification
7031 * @user defines new user to add
7032 * @credentials_delivery is NULL if new user's password should be delivered
7033 * off-line to the user. It is valid pointer if user should obtain new
7034 * password on-line on dedicated web server. Then input
7035 * @credentials_delivery.email value is user's e-mail address user must
7036 * provide to dedicated web server together with @credentials_delivery.token.
7037 * The output reallocated token user needs to use to authorize on the web
7038 * server to view his new password. Output reallocated
7039 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7040 * assingned up on this call.
7041 * @approval is optional external approval of box manipulation
7042 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7043 * NULL, if you don't care.*/
7044 isds_error isds_add_user(struct isds_ctx *context,
7045 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7046 struct isds_credentials_delivery *credentials_delivery,
7047 const struct isds_approval *approval, char **refnumber) {
7048 return build_send_manipulationboxuser_request_check_drop_response(context,
7049 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
7050 approval, (xmlChar **) refnumber);
7054 /* Remove user assigned to given box.
7055 * @context is session context
7056 * @box is box identification
7057 * @user identifies user to remove
7058 * @approval is optional external approval of box manipulation
7059 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7060 * NULL, if you don't care.*/
7061 isds_error isds_delete_user(struct isds_ctx *context,
7062 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7063 const struct isds_approval *approval, char **refnumber) {
7064 return build_send_manipulationboxuser_request_check_drop_response(context,
7065 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
7066 (xmlChar **) refnumber);
7070 /* Get list of boxes in ZIP archive.
7071 * @context is session context
7072 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7073 * System recognizes following values currently: ALL (all boxes), UPG
7074 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7075 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7076 * commercial messages). This argument is a string because specification
7077 * states new values can appear in the future. Not all list types are
7078 * available to all users.
7079 * @buffer is automatically reallocated memory to store the list of boxes. The
7080 * list is zipped CSV file.
7081 * @buffer_length is size of @buffer data in bytes.
7082 * In case of error @buffer will be freed and @buffer_length will be
7083 * undefined.*/
7084 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7085 const char *list_identifier, void **buffer, size_t *buffer_length) {
7086 isds_error err = IE_SUCCESS;
7087 #if HAVE_LIBCURL
7088 xmlNsPtr isds_ns = NULL;
7089 xmlNodePtr request = NULL, node;
7090 xmlDocPtr response = NULL;
7091 xmlXPathContextPtr xpath_ctx = NULL;
7092 xmlXPathObjectPtr result = NULL;
7093 char *string = NULL;
7094 #endif
7097 if (!context) return IE_INVALID_CONTEXT;
7098 zfree(context->long_message);
7099 if (buffer) zfree(*buffer);
7100 if (!buffer || !buffer_length) return IE_INVAL;
7103 #if HAVE_LIBCURL
7104 /* Check if connection is established
7105 * TODO: This check should be done downstairs. */
7106 if (!context->curl) return IE_CONNECTION_CLOSED;
7109 /* Build AuthenticateMessage request */
7110 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7111 if (!request) {
7112 isds_log_message(context,
7113 _("Could not build GetDataBoxList request"));
7114 return IE_ERROR;
7116 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7117 if(!isds_ns) {
7118 isds_log_message(context, _("Could not create ISDS name space"));
7119 xmlFreeNode(request);
7120 return IE_ERROR;
7122 xmlSetNs(request, isds_ns);
7123 INSERT_STRING(request, "dblType", list_identifier);
7125 /* Send request to server and process response */
7126 err = send_destroy_request_check_response(context,
7127 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7128 &response, NULL, NULL);
7129 if (err) goto leave;
7132 /* Extract Base-64 encoded ZIP file */
7133 xpath_ctx = xmlXPathNewContext(response);
7134 if (!xpath_ctx) {
7135 err = IE_ERROR;
7136 goto leave;
7138 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7139 err = IE_ERROR;
7140 goto leave;
7142 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7144 /* Decode non-empty archive */
7145 if (string && string[0] != '\0') {
7146 *buffer_length = _isds_b64decode(string, buffer);
7147 if (*buffer_length == (size_t) -1) {
7148 isds_printf_message(context,
7149 _("Error while Base64-decoding box list archive"));
7150 err = IE_ERROR;
7151 goto leave;
7156 leave:
7157 free(string);
7158 xmlXPathFreeObject(result);
7159 xmlXPathFreeContext(xpath_ctx);
7160 xmlFreeDoc(response);
7161 xmlFreeNode(request);
7163 if (!err) {
7164 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7165 "processed by server successfully.\n"));
7167 #else /* not HAVE_LIBCURL */
7168 err = IE_NOTSUP;
7169 #endif
7171 return err;
7175 /* Find boxes suiting given criteria.
7176 * @criteria is filter. You should fill in at least some members.
7177 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7178 * possibly empty. Input NULL or valid old structure.
7179 * @return:
7180 * IE_SUCCESS if search succeeded, @boxes contains useful data
7181 * IE_NOEXIST if no such box exists, @boxes will be NULL
7182 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7183 * contains still valid data
7184 * other code if something bad happens. @boxes will be NULL. */
7185 isds_error isds_FindDataBox(struct isds_ctx *context,
7186 const struct isds_DbOwnerInfo *criteria,
7187 struct isds_list **boxes) {
7188 isds_error err = IE_SUCCESS;
7189 #if HAVE_LIBCURL
7190 _Bool truncated = 0;
7191 xmlNsPtr isds_ns = NULL;
7192 xmlNodePtr request = NULL;
7193 xmlDocPtr response = NULL;
7194 xmlChar *code = NULL, *message = NULL;
7195 xmlNodePtr db_owner_info;
7196 xmlXPathContextPtr xpath_ctx = NULL;
7197 xmlXPathObjectPtr result = NULL;
7198 xmlChar *string = NULL;
7199 #endif
7202 if (!context) return IE_INVALID_CONTEXT;
7203 zfree(context->long_message);
7204 if (!boxes) return IE_INVAL;
7205 isds_list_free(boxes);
7207 if (!criteria) {
7208 return IE_INVAL;
7211 #if HAVE_LIBCURL
7212 /* Check if connection is established
7213 * TODO: This check should be done downstairs. */
7214 if (!context->curl) return IE_CONNECTION_CLOSED;
7217 /* Build FindDataBox request */
7218 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
7219 if (!request) {
7220 isds_log_message(context,
7221 _("Could build FindDataBox request"));
7222 return IE_ERROR;
7224 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7225 if(!isds_ns) {
7226 isds_log_message(context, _("Could not create ISDS name space"));
7227 xmlFreeNode(request);
7228 return IE_ERROR;
7230 xmlSetNs(request, isds_ns);
7231 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7232 if (!db_owner_info) {
7233 isds_log_message(context, _("Could not add dbOwnerInfo child to "
7234 "FindDataBox element"));
7235 xmlFreeNode(request);
7236 return IE_ERROR;
7239 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
7240 if (err) goto leave;
7243 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
7245 /* Sent request */
7246 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7248 /* Destroy request */
7249 xmlFreeNode(request); request = NULL;
7251 if (err) {
7252 isds_log(ILF_ISDS, ILL_DEBUG,
7253 _("Processing ISDS response on FindDataBox "
7254 "request failed\n"));
7255 goto leave;
7258 /* Check for response status */
7259 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7260 &code, &message, NULL);
7261 if (err) {
7262 isds_log(ILF_ISDS, ILL_DEBUG,
7263 _("ISDS response on FindDataBox request is missing status\n"));
7264 goto leave;
7267 /* Request processed, but nothing found */
7268 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7269 !xmlStrcmp(code, BAD_CAST "5001")) {
7270 char *code_locale = _isds_utf82locale((char*)code);
7271 char *message_locale = _isds_utf82locale((char*)message);
7272 isds_log(ILF_ISDS, ILL_DEBUG,
7273 _("Server did not found any box on FindDataBox request "
7274 "(code=%s, message=%s)\n"), code_locale, message_locale);
7275 isds_log_message(context, message_locale);
7276 free(code_locale);
7277 free(message_locale);
7278 err = IE_NOEXIST;
7279 goto leave;
7282 /* Warning, not a error */
7283 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7284 char *code_locale = _isds_utf82locale((char*)code);
7285 char *message_locale = _isds_utf82locale((char*)message);
7286 isds_log(ILF_ISDS, ILL_DEBUG,
7287 _("Server truncated response on FindDataBox request "
7288 "(code=%s, message=%s)\n"), code_locale, message_locale);
7289 isds_log_message(context, message_locale);
7290 free(code_locale);
7291 free(message_locale);
7292 truncated = 1;
7295 /* Other error */
7296 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7297 char *code_locale = _isds_utf82locale((char*)code);
7298 char *message_locale = _isds_utf82locale((char*)message);
7299 isds_log(ILF_ISDS, ILL_DEBUG,
7300 _("Server refused FindDataBox request "
7301 "(code=%s, message=%s)\n"), code_locale, message_locale);
7302 isds_log_message(context, message_locale);
7303 free(code_locale);
7304 free(message_locale);
7305 err = IE_ISDS;
7306 goto leave;
7309 xpath_ctx = xmlXPathNewContext(response);
7310 if (!xpath_ctx) {
7311 err = IE_ERROR;
7312 goto leave;
7314 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7315 err = IE_ERROR;
7316 goto leave;
7319 /* Extract boxes if they present */
7320 result = xmlXPathEvalExpression(BAD_CAST
7321 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7322 xpath_ctx);
7323 if (!result) {
7324 err = IE_ERROR;
7325 goto leave;
7327 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7328 struct isds_list *item, *prev_item = NULL;
7329 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7330 item = calloc(1, sizeof(*item));
7331 if (!item) {
7332 err = IE_NOMEM;
7333 goto leave;
7336 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7337 if (i == 0) *boxes = item;
7338 else prev_item->next = item;
7339 prev_item = item;
7341 xpath_ctx->node = result->nodesetval->nodeTab[i];
7342 err = extract_DbOwnerInfo(context,
7343 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7344 if (err) goto leave;
7348 leave:
7349 if (err) {
7350 isds_list_free(boxes);
7351 } else {
7352 if (truncated) err = IE_2BIG;
7355 free(string);
7356 xmlFreeNode(request);
7357 xmlXPathFreeObject(result);
7358 xmlXPathFreeContext(xpath_ctx);
7360 free(code);
7361 free(message);
7362 xmlFreeDoc(response);
7364 if (!err)
7365 isds_log(ILF_ISDS, ILL_DEBUG,
7366 _("FindDataBox request processed by server successfully.\n"));
7367 #else /* not HAVE_LIBCURL */
7368 err = IE_NOTSUP;
7369 #endif
7371 return err;
7375 #if HAVE_LIBCURL
7376 /* Convert a string with match markers into a plain string with list of
7377 * pointers to the matches
7378 * @string is an UTF-8 encoded non-constant string with match markers
7379 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7380 * The markers will be removed from the string.
7381 * @starts is a reallocated list of static pointers into the @string pointing
7382 * to places where match start markers occured.
7383 * @ends is a reallocated list of static pointers into the @string pointing
7384 * to places where match end markers occured.
7385 * @return IE_SUCCESS in case of no failure. */
7386 static isds_error interpret_matches(xmlChar *string,
7387 struct isds_list **starts, struct isds_list **ends) {
7388 isds_error err = IE_SUCCESS;
7389 xmlChar *pointer, *destination, *source;
7390 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7392 isds_list_free(starts);
7393 isds_list_free(ends);
7394 if (NULL == starts || NULL == ends) return IE_INVAL;
7395 if (NULL == string) return IE_SUCCESS;
7397 for (pointer = string; *pointer != '\0';) {
7398 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7399 /* Remove the start marker */
7400 for (source = pointer + 14, destination = pointer;
7401 *source != '\0'; source++, destination++) {
7402 *destination = *source;
7404 *destination = '\0';
7405 /* Append the pointer into the list */
7406 item = calloc(1, sizeof(*item));
7407 if (!item) {
7408 err = IE_NOMEM;
7409 goto leave;
7411 item->destructor = (void (*)(void **))NULL;
7412 item->data = pointer;
7413 if (NULL == prev_start) *starts = item;
7414 else prev_start->next = item;
7415 prev_start = item;
7416 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7417 /* Remove the end marker */
7418 for (source = pointer + 12, destination = pointer;
7419 *source != '\0'; source++, destination++) {
7420 *destination = *source;
7422 *destination = '\0';
7423 /* Append the pointer into the list */
7424 item = calloc(1, sizeof(*item));
7425 if (!item) {
7426 err = IE_NOMEM;
7427 goto leave;
7429 item->destructor = (void (*)(void **))NULL;
7430 item->data = pointer;
7431 if (NULL == prev_end) *ends = item;
7432 else prev_end->next = item;
7433 prev_end = item;
7434 } else {
7435 pointer++;
7439 leave:
7440 if (err) {
7441 isds_list_free(starts);
7442 isds_list_free(ends);
7444 return err;
7448 /* Convert isds:dbResult XML tree into structure
7449 * @context is ISDS context.
7450 * @fulltext_result is automatically reallocated found box structure.
7451 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7452 * @collect_matches is true to interpret match markers.
7453 * In case of error @result will be freed. */
7454 static isds_error extract_dbResult(struct isds_ctx *context,
7455 struct isds_fulltext_result **fulltext_result,
7456 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7457 isds_error err = IE_SUCCESS;
7458 xmlXPathObjectPtr result = NULL;
7459 char *string = NULL;
7461 if (NULL == context) return IE_INVALID_CONTEXT;
7462 if (NULL == fulltext_result) return IE_INVAL;
7463 isds_fulltext_result_free(fulltext_result);
7464 if (!xpath_ctx) return IE_INVAL;
7467 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7468 if (NULL == *fulltext_result) {
7469 err = IE_NOMEM;
7470 goto leave;
7473 /* Extract data */
7474 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7476 EXTRACT_STRING("isds:dbType", string);
7477 if (NULL == string) {
7478 err = IE_ISDS;
7479 isds_log_message(context, _("Empty isds:dbType element"));
7480 goto leave;
7482 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7483 if (err) {
7484 if (err == IE_ENUM) {
7485 err = IE_ISDS;
7486 char *string_locale = _isds_utf82locale(string);
7487 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7488 string_locale);
7489 free(string_locale);
7491 goto leave;
7493 zfree(string);
7495 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7496 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7498 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7499 if (err) goto leave;
7501 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7502 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7503 (*fulltext_result)->dbEffectiveOVM);
7505 EXTRACT_STRING("isds:dbSendOptions", string);
7506 if (NULL == string) {
7507 err = IE_ISDS;
7508 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7509 goto leave;
7511 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7512 (*fulltext_result)->active = 1;
7513 (*fulltext_result)->public_sending = 1;
7514 (*fulltext_result)->commercial_sending = 0;
7515 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7516 (*fulltext_result)->active = 1;
7517 (*fulltext_result)->public_sending = 1;
7518 (*fulltext_result)->commercial_sending = 1;
7519 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7520 (*fulltext_result)->active = 1;
7521 (*fulltext_result)->public_sending = 0;
7522 (*fulltext_result)->commercial_sending = 1;
7523 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7524 (*fulltext_result)->active = 1;
7525 (*fulltext_result)->public_sending = 0;
7526 (*fulltext_result)->commercial_sending = 0;
7527 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7528 (*fulltext_result)->active = 0;
7529 (*fulltext_result)->public_sending = 0;
7530 (*fulltext_result)->commercial_sending = 0;
7531 } else {
7532 err = IE_ISDS;
7533 char *string_locale = _isds_utf82locale(string);
7534 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7535 string_locale);
7536 free(string_locale);
7537 goto leave;
7539 zfree(string);
7541 /* Interpret match marks */
7542 if (collect_matches) {
7543 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7544 &((*fulltext_result)->name_match_start),
7545 &((*fulltext_result)->name_match_end));
7546 if (err) goto leave;
7547 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7548 &((*fulltext_result)->address_match_start),
7549 &((*fulltext_result)->address_match_end));
7550 if (err) goto leave;
7553 leave:
7554 if (err) isds_fulltext_result_free(fulltext_result);
7555 free(string);
7556 xmlXPathFreeObject(result);
7557 return err;
7559 #endif /* HAVE_LIBCURL */
7562 /* Find boxes matching a given full-text criteria.
7563 * @context is a session context
7564 * @query is a non-empty string which consists of words to search
7565 * @target selects box attributes to search for @query words. Pass NULL if you
7566 * don't care.
7567 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7568 * to search in all box types. Pass NULL to let server to use default value
7569 * which is DBTYPE_SYSTEM.
7570 * @page_size defines count of boxes to constitute a response page. It counts
7571 * from zero. Pass NULL to let server to use a default value (50 now).
7572 * @page_number defines ordinar number of the response page to return. It
7573 * counts from zero. Pass NULL to let server to use a default value (0 now).
7574 * @track_matches points to true for marking @query words found in the box
7575 * attributes. It points to false for not marking. Pass NULL to let the server
7576 * to use default value (false now).
7577 * @total_matching_boxes outputs reallocated number of all boxes matching the
7578 * query. Will be pointer to NULL if server did not provide the value.
7579 * Pass NULL if you don't care.
7580 * @current_page_beginning outputs reallocated ordinar number of the first box
7581 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7582 * server did not provide the value. Pass NULL if you don't care.
7583 * @current_page_size outputs reallocated count of boxes in the this @boxes
7584 * page. It will be pointer to NULL if the server did not provide the value.
7585 * Pass NULL if you don't care.
7586 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7587 * is the last one, false if more boxes match, NULL if the server did not
7588 * provude the value. Pass NULL if you don't care.
7589 * @boxes outputs reallocated list of isds_fulltext_result structures,
7590 * possibly empty.
7591 * @return:
7592 * IE_SUCCESS if search succeeded
7593 * IE_2BIG if @page_size is too large
7594 * other code if something bad happens; output arguments will be NULL. */
7595 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7596 const char *query,
7597 const isds_fulltext_target *target,
7598 const isds_DbType *box_type,
7599 const unsigned long int *page_size,
7600 const unsigned long int *page_number,
7601 const _Bool *track_matches,
7602 unsigned long int **total_matching_boxes,
7603 unsigned long int **current_page_beginning,
7604 unsigned long int **current_page_size,
7605 _Bool **last_page,
7606 struct isds_list **boxes) {
7607 isds_error err = IE_SUCCESS;
7608 #if HAVE_LIBCURL
7609 xmlNsPtr isds_ns = NULL;
7610 xmlNodePtr request = NULL;
7611 xmlDocPtr response = NULL;
7612 xmlNodePtr node;
7613 xmlXPathContextPtr xpath_ctx = NULL;
7614 xmlXPathObjectPtr result = NULL;
7615 const xmlChar *static_string = NULL;
7616 xmlChar *string = NULL;
7618 const xmlChar *codes[] = {
7619 BAD_CAST "1004",
7620 BAD_CAST "1152",
7621 BAD_CAST "1153",
7622 BAD_CAST "1154",
7623 BAD_CAST "1155",
7624 BAD_CAST "1156",
7625 BAD_CAST "9002",
7626 NULL
7628 const char *meanings[] = {
7629 N_("You are not allowed to perform the search"),
7630 N_("The query string is empty"),
7631 N_("Searched box ID is malformed"),
7632 N_("Searched organization ID is malformed"),
7633 N_("Invalid input"),
7634 N_("Requested page size is too large"),
7635 N_("Search engine internal error")
7637 const isds_error errors[] = {
7638 IE_ISDS,
7639 IE_INVAL,
7640 IE_INVAL,
7641 IE_INVAL,
7642 IE_INVAL,
7643 IE_2BIG,
7644 IE_ISDS
7646 struct code_map_isds_error map = {
7647 .codes = codes,
7648 .meanings = meanings,
7649 .errors = errors
7651 #endif
7654 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7655 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7656 if (NULL != current_page_size) zfree(*current_page_size);
7657 if (NULL != last_page) zfree(*last_page);
7658 isds_list_free(boxes);
7660 if (NULL == context) return IE_INVALID_CONTEXT;
7661 zfree(context->long_message);
7663 if (NULL == boxes) return IE_INVAL;
7665 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
7666 isds_log_message(context, _("Query string must be non-empty"));
7667 return IE_INVAL;
7670 #if HAVE_LIBCURL
7671 /* Check if connection is established
7672 * TODO: This check should be done downstairs. */
7673 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7675 /* Build FindDataBox request */
7676 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
7677 if (NULL == request) {
7678 isds_log_message(context,
7679 _("Could not build ISDSSearch2 request"));
7680 return IE_ERROR;
7682 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7683 if(NULL == isds_ns) {
7684 isds_log_message(context, _("Could not create ISDS name space"));
7685 xmlFreeNode(request);
7686 return IE_ERROR;
7688 xmlSetNs(request, isds_ns);
7690 INSERT_STRING(request, "searchText", query);
7692 if (NULL != target) {
7693 static_string = isds_fulltext_target2string(*(target));
7694 if (NULL == static_string) {
7695 isds_printf_message(context, _("Invalid target value: %d"),
7696 *(target));
7697 err = IE_ENUM;
7698 goto leave;
7701 INSERT_STRING(request, "searchType", static_string);
7702 static_string = NULL;
7704 if (NULL != box_type) {
7705 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7706 if (DBTYPE_SYSTEM == *box_type) {
7707 static_string = BAD_CAST "ALL";
7708 } else {
7709 static_string = isds_DbType2string(*(box_type));
7710 if (NULL == static_string) {
7711 isds_printf_message(context, _("Invalid box type value: %d"),
7712 *(box_type));
7713 err = IE_ENUM;
7714 goto leave;
7718 INSERT_STRING(request, "searchScope", static_string);
7719 static_string = NULL;
7721 INSERT_ULONGINT(request, "page", page_number, string);
7722 INSERT_ULONGINT(request, "pageSize", page_size, string);
7723 INSERT_BOOLEAN(request, "highlighting", track_matches);
7725 /* Send request and check response */
7726 err = send_destroy_request_check_response(context,
7727 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
7728 &request, &response, NULL, &map);
7729 if (err) goto leave;
7731 /* Parse response */
7732 xpath_ctx = xmlXPathNewContext(response);
7733 if (NULL == xpath_ctx) {
7734 err = IE_ERROR;
7735 goto leave;
7737 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7738 err = IE_ERROR;
7739 goto leave;
7741 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
7742 xpath_ctx);
7743 if (!result) {
7744 err = IE_ERROR;
7745 goto leave;
7747 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7748 isds_log_message(context, _("Missing ISDSSearch2 element"));
7749 err = IE_ISDS;
7750 goto leave;
7752 if (result->nodesetval->nodeNr > 1) {
7753 isds_log_message(context, _("Multiple ISDSSearch2 element"));
7754 err = IE_ISDS;
7755 goto leave;
7757 xpath_ctx->node = result->nodesetval->nodeTab[0];
7758 xmlXPathFreeObject(result); result = NULL;
7761 /* Extract counters */
7762 if (NULL != total_matching_boxes) {
7763 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
7765 if (NULL != current_page_size) {
7766 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
7768 if (NULL != current_page_beginning) {
7769 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
7771 if (NULL != last_page) {
7772 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
7774 xmlXPathFreeObject(result); result = NULL;
7776 /* Extract boxes if they present */
7777 result = xmlXPathEvalExpression(BAD_CAST
7778 "isds:dbResults/isds:dbResult", xpath_ctx);
7779 if (NULL == result) {
7780 err = IE_ERROR;
7781 goto leave;
7783 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7784 struct isds_list *item, *prev_item = NULL;
7785 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7786 item = calloc(1, sizeof(*item));
7787 if (!item) {
7788 err = IE_NOMEM;
7789 goto leave;
7792 item->destructor = (void (*)(void **))isds_fulltext_result_free;
7793 if (i == 0) *boxes = item;
7794 else prev_item->next = item;
7795 prev_item = item;
7797 xpath_ctx->node = result->nodesetval->nodeTab[i];
7798 err = extract_dbResult(context,
7799 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
7800 (NULL == track_matches) ? 0 : *track_matches);
7801 if (err) goto leave;
7805 leave:
7806 if (err) {
7807 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7808 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7809 if (NULL != current_page_size) zfree(*current_page_size);
7810 if (NULL != last_page) zfree(*last_page);
7811 isds_list_free(boxes);
7814 free(string);
7815 xmlFreeNode(request);
7816 xmlXPathFreeObject(result);
7817 xmlXPathFreeContext(xpath_ctx);
7818 xmlFreeDoc(response);
7820 if (!err)
7821 isds_log(ILF_ISDS, ILL_DEBUG,
7822 _("ISDSSearch2 request processed by server successfully.\n"));
7823 #else /* not HAVE_LIBCURL */
7824 err = IE_NOTSUP;
7825 #endif
7827 return err;
7831 /* Get status of a box.
7832 * @context is ISDS session context.
7833 * @box_id is UTF-8 encoded box identifier as zero terminated string
7834 * @box_status is return value of box status.
7835 * @return:
7836 * IE_SUCCESS if box has been found and its status retrieved
7837 * IE_NOEXIST if box is not known to ISDS server
7838 * or other appropriate error.
7839 * You can use isds_DbState to enumerate box status. However out of enum
7840 * range value can be returned too. This is feature because ISDS
7841 * specification leaves the set of values open.
7842 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7843 * the box has been deleted, but ISDS still lists its former existence. */
7844 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7845 long int *box_status) {
7846 isds_error err = IE_SUCCESS;
7847 #if HAVE_LIBCURL
7848 xmlNsPtr isds_ns = NULL;
7849 xmlNodePtr request = NULL, db_id;
7850 xmlDocPtr response = NULL;
7851 xmlXPathContextPtr xpath_ctx = NULL;
7852 xmlXPathObjectPtr result = NULL;
7853 xmlChar *string = NULL;
7855 const xmlChar *codes[] = {
7856 BAD_CAST "5001",
7857 BAD_CAST "1007",
7858 BAD_CAST "2011",
7859 NULL
7861 const char *meanings[] = {
7862 "The box does not exist",
7863 "Box ID is malformed",
7864 "Box ID malformed",
7866 const isds_error errors[] = {
7867 IE_NOEXIST,
7868 IE_INVAL,
7869 IE_INVAL,
7871 struct code_map_isds_error map = {
7872 .codes = codes,
7873 .meanings = meanings,
7874 .errors = errors
7876 #endif
7878 if (!context) return IE_INVALID_CONTEXT;
7879 zfree(context->long_message);
7880 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7882 #if HAVE_LIBCURL
7883 /* Check if connection is established
7884 * TODO: This check should be done downstairs. */
7885 if (!context->curl) return IE_CONNECTION_CLOSED;
7888 /* Build CheckDataBox request */
7889 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7890 if (!request) {
7891 isds_log_message(context,
7892 _("Could build CheckDataBox request"));
7893 return IE_ERROR;
7895 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7896 if(!isds_ns) {
7897 isds_log_message(context, _("Could not create ISDS name space"));
7898 xmlFreeNode(request);
7899 return IE_ERROR;
7901 xmlSetNs(request, isds_ns);
7902 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7903 if (!db_id) {
7904 isds_log_message(context, _("Could not add dbID child to "
7905 "CheckDataBox element"));
7906 xmlFreeNode(request);
7907 return IE_ERROR;
7911 /* Send request and check response*/
7912 err = send_destroy_request_check_response(context,
7913 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7914 &request, &response, NULL, &map);
7915 if (err) goto leave;
7918 /* Extract data */
7919 xpath_ctx = xmlXPathNewContext(response);
7920 if (!xpath_ctx) {
7921 err = IE_ERROR;
7922 goto leave;
7924 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7925 err = IE_ERROR;
7926 goto leave;
7928 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7929 xpath_ctx);
7930 if (!result) {
7931 err = IE_ERROR;
7932 goto leave;
7934 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7935 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7936 err = IE_ISDS;
7937 goto leave;
7939 if (result->nodesetval->nodeNr > 1) {
7940 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7941 err = IE_ISDS;
7942 goto leave;
7944 xpath_ctx->node = result->nodesetval->nodeTab[0];
7945 xmlXPathFreeObject(result); result = NULL;
7947 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7950 leave:
7951 free(string);
7952 xmlXPathFreeObject(result);
7953 xmlXPathFreeContext(xpath_ctx);
7955 xmlFreeDoc(response);
7957 if (!err)
7958 isds_log(ILF_ISDS, ILL_DEBUG,
7959 _("CheckDataBox request processed by server successfully.\n"));
7960 #else /* not HAVE_LIBCURL */
7961 err = IE_NOTSUP;
7962 #endif
7964 return err;
7968 /* Get list of permissions to send commercial messages.
7969 * @context is ISDS session context.
7970 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7971 * @permissions is a reallocated list of permissions (struct
7972 * isds_commercial_permission*) to send commercial messages from @box_id. The
7973 * order of permissions is significant as the server applies the permissions
7974 * and associated pre-paid credits in the order. Empty list means no
7975 * permission.
7976 * @return:
7977 * IE_SUCCESS if the list has been obtained correctly,
7978 * or other appropriate error. */
7979 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7980 const char *box_id, struct isds_list **permissions) {
7981 isds_error err = IE_SUCCESS;
7982 #if HAVE_LIBCURL
7983 xmlDocPtr response = NULL;
7984 xmlXPathContextPtr xpath_ctx = NULL;
7985 xmlXPathObjectPtr result = NULL;
7986 #endif
7988 if (!context) return IE_INVALID_CONTEXT;
7989 zfree(context->long_message);
7990 if (NULL == permissions) return IE_INVAL;
7991 isds_list_free(permissions);
7992 if (NULL == box_id) return IE_INVAL;
7994 #if HAVE_LIBCURL
7995 /* Check if connection is established */
7996 if (!context->curl) return IE_CONNECTION_CLOSED;
7998 /* Do request and check for success */
7999 err = build_send_dbid_request_check_response(context,
8000 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
8001 BAD_CAST box_id, NULL, &response, NULL);
8002 if (!err) {
8003 isds_log(ILF_ISDS, ILL_DEBUG,
8004 _("PDZInfo request processed by server successfully.\n"));
8007 /* Extract data */
8008 /* Prepare structure */
8009 xpath_ctx = xmlXPathNewContext(response);
8010 if (!xpath_ctx) {
8011 err = IE_ERROR;
8012 goto leave;
8014 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8015 err = IE_ERROR;
8016 goto leave;
8019 /* Set context node */
8020 result = xmlXPathEvalExpression(BAD_CAST
8021 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8022 xpath_ctx);
8023 if (!result) {
8024 err = IE_ERROR;
8025 goto leave;
8027 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8028 struct isds_list *prev_item = NULL;
8030 /* Iterate over all permission records */
8031 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8032 struct isds_list *item;
8034 /* Prepare structure */
8035 item = calloc(1, sizeof(*item));
8036 if (!item) {
8037 err = IE_NOMEM;
8038 goto leave;
8040 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8041 if (i == 0) *permissions = item;
8042 else prev_item->next = item;
8043 prev_item = item;
8045 /* Extract it */
8046 xpath_ctx->node = result->nodesetval->nodeTab[i];
8047 err = extract_DbPDZRecord(context,
8048 (struct isds_commercial_permission **) (&item->data),
8049 xpath_ctx);
8050 if (err) goto leave;
8054 leave:
8055 if (err) {
8056 isds_list_free(permissions);
8059 xmlXPathFreeObject(result);
8060 xmlXPathFreeContext(xpath_ctx);
8061 xmlFreeDoc(response);
8063 #else /* not HAVE_LIBCURL */
8064 err = IE_NOTSUP;
8065 #endif
8067 return err;
8071 /* Get details about credit for sending pre-paid commercial messages.
8072 * @context is ISDS session context.
8073 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8074 * @from_date is first day of credit history to return in @history. Only
8075 * tm_year, tm_mon and tm_mday carry sane value.
8076 * @to_date is last day of credit history to return in @history. Only
8077 * tm_year, tm_mon and tm_mday carry sane value.
8078 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8079 * if you don't care. This and all other credit values are integers in
8080 * hundredths of Czech Crowns.
8081 * @email outputs notification e-mail address where notifications about credit
8082 * are sent. This is automatically reallocated string. Pass NULL if you don't
8083 * care. It can return NULL if no address is defined.
8084 * @history outputs auto-reallocated list of pointers to struct
8085 * isds_credit_event. Events in closed interval @from_time to @to_time are
8086 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8087 * are sorted by time.
8088 * @return:
8089 * IE_SUCCESS if the credit details have been obtained correctly,
8090 * or other appropriate error. Please note that server allows to retrieve
8091 * only limited history of events. */
8092 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8093 const char *box_id,
8094 const struct tm *from_date, const struct tm *to_date,
8095 long int *credit, char **email, struct isds_list **history) {
8096 isds_error err = IE_SUCCESS;
8097 #if HAVE_LIBCURL
8098 char *box_id_locale = NULL;
8099 xmlNodePtr request = NULL, node;
8100 xmlNsPtr isds_ns = NULL;
8101 xmlChar *string = NULL;
8103 xmlDocPtr response = NULL;
8104 xmlXPathContextPtr xpath_ctx = NULL;
8105 xmlXPathObjectPtr result = NULL;
8107 const xmlChar *codes[] = {
8108 BAD_CAST "1004",
8109 BAD_CAST "2011",
8110 BAD_CAST "1093",
8111 BAD_CAST "1137",
8112 BAD_CAST "1058",
8113 NULL
8115 const char *meanings[] = {
8116 "Insufficient priviledges for the box",
8117 "The box does not exist",
8118 "Date is too long (history is not available after 15 months)",
8119 "Interval is too long (limit is 3 months)",
8120 "Invalid date"
8122 const isds_error errors[] = {
8123 IE_ISDS,
8124 IE_NOEXIST,
8125 IE_DATE,
8126 IE_DATE,
8127 IE_DATE,
8129 struct code_map_isds_error map = {
8130 .codes = codes,
8131 .meanings = meanings,
8132 .errors = errors
8134 #endif
8136 if (!context) return IE_INVALID_CONTEXT;
8137 zfree(context->long_message);
8139 /* Free output argument */
8140 if (NULL != credit) *credit = 0;
8141 if (NULL != email) zfree(*email);
8142 isds_list_free(history);
8144 if (NULL == box_id) return IE_INVAL;
8146 #if HAVE_LIBCURL
8147 /* Check if connection is established */
8148 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8150 box_id_locale = _isds_utf82locale((char*)box_id);
8151 if (NULL == box_id_locale) {
8152 err = IE_NOMEM;
8153 goto leave;
8156 /* Build request */
8157 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8158 if (NULL == request) {
8159 isds_printf_message(context,
8160 _("Could not build DataBoxCreditInfo request for %s box"),
8161 box_id_locale);
8162 err = IE_ERROR;
8163 goto leave;
8165 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8166 if(!isds_ns) {
8167 isds_log_message(context, _("Could not create ISDS name space"));
8168 err = IE_ERROR;
8169 goto leave;
8171 xmlSetNs(request, isds_ns);
8173 /* Add mandatory XSD:tIdDbInput child */
8174 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8175 /* Add mandatory dates elements with optional values */
8176 if (from_date) {
8177 err = tm2datestring(from_date, &string);
8178 if (err) {
8179 isds_log_message(context,
8180 _("Could not convert `from_date' argument to ISO date "
8181 "string"));
8182 goto leave;
8184 INSERT_STRING(request, "ciFromDate", string);
8185 zfree(string);
8186 } else {
8187 INSERT_STRING(request, "ciFromDate", NULL);
8189 if (to_date) {
8190 err = tm2datestring(to_date, &string);
8191 if (err) {
8192 isds_log_message(context,
8193 _("Could not convert `to_date' argument to ISO date "
8194 "string"));
8195 goto leave;
8197 INSERT_STRING(request, "ciTodate", string);
8198 zfree(string);
8199 } else {
8200 INSERT_STRING(request, "ciTodate", NULL);
8203 /* Send request and check response*/
8204 err = send_destroy_request_check_response(context,
8205 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8206 &request, &response, NULL, &map);
8207 if (err) goto leave;
8210 /* Extract data */
8211 /* Set context to the root */
8212 xpath_ctx = xmlXPathNewContext(response);
8213 if (!xpath_ctx) {
8214 err = IE_ERROR;
8215 goto leave;
8217 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8218 err = IE_ERROR;
8219 goto leave;
8221 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8222 xpath_ctx);
8223 if (!result) {
8224 err = IE_ERROR;
8225 goto leave;
8227 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8228 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8229 err = IE_ISDS;
8230 goto leave;
8232 if (result->nodesetval->nodeNr > 1) {
8233 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8234 err = IE_ISDS;
8235 goto leave;
8237 xpath_ctx->node = result->nodesetval->nodeTab[0];
8238 xmlXPathFreeObject(result); result = NULL;
8240 /* Extract common data */
8241 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8242 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8244 /* Extract records */
8245 if (NULL == history) goto leave;
8246 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8247 xpath_ctx);
8248 if (!result) {
8249 err = IE_ERROR;
8250 goto leave;
8252 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8253 struct isds_list *prev_item = NULL;
8255 /* Iterate over all records */
8256 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8257 struct isds_list *item;
8259 /* Prepare structure */
8260 item = calloc(1, sizeof(*item));
8261 if (!item) {
8262 err = IE_NOMEM;
8263 goto leave;
8265 item->destructor = (void(*)(void**))isds_credit_event_free;
8266 if (i == 0) *history = item;
8267 else prev_item->next = item;
8268 prev_item = item;
8270 /* Extract it */
8271 xpath_ctx->node = result->nodesetval->nodeTab[i];
8272 err = extract_CiRecord(context,
8273 (struct isds_credit_event **) (&item->data),
8274 xpath_ctx);
8275 if (err) goto leave;
8279 leave:
8280 if (!err) {
8281 isds_log(ILF_ISDS, ILL_DEBUG,
8282 _("DataBoxCreditInfo request processed by server successfully.\n"));
8284 if (err) {
8285 isds_list_free(history);
8286 if (NULL != email) zfree(*email)
8289 free(box_id_locale);
8290 xmlXPathFreeObject(result);
8291 xmlXPathFreeContext(xpath_ctx);
8292 xmlFreeDoc(response);
8294 #else /* not HAVE_LIBCURL */
8295 err = IE_NOTSUP;
8296 #endif
8298 return err;
8302 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8303 * code, destroy response and log success.
8304 * @context is ISDS session context.
8305 * @service_name is name of SERVICE_DB_MANIPULATION service
8306 * @box_id is UTF-8 encoded box identifier as zero terminated string
8307 * @approval is optional external approval of box manipulation
8308 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8309 * NULL, if you don't care. */
8310 static isds_error build_send_manipulationdbid_request_check_drop_response(
8311 struct isds_ctx *context, const xmlChar *service_name,
8312 const xmlChar *box_id, const struct isds_approval *approval,
8313 xmlChar **refnumber) {
8314 isds_error err = IE_SUCCESS;
8315 #if HAVE_LIBCURL
8316 xmlDocPtr response = NULL;
8317 #endif
8319 if (!context) return IE_INVALID_CONTEXT;
8320 zfree(context->long_message);
8321 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8323 #if HAVE_LIBCURL
8324 /* Check if connection is established */
8325 if (!context->curl) return IE_CONNECTION_CLOSED;
8327 /* Do request and check for success */
8328 err = build_send_dbid_request_check_response(context,
8329 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8330 &response, refnumber);
8331 xmlFreeDoc(response);
8333 if (!err) {
8334 char *service_name_locale = _isds_utf82locale((char *) service_name);
8335 isds_log(ILF_ISDS, ILL_DEBUG,
8336 _("%s request processed by server successfully.\n"),
8337 service_name_locale);
8338 free(service_name_locale);
8340 #else /* not HAVE_LIBCURL */
8341 err = IE_NOTSUP;
8342 #endif
8344 return err;
8348 /* Switch box into state where box can receive commercial messages (off by
8349 * default)
8350 * @context is ISDS session context.
8351 * @box_id is UTF-8 encoded box identifier as zero terminated string
8352 * @allow is true for enable, false for disable commercial messages income
8353 * @approval is optional external approval of box manipulation
8354 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8355 * NULL, if you don't care. */
8356 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8357 const char *box_id, const _Bool allow,
8358 const struct isds_approval *approval, char **refnumber) {
8359 return build_send_manipulationdbid_request_check_drop_response(context,
8360 (allow) ? BAD_CAST "SetOpenAddressing" :
8361 BAD_CAST "ClearOpenAddressing",
8362 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8366 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8367 * message acceptance). This is just a box permission. Sender must apply
8368 * such role by sending each message.
8369 * @context is ISDS session context.
8370 * @box_id is UTF-8 encoded box identifier as zero terminated string
8371 * @allow is true for enable, false for disable OVM role permission
8372 * @approval is optional external approval of box manipulation
8373 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8374 * NULL, if you don't care. */
8375 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
8376 const char *box_id, const _Bool allow,
8377 const struct isds_approval *approval, char **refnumber) {
8378 return build_send_manipulationdbid_request_check_drop_response(context,
8379 (allow) ? BAD_CAST "SetEffectiveOVM" :
8380 BAD_CAST "ClearEffectiveOVM",
8381 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8385 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8386 * code, destroy response and log success.
8387 * @context is ISDS session context.
8388 * @service_name is name of SERVICE_DB_MANIPULATION service
8389 * @owner is structure describing box
8390 * @approval is optional external approval of box manipulation
8391 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8392 * NULL, if you don't care. */
8393 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8394 struct isds_ctx *context, const xmlChar *service_name,
8395 const struct isds_DbOwnerInfo *owner,
8396 const struct isds_approval *approval, xmlChar **refnumber) {
8397 isds_error err = IE_SUCCESS;
8398 #if HAVE_LIBCURL
8399 char *service_name_locale = NULL;
8400 xmlNodePtr request = NULL, db_owner_info;
8401 xmlNsPtr isds_ns = NULL;
8402 #endif
8405 if (!context) return IE_INVALID_CONTEXT;
8406 zfree(context->long_message);
8407 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8409 #if HAVE_LIBCURL
8410 service_name_locale = _isds_utf82locale((char*)service_name);
8411 if (!service_name_locale) {
8412 err = IE_NOMEM;
8413 goto leave;
8416 /* Build request */
8417 request = xmlNewNode(NULL, service_name);
8418 if (!request) {
8419 isds_printf_message(context,
8420 _("Could not build %s request"), service_name_locale);
8421 err = IE_ERROR;
8422 goto leave;
8424 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8425 if(!isds_ns) {
8426 isds_log_message(context, _("Could not create ISDS name space"));
8427 err = IE_ERROR;
8428 goto leave;
8430 xmlSetNs(request, isds_ns);
8433 /* Add XSD:tOwnerInfoInput child*/
8434 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8435 err = insert_DbOwnerInfo(context, owner, db_owner_info);
8436 if (err) goto leave;
8438 /* Add XSD:gExtApproval*/
8439 err = insert_GExtApproval(context, approval, request);
8440 if (err) goto leave;
8442 /* Send it to server and process response */
8443 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8444 service_name, &request, refnumber);
8446 leave:
8447 xmlFreeNode(request);
8448 free(service_name_locale);
8449 #else /* not HAVE_LIBCURL */
8450 err = IE_NOTSUP;
8451 #endif
8453 return err;
8457 /* Switch box accessibility state on request of box owner.
8458 * Despite the name, owner must do the request off-line. This function is
8459 * designed for such off-line meeting points (e.g. Czech POINT).
8460 * @context is ISDS session context.
8461 * @box identifies box to switch accessibility state.
8462 * @allow is true for making accessible, false to disallow access.
8463 * @approval is optional external approval of box manipulation
8464 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8465 * NULL, if you don't care. */
8466 isds_error isds_switch_box_accessibility_on_owner_request(
8467 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8468 const _Bool allow, const struct isds_approval *approval,
8469 char **refnumber) {
8470 return build_send_manipulationdbowner_request_check_drop_response(context,
8471 (allow) ? BAD_CAST "EnableOwnDataBox" :
8472 BAD_CAST "DisableOwnDataBox",
8473 box, approval, (xmlChar **) refnumber);
8477 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8478 * date.
8479 * @context is ISDS session context.
8480 * @box identifies box to switch accessibility state.
8481 * @since is date since accessibility has been denied. This can be past too.
8482 * Only tm_year, tm_mon and tm_mday carry sane value.
8483 * @approval is optional external approval of box manipulation
8484 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8485 * NULL, if you don't care. */
8486 isds_error isds_disable_box_accessibility_externaly(
8487 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8488 const struct tm *since, const struct isds_approval *approval,
8489 char **refnumber) {
8490 isds_error err = IE_SUCCESS;
8491 #if HAVE_LIBCURL
8492 char *service_name_locale = NULL;
8493 xmlNodePtr request = NULL, node;
8494 xmlNsPtr isds_ns = NULL;
8495 xmlChar *string = NULL;
8496 #endif
8499 if (!context) return IE_INVALID_CONTEXT;
8500 zfree(context->long_message);
8501 if (!box || !since) return IE_INVAL;
8503 #if HAVE_LIBCURL
8504 /* Build request */
8505 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
8506 if (!request) {
8507 isds_printf_message(context,
8508 _("Could not build %s request"), "DisableDataBoxExternally");
8509 err = IE_ERROR;
8510 goto leave;
8512 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8513 if(!isds_ns) {
8514 isds_log_message(context, _("Could not create ISDS name space"));
8515 err = IE_ERROR;
8516 goto leave;
8518 xmlSetNs(request, isds_ns);
8521 /* Add @box identification */
8522 INSERT_ELEMENT(node, request, "dbOwnerInfo");
8523 err = insert_DbOwnerInfo(context, box, node);
8524 if (err) goto leave;
8526 /* Add @since date */
8527 err = tm2datestring(since, &string);
8528 if(err) {
8529 isds_log_message(context,
8530 _("Could not convert `since' argument to ISO date string"));
8531 goto leave;
8533 INSERT_STRING(request, "dbOwnerDisableDate", string);
8534 zfree(string);
8536 /* Add @approval */
8537 err = insert_GExtApproval(context, approval, request);
8538 if (err) goto leave;
8540 /* Send it to server and process response */
8541 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8542 BAD_CAST "DisableDataBoxExternally", &request,
8543 (xmlChar **) refnumber);
8545 leave:
8546 free(string);
8547 xmlFreeNode(request);
8548 free(service_name_locale);
8549 #else /* not HAVE_LIBCURL */
8550 err = IE_NOTSUP;
8551 #endif
8553 return err;
8557 #if HAVE_LIBCURL
8558 /* Insert struct isds_message data (envelope (recipient data optional) and
8559 * documents into XML tree
8560 * @context is session context
8561 * @outgoing_message is libisds structure with message data
8562 * @create_message is XML CreateMessage or CreateMultipleMessage element
8563 * @process_recipient true for recipient data serialization, false for no
8564 * serialization */
8565 static isds_error insert_envelope_files(struct isds_ctx *context,
8566 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8567 const _Bool process_recipient) {
8569 isds_error err = IE_SUCCESS;
8570 xmlNodePtr envelope, dm_files, node;
8571 xmlChar *string = NULL;
8573 if (!context) return IE_INVALID_CONTEXT;
8574 if (!outgoing_message || !create_message) return IE_INVAL;
8577 /* Build envelope */
8578 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8579 if (!envelope) {
8580 isds_printf_message(context, _("Could not add dmEnvelope child to "
8581 "%s element"), create_message->name);
8582 return IE_ERROR;
8585 if (!outgoing_message->envelope) {
8586 isds_log_message(context, _("Outgoing message is missing envelope"));
8587 err = IE_INVAL;
8588 goto leave;
8591 /* Insert optional message type */
8592 err = insert_message_type(context, outgoing_message->envelope->dmType,
8593 envelope);
8594 if (err) goto leave;
8596 INSERT_STRING(envelope, "dmSenderOrgUnit",
8597 outgoing_message->envelope->dmSenderOrgUnit);
8598 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8599 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8601 if (process_recipient) {
8602 if (!outgoing_message->envelope->dbIDRecipient) {
8603 isds_log_message(context,
8604 _("Outgoing message is missing recipient box identifier"));
8605 err = IE_INVAL;
8606 goto leave;
8608 INSERT_STRING(envelope, "dbIDRecipient",
8609 outgoing_message->envelope->dbIDRecipient);
8611 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8612 outgoing_message->envelope->dmRecipientOrgUnit);
8613 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8614 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8615 INSERT_STRING(envelope, "dmToHands",
8616 outgoing_message->envelope->dmToHands);
8619 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8620 "dmAnnotation");
8621 INSERT_STRING(envelope, "dmAnnotation",
8622 outgoing_message->envelope->dmAnnotation);
8624 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8625 0, 50, "dmRecipientRefNumber");
8626 INSERT_STRING(envelope, "dmRecipientRefNumber",
8627 outgoing_message->envelope->dmRecipientRefNumber);
8629 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8630 0, 50, "dmSenderRefNumber");
8631 INSERT_STRING(envelope, "dmSenderRefNumber",
8632 outgoing_message->envelope->dmSenderRefNumber);
8634 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8635 0, 50, "dmRecipientIdent");
8636 INSERT_STRING(envelope, "dmRecipientIdent",
8637 outgoing_message->envelope->dmRecipientIdent);
8639 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8640 0, 50, "dmSenderIdent");
8641 INSERT_STRING(envelope, "dmSenderIdent",
8642 outgoing_message->envelope->dmSenderIdent);
8644 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8645 outgoing_message->envelope->dmLegalTitleLaw, string);
8646 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8647 outgoing_message->envelope->dmLegalTitleYear, string);
8648 INSERT_STRING(envelope, "dmLegalTitleSect",
8649 outgoing_message->envelope->dmLegalTitleSect);
8650 INSERT_STRING(envelope, "dmLegalTitlePar",
8651 outgoing_message->envelope->dmLegalTitlePar);
8652 INSERT_STRING(envelope, "dmLegalTitlePoint",
8653 outgoing_message->envelope->dmLegalTitlePoint);
8655 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8656 outgoing_message->envelope->dmPersonalDelivery);
8657 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8658 outgoing_message->envelope->dmAllowSubstDelivery);
8660 /* ???: Should we require value for dbEffectiveOVM sender?
8661 * ISDS has default as true */
8662 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8663 INSERT_BOOLEAN(envelope, "dmOVM",
8664 outgoing_message->envelope->dmPublishOwnID);
8667 /* Append dmFiles */
8668 if (!outgoing_message->documents) {
8669 isds_log_message(context,
8670 _("Outgoing message is missing list of documents"));
8671 err = IE_INVAL;
8672 goto leave;
8674 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8675 if (!dm_files) {
8676 isds_printf_message(context, _("Could not add dmFiles child to "
8677 "%s element"), create_message->name);
8678 err = IE_ERROR;
8679 goto leave;
8682 /* Check for document hierarchy */
8683 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8684 if (err) goto leave;
8686 /* Process each document */
8687 for (struct isds_list *item =
8688 (struct isds_list *) outgoing_message->documents;
8689 item; item = item->next) {
8690 if (!item->data) {
8691 isds_log_message(context,
8692 _("List of documents contains empty item"));
8693 err = IE_INVAL;
8694 goto leave;
8696 /* FIXME: Check for dmFileMetaType and for document references.
8697 * Only first document can be of MAIN type */
8698 err = insert_document(context, (struct isds_document*) item->data,
8699 dm_files);
8701 if (err) goto leave;
8704 leave:
8705 free(string);
8706 return err;
8708 #endif /* HAVE_LIBCURL */
8711 /* Send a message via ISDS to a recipient
8712 * @context is session context
8713 * @outgoing_message is message to send; Some members are mandatory (like
8714 * dbIDRecipient), some are optional and some are irrelevant (especially data
8715 * about sender). Included pointer to isds_list documents must contain at
8716 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8717 * members will be filled with valid data from ISDS. Exact list of write
8718 * members is subject to change. Currently dmID is changed.
8719 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8720 isds_error isds_send_message(struct isds_ctx *context,
8721 struct isds_message *outgoing_message) {
8723 isds_error err = IE_SUCCESS;
8724 #if HAVE_LIBCURL
8725 xmlNsPtr isds_ns = NULL;
8726 xmlNodePtr request = NULL;
8727 xmlDocPtr response = NULL;
8728 xmlChar *code = NULL, *message = NULL;
8729 xmlXPathContextPtr xpath_ctx = NULL;
8730 xmlXPathObjectPtr result = NULL;
8731 /*_Bool message_is_complete = 0;*/
8732 #endif
8734 if (!context) return IE_INVALID_CONTEXT;
8735 zfree(context->long_message);
8736 if (!outgoing_message) return IE_INVAL;
8738 #if HAVE_LIBCURL
8739 /* Check if connection is established
8740 * TODO: This check should be done downstairs. */
8741 if (!context->curl) return IE_CONNECTION_CLOSED;
8744 /* Build CreateMessage request */
8745 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8746 if (!request) {
8747 isds_log_message(context,
8748 _("Could not build CreateMessage request"));
8749 return IE_ERROR;
8751 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8752 if(!isds_ns) {
8753 isds_log_message(context, _("Could not create ISDS name space"));
8754 xmlFreeNode(request);
8755 return IE_ERROR;
8757 xmlSetNs(request, isds_ns);
8759 /* Append envelope and files */
8760 err = insert_envelope_files(context, outgoing_message, request, 1);
8761 if (err) goto leave;
8764 /* Signal we can serialize message since now */
8765 /*message_is_complete = 1;*/
8768 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8770 /* Sent request */
8771 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8773 /* Don't' destroy request, we want to provide it to application later */
8775 if (err) {
8776 isds_log(ILF_ISDS, ILL_DEBUG,
8777 _("Processing ISDS response on CreateMessage "
8778 "request failed\n"));
8779 goto leave;
8782 /* Check for response status */
8783 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8784 &code, &message, NULL);
8785 if (err) {
8786 isds_log(ILF_ISDS, ILL_DEBUG,
8787 _("ISDS response on CreateMessage request "
8788 "is missing status\n"));
8789 goto leave;
8792 /* Request processed, but refused by server or server failed */
8793 if (xmlStrcmp(code, BAD_CAST "0000")) {
8794 char *box_id_locale =
8795 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8796 char *code_locale = _isds_utf82locale((char*)code);
8797 char *message_locale = _isds_utf82locale((char*)message);
8798 isds_log(ILF_ISDS, ILL_DEBUG,
8799 _("Server did not accept message for %s on CreateMessage "
8800 "request (code=%s, message=%s)\n"),
8801 box_id_locale, code_locale, message_locale);
8802 isds_log_message(context, message_locale);
8803 free(box_id_locale);
8804 free(code_locale);
8805 free(message_locale);
8806 err = IE_ISDS;
8807 goto leave;
8811 /* Extract data */
8812 xpath_ctx = xmlXPathNewContext(response);
8813 if (!xpath_ctx) {
8814 err = IE_ERROR;
8815 goto leave;
8817 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8818 err = IE_ERROR;
8819 goto leave;
8821 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8822 xpath_ctx);
8823 if (!result) {
8824 err = IE_ERROR;
8825 goto leave;
8827 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8828 isds_log_message(context, _("Missing CreateMessageResponse element"));
8829 err = IE_ISDS;
8830 goto leave;
8832 if (result->nodesetval->nodeNr > 1) {
8833 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8834 err = IE_ISDS;
8835 goto leave;
8837 xpath_ctx->node = result->nodesetval->nodeTab[0];
8838 xmlXPathFreeObject(result); result = NULL;
8840 if (outgoing_message->envelope->dmID) {
8841 free(outgoing_message->envelope->dmID);
8842 outgoing_message->envelope->dmID = NULL;
8844 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8845 if (!outgoing_message->envelope->dmID) {
8846 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8847 "but did not return assigned message ID\n"));
8850 leave:
8851 /* TODO: Serialize message into structure member raw */
8852 /* XXX: Each web service transport message in different format.
8853 * Therefore it's not possible to save them directly.
8854 * To save them, one must figure out common format.
8855 * We can leave it on application, or we can implement the ESS format. */
8856 /*if (message_is_complete) {
8857 if (outgoing_message->envelope->dmID) {
8859 /* Add assigned message ID as first child*/
8860 /*xmlNodePtr dmid_text = xmlNewText(
8861 (xmlChar *) outgoing_message->envelope->dmID);
8862 if (!dmid_text) goto serialization_failed;
8864 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8865 BAD_CAST "dmID");
8866 if (!dmid_element) {
8867 xmlFreeNode(dmid_text);
8868 goto serialization_failed;
8871 xmlNodePtr dmid_element_with_text =
8872 xmlAddChild(dmid_element, dmid_text);
8873 if (!dmid_element_with_text) {
8874 xmlFreeNode(dmid_element);
8875 xmlFreeNode(dmid_text);
8876 goto serialization_failed;
8879 node = xmlAddPrevSibling(envelope->childern,
8880 dmid_element_with_text);
8881 if (!node) {
8882 xmlFreeNodeList(dmid_element_with_text);
8883 goto serialization_failed;
8887 /* Serialize message with ID into raw */
8888 /*buffer = serialize_element(envelope)*/
8889 /* }
8891 serialization_failed:
8895 /* Clean up */
8896 xmlXPathFreeObject(result);
8897 xmlXPathFreeContext(xpath_ctx);
8899 free(code);
8900 free(message);
8901 xmlFreeDoc(response);
8902 xmlFreeNode(request);
8904 if (!err)
8905 isds_log(ILF_ISDS, ILL_DEBUG,
8906 _("CreateMessage request processed by server "
8907 "successfully.\n"));
8908 #else /* not HAVE_LIBCURL */
8909 err = IE_NOTSUP;
8910 #endif
8912 return err;
8916 /* Send a message via ISDS to a multiple recipients
8917 * @context is session context
8918 * @outgoing_message is message to send; Some members are mandatory,
8919 * some are optional and some are irrelevant (especially data
8920 * about sender). Data about recipient will be substituted by ISDS from
8921 * @copies. Included pointer to isds_list documents must
8922 * contain at least one document of FILEMETATYPE_MAIN.
8923 * @copies is list of isds_message_copy structures addressing all desired
8924 * recipients. This is read-write structure, some members will be filled with
8925 * valid data from ISDS (message IDs, error codes, error descriptions).
8926 * @return
8927 * ISDS_SUCCESS if all messages have been sent
8928 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8929 * succeeded messages can be identified by copies->data->error),
8930 * or other error code if something other goes wrong. */
8931 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8932 const struct isds_message *outgoing_message,
8933 struct isds_list *copies) {
8935 isds_error err = IE_SUCCESS;
8936 #if HAVE_LIBCURL
8937 isds_error append_err;
8938 xmlNsPtr isds_ns = NULL;
8939 xmlNodePtr request = NULL, recipients, recipient, node;
8940 struct isds_list *item;
8941 struct isds_message_copy *copy;
8942 xmlDocPtr response = NULL;
8943 xmlChar *code = NULL, *message = NULL;
8944 xmlXPathContextPtr xpath_ctx = NULL;
8945 xmlXPathObjectPtr result = NULL;
8946 xmlChar *string = NULL;
8947 int i;
8948 #endif
8950 if (!context) return IE_INVALID_CONTEXT;
8951 zfree(context->long_message);
8952 if (!outgoing_message || !copies) return IE_INVAL;
8954 #if HAVE_LIBCURL
8955 /* Check if connection is established
8956 * TODO: This check should be done downstairs. */
8957 if (!context->curl) return IE_CONNECTION_CLOSED;
8960 /* Build CreateMultipleMessage request */
8961 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
8962 if (!request) {
8963 isds_log_message(context,
8964 _("Could not build CreateMultipleMessage request"));
8965 return IE_ERROR;
8967 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8968 if(!isds_ns) {
8969 isds_log_message(context, _("Could not create ISDS name space"));
8970 xmlFreeNode(request);
8971 return IE_ERROR;
8973 xmlSetNs(request, isds_ns);
8976 /* Build recipients */
8977 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8978 if (!recipients) {
8979 isds_log_message(context, _("Could not add dmRecipients child to "
8980 "CreateMultipleMessage element"));
8981 xmlFreeNode(request);
8982 return IE_ERROR;
8985 /* Insert each recipient */
8986 for (item = copies; item; item = item->next) {
8987 copy = (struct isds_message_copy *) item->data;
8988 if (!copy) {
8989 isds_log_message(context,
8990 _("`copies' list item contains empty data"));
8991 err = IE_INVAL;
8992 goto leave;
8995 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
8996 if (!recipient) {
8997 isds_log_message(context, _("Could not add dmRecipient child to "
8998 "dmRecipients element"));
8999 err = IE_ERROR;
9000 goto leave;
9003 if (!copy->dbIDRecipient) {
9004 isds_log_message(context,
9005 _("Message copy is missing recipient box identifier"));
9006 err = IE_INVAL;
9007 goto leave;
9009 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
9010 INSERT_STRING(recipient, "dmRecipientOrgUnit",
9011 copy->dmRecipientOrgUnit);
9012 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
9013 copy->dmRecipientOrgUnitNum, string);
9014 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
9017 /* Append envelope and files */
9018 err = insert_envelope_files(context, outgoing_message, request, 0);
9019 if (err) goto leave;
9022 isds_log(ILF_ISDS, ILL_DEBUG,
9023 _("Sending CreateMultipleMessage request to ISDS\n"));
9025 /* Sent request */
9026 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9027 if (err) {
9028 isds_log(ILF_ISDS, ILL_DEBUG,
9029 _("Processing ISDS response on CreateMultipleMessage "
9030 "request failed\n"));
9031 goto leave;
9034 /* Check for response status */
9035 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9036 &code, &message, NULL);
9037 if (err) {
9038 isds_log(ILF_ISDS, ILL_DEBUG,
9039 _("ISDS response on CreateMultipleMessage request "
9040 "is missing status\n"));
9041 goto leave;
9044 /* Request processed, but some copies failed */
9045 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9046 char *box_id_locale =
9047 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9048 char *code_locale = _isds_utf82locale((char*)code);
9049 char *message_locale = _isds_utf82locale((char*)message);
9050 isds_log(ILF_ISDS, ILL_DEBUG,
9051 _("Server did accept message for multiple recipients "
9052 "on CreateMultipleMessage request but delivery to "
9053 "some of them failed (code=%s, message=%s)\n"),
9054 box_id_locale, code_locale, message_locale);
9055 isds_log_message(context, message_locale);
9056 free(box_id_locale);
9057 free(code_locale);
9058 free(message_locale);
9059 err = IE_PARTIAL_SUCCESS;
9062 /* Request refused by server as whole */
9063 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9064 char *box_id_locale =
9065 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9066 char *code_locale = _isds_utf82locale((char*)code);
9067 char *message_locale = _isds_utf82locale((char*)message);
9068 isds_log(ILF_ISDS, ILL_DEBUG,
9069 _("Server did not accept message for multiple recipients "
9070 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9071 box_id_locale, code_locale, message_locale);
9072 isds_log_message(context, message_locale);
9073 free(box_id_locale);
9074 free(code_locale);
9075 free(message_locale);
9076 err = IE_ISDS;
9077 goto leave;
9081 /* Extract data */
9082 xpath_ctx = xmlXPathNewContext(response);
9083 if (!xpath_ctx) {
9084 err = IE_ERROR;
9085 goto leave;
9087 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9088 err = IE_ERROR;
9089 goto leave;
9091 result = xmlXPathEvalExpression(
9092 BAD_CAST "/isds:CreateMultipleMessageResponse"
9093 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9094 xpath_ctx);
9095 if (!result) {
9096 err = IE_ERROR;
9097 goto leave;
9099 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9100 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9101 err = IE_ISDS;
9102 goto leave;
9105 /* Extract message ID and delivery status for each copy */
9106 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9107 item = item->next, i++) {
9108 copy = (struct isds_message_copy *) item->data;
9109 xpath_ctx->node = result->nodesetval->nodeTab[i];
9111 append_err = append_TMStatus(context, copy, xpath_ctx);
9112 if (append_err) {
9113 err = append_err;
9114 goto leave;
9117 if (item || i < result->nodesetval->nodeNr) {
9118 isds_printf_message(context, _("ISDS returned unexpected number of "
9119 "message copy delivery states: %d"),
9120 result->nodesetval->nodeNr);
9121 err = IE_ISDS;
9122 goto leave;
9126 leave:
9127 /* Clean up */
9128 free(string);
9129 xmlXPathFreeObject(result);
9130 xmlXPathFreeContext(xpath_ctx);
9132 free(code);
9133 free(message);
9134 xmlFreeDoc(response);
9135 xmlFreeNode(request);
9137 if (!err)
9138 isds_log(ILF_ISDS, ILL_DEBUG,
9139 _("CreateMultipleMessageResponse request processed by server "
9140 "successfully.\n"));
9141 #else /* not HAVE_LIBCURL */
9142 err = IE_NOTSUP;
9143 #endif
9145 return err;
9149 /* Get list of messages. This is common core for getting sent or received
9150 * messages.
9151 * Any criterion argument can be NULL, if you don't care about it.
9152 * @context is session context. Must not be NULL.
9153 * @outgoing_direction is true if you want list of outgoing messages,
9154 * it's false if you want incoming messages.
9155 * @from_time is minimal time and date of message sending inclusive.
9156 * @to_time is maximal time and date of message sending inclusive
9157 * @organization_unit_number is number of sender/recipient respectively.
9158 * @status_filter is bit field of isds_message_status values. Use special
9159 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9160 * all values, you can use bit-wise arithmetic if you want.)
9161 * @offset is index of first message we are interested in. First message is 1.
9162 * Set to 0 (or 1) if you don't care.
9163 * @number is maximal length of list you want to get as input value, outputs
9164 * number of messages matching these criteria. Can be NULL if you don't care
9165 * (applies to output value either).
9166 * @messages is automatically reallocated list of isds_message's. Be ware that
9167 * it returns only brief overview (envelope and some other fields) about each
9168 * message, not the complete message. FIXME: Specify exact fields.
9169 * The list is sorted by delivery time in ascending order.
9170 * Use NULL if you don't care about don't need the data (useful if you want to
9171 * know only the @number). If you provide &NULL, list will be allocated on
9172 * heap, if you provide pointer to non-NULL, list will be freed automatically
9173 * at first. Also in case of error the list will be NULLed.
9174 * @return IE_SUCCESS or appropriate error code. */
9175 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9176 _Bool outgoing_direction,
9177 const struct timeval *from_time, const struct timeval *to_time,
9178 const long int *organization_unit_number,
9179 const unsigned int status_filter,
9180 const unsigned long int offset, unsigned long int *number,
9181 struct isds_list **messages) {
9183 isds_error err = IE_SUCCESS;
9184 #if HAVE_LIBCURL
9185 xmlNsPtr isds_ns = NULL;
9186 xmlNodePtr request = NULL, node;
9187 xmlDocPtr response = NULL;
9188 xmlChar *code = NULL, *message = NULL;
9189 xmlXPathContextPtr xpath_ctx = NULL;
9190 xmlXPathObjectPtr result = NULL;
9191 xmlChar *string = NULL;
9192 int count = 0;
9193 #endif
9195 if (!context) return IE_INVALID_CONTEXT;
9196 zfree(context->long_message);
9198 /* Free former message list if any */
9199 if (messages) isds_list_free(messages);
9201 #if HAVE_LIBCURL
9202 /* Check if connection is established
9203 * TODO: This check should be done downstairs. */
9204 if (!context->curl) return IE_CONNECTION_CLOSED;
9206 /* Build GetListOf*Messages request */
9207 request = xmlNewNode(NULL,
9208 (outgoing_direction) ?
9209 BAD_CAST "GetListOfSentMessages" :
9210 BAD_CAST "GetListOfReceivedMessages"
9212 if (!request) {
9213 isds_log_message(context,
9214 (outgoing_direction) ?
9215 _("Could not build GetListOfSentMessages request") :
9216 _("Could not build GetListOfReceivedMessages request")
9218 return IE_ERROR;
9220 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9221 if(!isds_ns) {
9222 isds_log_message(context, _("Could not create ISDS name space"));
9223 xmlFreeNode(request);
9224 return IE_ERROR;
9226 xmlSetNs(request, isds_ns);
9229 if (from_time) {
9230 err = timeval2timestring(from_time, &string);
9231 if (err) goto leave;
9233 INSERT_STRING(request, "dmFromTime", string);
9234 free(string); string = NULL;
9236 if (to_time) {
9237 err = timeval2timestring(to_time, &string);
9238 if (err) goto leave;
9240 INSERT_STRING(request, "dmToTime", string);
9241 free(string); string = NULL;
9243 if (outgoing_direction) {
9244 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9245 organization_unit_number, string);
9246 } else {
9247 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9248 organization_unit_number, string);
9251 if (status_filter > MESSAGESTATE_ANY) {
9252 isds_printf_message(context,
9253 _("Invalid message state filter value: %ld"), status_filter);
9254 err = IE_INVAL;
9255 goto leave;
9257 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9259 if (offset > 0 ) {
9260 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9261 } else {
9262 INSERT_STRING(request, "dmOffset", "1");
9265 /* number 0 means no limit */
9266 if (number && *number == 0) {
9267 INSERT_STRING(request, "dmLimit", NULL);
9268 } else {
9269 INSERT_ULONGINT(request, "dmLimit", number, string);
9273 isds_log(ILF_ISDS, ILL_DEBUG,
9274 (outgoing_direction) ?
9275 _("Sending GetListOfSentMessages request to ISDS\n") :
9276 _("Sending GetListOfReceivedMessages request to ISDS\n")
9279 /* Sent request */
9280 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9281 xmlFreeNode(request); request = NULL;
9283 if (err) {
9284 isds_log(ILF_ISDS, ILL_DEBUG,
9285 (outgoing_direction) ?
9286 _("Processing ISDS response on GetListOfSentMessages "
9287 "request failed\n") :
9288 _("Processing ISDS response on GetListOfReceivedMessages "
9289 "request failed\n")
9291 goto leave;
9294 /* Check for response status */
9295 err = isds_response_status(context, SERVICE_DM_INFO, response,
9296 &code, &message, NULL);
9297 if (err) {
9298 isds_log(ILF_ISDS, ILL_DEBUG,
9299 (outgoing_direction) ?
9300 _("ISDS response on GetListOfSentMessages request "
9301 "is missing status\n") :
9302 _("ISDS response on GetListOfReceivedMessages request "
9303 "is missing status\n")
9305 goto leave;
9308 /* Request processed, but nothing found */
9309 if (xmlStrcmp(code, BAD_CAST "0000")) {
9310 char *code_locale = _isds_utf82locale((char*)code);
9311 char *message_locale = _isds_utf82locale((char*)message);
9312 isds_log(ILF_ISDS, ILL_DEBUG,
9313 (outgoing_direction) ?
9314 _("Server refused GetListOfSentMessages request "
9315 "(code=%s, message=%s)\n") :
9316 _("Server refused GetListOfReceivedMessages request "
9317 "(code=%s, message=%s)\n"),
9318 code_locale, message_locale);
9319 isds_log_message(context, message_locale);
9320 free(code_locale);
9321 free(message_locale);
9322 err = IE_ISDS;
9323 goto leave;
9327 /* Extract data */
9328 xpath_ctx = xmlXPathNewContext(response);
9329 if (!xpath_ctx) {
9330 err = IE_ERROR;
9331 goto leave;
9333 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9334 err = IE_ERROR;
9335 goto leave;
9337 result = xmlXPathEvalExpression(
9338 (outgoing_direction) ?
9339 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9340 "isds:dmRecords/isds:dmRecord" :
9341 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9342 "isds:dmRecords/isds:dmRecord",
9343 xpath_ctx);
9344 if (!result) {
9345 err = IE_ERROR;
9346 goto leave;
9349 /* Fill output arguments in */
9350 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9351 struct isds_envelope *envelope;
9352 struct isds_list *item = NULL, *last_item = NULL;
9354 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9355 /* Create new message */
9356 item = calloc(1, sizeof(*item));
9357 if (!item) {
9358 err = IE_NOMEM;
9359 goto leave;
9361 item->destructor = (void(*)(void**)) &isds_message_free;
9362 item->data = calloc(1, sizeof(struct isds_message));
9363 if (!item->data) {
9364 isds_list_free(&item);
9365 err = IE_NOMEM;
9366 goto leave;
9369 /* Extract envelope data */
9370 xpath_ctx->node = result->nodesetval->nodeTab[count];
9371 envelope = NULL;
9372 err = extract_DmRecord(context, &envelope, xpath_ctx);
9373 if (err) {
9374 isds_list_free(&item);
9375 goto leave;
9378 /* Attach extracted envelope */
9379 ((struct isds_message *) item->data)->envelope = envelope;
9381 /* Append new message into the list */
9382 if (!*messages) {
9383 *messages = last_item = item;
9384 } else {
9385 last_item->next = item;
9386 last_item = item;
9390 if (number) *number = count;
9392 leave:
9393 if (err) {
9394 isds_list_free(messages);
9397 free(string);
9398 xmlXPathFreeObject(result);
9399 xmlXPathFreeContext(xpath_ctx);
9401 free(code);
9402 free(message);
9403 xmlFreeDoc(response);
9404 xmlFreeNode(request);
9406 if (!err)
9407 isds_log(ILF_ISDS, ILL_DEBUG,
9408 (outgoing_direction) ?
9409 _("GetListOfSentMessages request processed by server "
9410 "successfully.\n") :
9411 _("GetListOfReceivedMessages request processed by server "
9412 "successfully.\n")
9414 #else /* not HAVE_LIBCURL */
9415 err = IE_NOTSUP;
9416 #endif
9417 return err;
9421 /* Get list of outgoing (already sent) messages.
9422 * Any criterion argument can be NULL, if you don't care about it.
9423 * @context is session context. Must not be NULL.
9424 * @from_time is minimal time and date of message sending inclusive.
9425 * @to_time is maximal time and date of message sending inclusive
9426 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9427 * @status_filter is bit field of isds_message_status values. Use special
9428 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9429 * all values, you can use bit-wise arithmetic if you want.)
9430 * @offset is index of first message we are interested in. First message is 1.
9431 * Set to 0 (or 1) if you don't care.
9432 * @number is maximal length of list you want to get as input value, outputs
9433 * number of messages matching these criteria. Can be NULL if you don't care
9434 * (applies to output value either).
9435 * @messages is automatically reallocated list of isds_message's. Be ware that
9436 * it returns only brief overview (envelope and some other fields) about each
9437 * message, not the complete message. FIXME: Specify exact fields.
9438 * The list is sorted by delivery time in ascending order.
9439 * Use NULL if you don't care about the meta data (useful if you want to know
9440 * only the @number). If you provide &NULL, list will be allocated on heap,
9441 * if you provide pointer to non-NULL, list will be freed automatically at
9442 * first. Also in case of error the list will be NULLed.
9443 * @return IE_SUCCESS or appropriate error code. */
9444 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9445 const struct timeval *from_time, const struct timeval *to_time,
9446 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9447 const unsigned long int offset, unsigned long int *number,
9448 struct isds_list **messages) {
9450 return isds_get_list_of_messages(
9451 context, 1,
9452 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9453 offset, number,
9454 messages);
9458 /* Get list of incoming (addressed to you) messages.
9459 * Any criterion argument can be NULL, if you don't care about it.
9460 * @context is session context. Must not be NULL.
9461 * @from_time is minimal time and date of message sending inclusive.
9462 * @to_time is maximal time and date of message sending inclusive
9463 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9464 * @status_filter is bit field of isds_message_status values. Use special
9465 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9466 * all values, you can use bit-wise arithmetic if you want.)
9467 * @offset is index of first message we are interested in. First message is 1.
9468 * Set to 0 (or 1) if you don't care.
9469 * @number is maximal length of list you want to get as input value, outputs
9470 * number of messages matching these criteria. Can be NULL if you don't care
9471 * (applies to output value either).
9472 * @messages is automatically reallocated list of isds_message's. Be ware that
9473 * it returns only brief overview (envelope and some other fields) about each
9474 * message, not the complete message. FIXME: Specify exact fields.
9475 * Use NULL if you don't care about the meta data (useful if you want to know
9476 * only the @number). If you provide &NULL, list will be allocated on heap,
9477 * if you provide pointer to non-NULL, list will be freed automatically at
9478 * first. Also in case of error the list will be NULLed.
9479 * @return IE_SUCCESS or appropriate error code. */
9480 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9481 const struct timeval *from_time, const struct timeval *to_time,
9482 const long int *dmRecipientOrgUnitNum,
9483 const unsigned int status_filter,
9484 const unsigned long int offset, unsigned long int *number,
9485 struct isds_list **messages) {
9487 return isds_get_list_of_messages(
9488 context, 0,
9489 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9490 offset, number,
9491 messages);
9495 /* Get list of sent message state changes.
9496 * Any criterion argument can be NULL, if you don't care about it.
9497 * @context is session context. Must not be NULL.
9498 * @from_time is minimal time and date of status changes inclusive
9499 * @to_time is maximal time and date of status changes inclusive
9500 * @changed_states is automatically reallocated list of
9501 * isds_message_status_change's. If you provide &NULL, list will be allocated
9502 * on heap, if you provide pointer to non-NULL, list will be freed
9503 * automatically at first. Also in case of error the list will be NULLed.
9504 * XXX: The list item ordering is not specified.
9505 * XXX: Server provides only `recent' changes.
9506 * @return IE_SUCCESS or appropriate error code. */
9507 isds_error isds_get_list_of_sent_message_state_changes(
9508 struct isds_ctx *context,
9509 const struct timeval *from_time, const struct timeval *to_time,
9510 struct isds_list **changed_states) {
9512 isds_error err = IE_SUCCESS;
9513 #if HAVE_LIBCURL
9514 xmlNsPtr isds_ns = NULL;
9515 xmlNodePtr request = NULL, node;
9516 xmlDocPtr response = NULL;
9517 xmlXPathContextPtr xpath_ctx = NULL;
9518 xmlXPathObjectPtr result = NULL;
9519 xmlChar *string = NULL;
9520 int count = 0;
9521 #endif
9523 if (!context) return IE_INVALID_CONTEXT;
9524 zfree(context->long_message);
9526 /* Free former message list if any */
9527 isds_list_free(changed_states);
9529 #if HAVE_LIBCURL
9530 /* Check if connection is established
9531 * TODO: This check should be done downstairs. */
9532 if (!context->curl) return IE_CONNECTION_CLOSED;
9534 /* Build GetMessageStateChanges request */
9535 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
9536 if (!request) {
9537 isds_log_message(context,
9538 _("Could not build GetMessageStateChanges request"));
9539 return IE_ERROR;
9541 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9542 if(!isds_ns) {
9543 isds_log_message(context, _("Could not create ISDS name space"));
9544 xmlFreeNode(request);
9545 return IE_ERROR;
9547 xmlSetNs(request, isds_ns);
9550 if (from_time) {
9551 err = timeval2timestring(from_time, &string);
9552 if (err) goto leave;
9554 INSERT_STRING(request, "dmFromTime", string);
9555 zfree(string);
9557 if (to_time) {
9558 err = timeval2timestring(to_time, &string);
9559 if (err) goto leave;
9561 INSERT_STRING(request, "dmToTime", string);
9562 zfree(string);
9565 /* Sent request */
9566 err = send_destroy_request_check_response(context,
9567 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9568 &response, NULL, NULL);
9569 if (err) goto leave;
9572 /* Extract data */
9573 xpath_ctx = xmlXPathNewContext(response);
9574 if (!xpath_ctx) {
9575 err = IE_ERROR;
9576 goto leave;
9578 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9579 err = IE_ERROR;
9580 goto leave;
9582 result = xmlXPathEvalExpression(
9583 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9584 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9585 if (!result) {
9586 err = IE_ERROR;
9587 goto leave;
9590 /* Fill output arguments in */
9591 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9592 struct isds_list *item = NULL, *last_item = NULL;
9594 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9595 /* Create new status change */
9596 item = calloc(1, sizeof(*item));
9597 if (!item) {
9598 err = IE_NOMEM;
9599 goto leave;
9601 item->destructor =
9602 (void(*)(void**)) &isds_message_status_change_free;
9604 /* Extract message status change */
9605 xpath_ctx->node = result->nodesetval->nodeTab[count];
9606 err = extract_StateChangesRecord(context,
9607 (struct isds_message_status_change **) &item->data,
9608 xpath_ctx);
9609 if (err) {
9610 isds_list_free(&item);
9611 goto leave;
9614 /* Append new message status change into the list */
9615 if (!*changed_states) {
9616 *changed_states = last_item = item;
9617 } else {
9618 last_item->next = item;
9619 last_item = item;
9624 leave:
9625 if (err) {
9626 isds_list_free(changed_states);
9629 free(string);
9630 xmlXPathFreeObject(result);
9631 xmlXPathFreeContext(xpath_ctx);
9632 xmlFreeDoc(response);
9633 xmlFreeNode(request);
9635 if (!err)
9636 isds_log(ILF_ISDS, ILL_DEBUG,
9637 _("GetMessageStateChanges request processed by server "
9638 "successfully.\n"));
9639 #else /* not HAVE_LIBCURL */
9640 err = IE_NOTSUP;
9641 #endif
9642 return err;
9646 #if HAVE_LIBCURL
9647 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9648 * code
9649 * @context is session context
9650 * @service is ISDS WS service handler
9651 * @service_name is name of SERVICE_DM_OPERATIONS
9652 * @message_id is message ID to send as service argument to ISDS
9653 * @response is reallocated server SOAP body response as XML document
9654 * @raw_response is reallocated bit stream with response body. Use
9655 * NULL if you don't care
9656 * @raw_response_length is size of @raw_response in bytes
9657 * @code is reallocated ISDS status code
9658 * @status_message is reallocated ISDS status message
9659 * @return error coded from lower layer, context message will be set up
9660 * appropriately. */
9661 static isds_error build_send_check_message_request(struct isds_ctx *context,
9662 const isds_service service, const xmlChar *service_name,
9663 const char *message_id,
9664 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9665 xmlChar **code, xmlChar **status_message) {
9667 isds_error err = IE_SUCCESS;
9668 char *service_name_locale = NULL, *message_id_locale = NULL;
9669 xmlNodePtr request = NULL, node;
9670 xmlNsPtr isds_ns = NULL;
9672 if (!context) return IE_INVALID_CONTEXT;
9673 if (!service_name || !message_id) return IE_INVAL;
9674 if (!response || !code || !status_message) return IE_INVAL;
9675 if (!raw_response_length && raw_response) return IE_INVAL;
9677 /* Free output argument */
9678 xmlFreeDoc(*response); *response = NULL;
9679 if (raw_response) zfree(*raw_response);
9680 zfree(*code);
9681 zfree(*status_message);
9684 /* Check if connection is established
9685 * TODO: This check should be done downstairs. */
9686 if (!context->curl) return IE_CONNECTION_CLOSED;
9688 service_name_locale = _isds_utf82locale((char*)service_name);
9689 message_id_locale = _isds_utf82locale(message_id);
9690 if (!service_name_locale || !message_id_locale) {
9691 err = IE_NOMEM;
9692 goto leave;
9695 /* Build request */
9696 request = xmlNewNode(NULL, service_name);
9697 if (!request) {
9698 isds_printf_message(context,
9699 _("Could not build %s request for %s message ID"),
9700 service_name_locale, message_id_locale);
9701 err = IE_ERROR;
9702 goto leave;
9704 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9705 if(!isds_ns) {
9706 isds_log_message(context, _("Could not create ISDS name space"));
9707 err = IE_ERROR;
9708 goto leave;
9710 xmlSetNs(request, isds_ns);
9713 /* Add requested ID */
9714 err = validate_message_id_length(context, (xmlChar *) message_id);
9715 if (err) goto leave;
9716 INSERT_STRING(request, "dmID", message_id);
9719 isds_log(ILF_ISDS, ILL_DEBUG,
9720 _("Sending %s request for %s message ID to ISDS\n"),
9721 service_name_locale, message_id_locale);
9723 /* Send request */
9724 err = _isds(context, service, request, response,
9725 raw_response, raw_response_length);
9726 xmlFreeNode(request); request = NULL;
9728 if (err) {
9729 isds_log(ILF_ISDS, ILL_DEBUG,
9730 _("Processing ISDS response on %s request failed\n"),
9731 service_name_locale);
9732 goto leave;
9735 /* Check for response status */
9736 err = isds_response_status(context, service, *response,
9737 code, status_message, NULL);
9738 if (err) {
9739 isds_log(ILF_ISDS, ILL_DEBUG,
9740 _("ISDS response on %s request is missing status\n"),
9741 service_name_locale);
9742 goto leave;
9745 /* Request processed, but nothing found */
9746 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9747 char *code_locale = _isds_utf82locale((char*) *code);
9748 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9749 isds_log(ILF_ISDS, ILL_DEBUG,
9750 _("Server refused %s request for %s message ID "
9751 "(code=%s, message=%s)\n"),
9752 service_name_locale, message_id_locale,
9753 code_locale, status_message_locale);
9754 isds_log_message(context, status_message_locale);
9755 free(code_locale);
9756 free(status_message_locale);
9757 err = IE_ISDS;
9758 goto leave;
9761 leave:
9762 free(message_id_locale);
9763 free(service_name_locale);
9764 xmlFreeNode(request);
9765 return err;
9769 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9770 * signed data and free ISDS response.
9771 * @context is session context
9772 * @message_id is UTF-8 encoded message ID for logging purpose
9773 * @response is parsed XML document. It will be freed and NULLed in the middle
9774 * of function run to save memory. This is not guaranteed in case of error.
9775 * @request_name is name of ISDS request used to construct response root
9776 * element name and for logging purpose.
9777 * @raw is reallocated output buffer with DER encoded CMS data
9778 * @raw_length is size of @raw buffer in bytes
9779 * @returns standard error codes, in case of error, @raw will be freed and
9780 * NULLed, @response sometimes. */
9781 static isds_error find_extract_signed_data_free_response(
9782 struct isds_ctx *context, const xmlChar *message_id,
9783 xmlDocPtr *response, const xmlChar *request_name,
9784 void **raw, size_t *raw_length) {
9786 isds_error err = IE_SUCCESS;
9787 char *xpath_expression = NULL;
9788 xmlXPathContextPtr xpath_ctx = NULL;
9789 xmlXPathObjectPtr result = NULL;
9790 char *encoded_structure = NULL;
9792 if (!context) return IE_INVALID_CONTEXT;
9793 if (!raw) return IE_INVAL;
9794 zfree(*raw);
9795 if (!message_id || !response || !*response || !request_name || !raw_length)
9796 return IE_INVAL;
9798 /* Build XPath expression */
9799 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9800 "Response/isds:dmSignature");
9801 if (!xpath_expression) return IE_NOMEM;
9803 /* Extract data */
9804 xpath_ctx = xmlXPathNewContext(*response);
9805 if (!xpath_ctx) {
9806 err = IE_ERROR;
9807 goto leave;
9809 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9810 err = IE_ERROR;
9811 goto leave;
9813 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9814 if (!result) {
9815 err = IE_ERROR;
9816 goto leave;
9818 /* Empty response */
9819 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9820 char *message_id_locale = _isds_utf82locale((char*) message_id);
9821 isds_printf_message(context,
9822 _("Server did not return any signed data for message ID `%s' "
9823 "on %s request"),
9824 message_id_locale, request_name);
9825 free(message_id_locale);
9826 err = IE_ISDS;
9827 goto leave;
9829 /* More responses */
9830 if (result->nodesetval->nodeNr > 1) {
9831 char *message_id_locale = _isds_utf82locale((char*) message_id);
9832 isds_printf_message(context,
9833 _("Server did return more signed data for message ID `%s' "
9834 "on %s request"),
9835 message_id_locale, request_name);
9836 free(message_id_locale);
9837 err = IE_ISDS;
9838 goto leave;
9840 /* One response */
9841 xpath_ctx->node = result->nodesetval->nodeTab[0];
9843 /* Extract PKCS#7 structure */
9844 EXTRACT_STRING(".", encoded_structure);
9845 if (!encoded_structure) {
9846 isds_log_message(context, _("dmSignature element is empty"));
9849 /* Here we have delivery info as standalone CMS in encoded_structure.
9850 * We don't need any other data, free them: */
9851 xmlXPathFreeObject(result); result = NULL;
9852 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9853 xmlFreeDoc(*response); *response = NULL;
9856 /* Decode PKCS#7 to DER format */
9857 *raw_length = _isds_b64decode(encoded_structure, raw);
9858 if (*raw_length == (size_t) -1) {
9859 isds_log_message(context,
9860 _("Error while Base64-decoding PKCS#7 structure"));
9861 err = IE_ERROR;
9862 goto leave;
9865 leave:
9866 if (err) {
9867 zfree(*raw);
9868 raw_length = 0;
9871 free(encoded_structure);
9872 xmlXPathFreeObject(result);
9873 xmlXPathFreeContext(xpath_ctx);
9874 free(xpath_expression);
9876 return err;
9878 #endif /* HAVE_LIBCURL */
9881 /* Download incoming message envelope identified by ID.
9882 * @context is session context
9883 * @message_id is message identifier (you can get them from
9884 * isds_get_list_of_received_messages())
9885 * @message is automatically reallocated message retrieved from ISDS.
9886 * It will miss documents per se. Use isds_get_received_message(), if you are
9887 * interested in documents (content) too.
9888 * Returned hash and timestamp require documents to be verifiable. */
9889 isds_error isds_get_received_envelope(struct isds_ctx *context,
9890 const char *message_id, struct isds_message **message) {
9892 isds_error err = IE_SUCCESS;
9893 #if HAVE_LIBCURL
9894 xmlDocPtr response = NULL;
9895 xmlChar *code = NULL, *status_message = NULL;
9896 xmlXPathContextPtr xpath_ctx = NULL;
9897 xmlXPathObjectPtr result = NULL;
9898 #endif
9900 if (!context) return IE_INVALID_CONTEXT;
9901 zfree(context->long_message);
9903 /* Free former message if any */
9904 if (!message) return IE_INVAL;
9905 isds_message_free(message);
9907 #if HAVE_LIBCURL
9908 /* Do request and check for success */
9909 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9910 BAD_CAST "MessageEnvelopeDownload", message_id,
9911 &response, NULL, NULL, &code, &status_message);
9912 if (err) goto leave;
9914 /* Extract data */
9915 xpath_ctx = xmlXPathNewContext(response);
9916 if (!xpath_ctx) {
9917 err = IE_ERROR;
9918 goto leave;
9920 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9921 err = IE_ERROR;
9922 goto leave;
9924 result = xmlXPathEvalExpression(
9925 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9926 "isds:dmReturnedMessageEnvelope",
9927 xpath_ctx);
9928 if (!result) {
9929 err = IE_ERROR;
9930 goto leave;
9932 /* Empty response */
9933 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9934 char *message_id_locale = _isds_utf82locale((char*) message_id);
9935 isds_printf_message(context,
9936 _("Server did not return any envelope for ID `%s' "
9937 "on MessageEnvelopeDownload request"), message_id_locale);
9938 free(message_id_locale);
9939 err = IE_ISDS;
9940 goto leave;
9942 /* More envelops */
9943 if (result->nodesetval->nodeNr > 1) {
9944 char *message_id_locale = _isds_utf82locale((char*) message_id);
9945 isds_printf_message(context,
9946 _("Server did return more envelopes for ID `%s' "
9947 "on MessageEnvelopeDownload request"), message_id_locale);
9948 free(message_id_locale);
9949 err = IE_ISDS;
9950 goto leave;
9952 /* One message */
9953 xpath_ctx->node = result->nodesetval->nodeTab[0];
9955 /* Extract the envelope (= message without documents, hence 0) */
9956 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9957 if (err) goto leave;
9959 /* Save XML blob */
9960 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9961 &(*message)->raw_length);
9963 leave:
9964 if (err) {
9965 isds_message_free(message);
9968 xmlXPathFreeObject(result);
9969 xmlXPathFreeContext(xpath_ctx);
9971 free(code);
9972 free(status_message);
9973 if (!*message || !(*message)->xml) {
9974 xmlFreeDoc(response);
9977 if (!err)
9978 isds_log(ILF_ISDS, ILL_DEBUG,
9979 _("MessageEnvelopeDownload request processed by server "
9980 "successfully.\n")
9982 #else /* not HAVE_LIBCURL */
9983 err = IE_NOTSUP;
9984 #endif
9985 return err;
9989 /* Load delivery info of any format from buffer.
9990 * @context is session context
9991 * @raw_type advertises format of @buffer content. Only delivery info types
9992 * are accepted.
9993 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9994 * retrieve such data from message->raw after calling
9995 * isds_get_signed_delivery_info().
9996 * @length is length of buffer in bytes.
9997 * @message is automatically reallocated message parsed from @buffer.
9998 * @strategy selects how buffer will be attached into raw isds_message member.
9999 * */
10000 isds_error isds_load_delivery_info(struct isds_ctx *context,
10001 const isds_raw_type raw_type,
10002 const void *buffer, const size_t length,
10003 struct isds_message **message, const isds_buffer_strategy strategy) {
10005 isds_error err = IE_SUCCESS;
10006 message_ns_type message_ns;
10007 xmlDocPtr message_doc = NULL;
10008 xmlXPathContextPtr xpath_ctx = NULL;
10009 xmlXPathObjectPtr result = NULL;
10010 void *xml_stream = NULL;
10011 size_t xml_stream_length = 0;
10013 if (!context) return IE_INVALID_CONTEXT;
10014 zfree(context->long_message);
10015 if (!message) return IE_INVAL;
10016 isds_message_free(message);
10017 if (!buffer) return IE_INVAL;
10020 /* Select buffer format and extract XML from CMS*/
10021 switch (raw_type) {
10022 case RAWTYPE_DELIVERYINFO:
10023 message_ns = MESSAGE_NS_UNSIGNED;
10024 xml_stream = (void *) buffer;
10025 xml_stream_length = length;
10026 break;
10028 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
10029 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10030 xml_stream = (void *) buffer;
10031 xml_stream_length = length;
10032 break;
10034 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10035 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10036 err = _isds_extract_cms_data(context, buffer, length,
10037 &xml_stream, &xml_stream_length);
10038 if (err) goto leave;
10039 break;
10041 default:
10042 isds_log_message(context, _("Bad raw delivery representation type"));
10043 return IE_INVAL;
10044 break;
10047 isds_log(ILF_ISDS, ILL_DEBUG,
10048 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10049 xml_stream_length, xml_stream);
10051 /* Convert delivery info XML stream into XPath context */
10052 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10053 if (!message_doc) {
10054 err = IE_XML;
10055 goto leave;
10057 xpath_ctx = xmlXPathNewContext(message_doc);
10058 if (!xpath_ctx) {
10059 err = IE_ERROR;
10060 goto leave;
10062 /* XXX: Name spaces mangled for signed delivery info:
10063 * http://isds.czechpoint.cz/v20/delivery:
10065 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10066 * <q:dmDelivery>
10067 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10068 * <p:dmID>170272</p:dmID>
10069 * ...
10070 * </p:dmDm>
10071 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10072 * ...
10073 * </q:dmEvents>...</q:dmEvents>
10074 * </q:dmDelivery>
10075 * </q:GetDeliveryInfoResponse>
10076 * */
10077 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10078 err = IE_ERROR;
10079 goto leave;
10081 result = xmlXPathEvalExpression(
10082 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10083 xpath_ctx);
10084 if (!result) {
10085 err = IE_ERROR;
10086 goto leave;
10088 /* Empty delivery info */
10089 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10090 isds_printf_message(context,
10091 _("XML document is not sisds:dmDelivery document"));
10092 err = IE_ISDS;
10093 goto leave;
10095 /* More delivery info's */
10096 if (result->nodesetval->nodeNr > 1) {
10097 isds_printf_message(context,
10098 _("XML document has more sisds:dmDelivery elements"));
10099 err = IE_ISDS;
10100 goto leave;
10102 /* One delivery info */
10103 xpath_ctx->node = result->nodesetval->nodeTab[0];
10105 /* Extract the envelope (= message without documents, hence 0).
10106 * XXX: extract_TReturnedMessage() can obtain attachments size,
10107 * but delivery info carries none. It's coded as option elements,
10108 * so it should work. */
10109 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10110 if (err) goto leave;
10112 /* Extract events */
10113 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10114 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10115 if (err) { err = IE_ERROR; goto leave; }
10116 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10117 if (err) goto leave;
10119 /* Append raw CMS structure into message */
10120 (*message)->raw_type = raw_type;
10121 switch (strategy) {
10122 case BUFFER_DONT_STORE:
10123 break;
10124 case BUFFER_COPY:
10125 (*message)->raw = malloc(length);
10126 if (!(*message)->raw) {
10127 err = IE_NOMEM;
10128 goto leave;
10130 memcpy((*message)->raw, buffer, length);
10131 (*message)->raw_length = length;
10132 break;
10133 case BUFFER_MOVE:
10134 (*message)->raw = (void *) buffer;
10135 (*message)->raw_length = length;
10136 break;
10137 default:
10138 err = IE_ENUM;
10139 goto leave;
10142 leave:
10143 if (err) {
10144 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10145 isds_message_free(message);
10148 xmlXPathFreeObject(result);
10149 xmlXPathFreeContext(xpath_ctx);
10150 if (!*message || !(*message)->xml) {
10151 xmlFreeDoc(message_doc);
10153 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10155 if (!err)
10156 isds_log(ILF_ISDS, ILL_DEBUG,
10157 _("Delivery info loaded successfully.\n"));
10158 return err;
10162 /* Download signed delivery info-sheet of given message identified by ID.
10163 * @context is session context
10164 * @message_id is message identifier (you can get them from
10165 * isds_get_list_of_{sent,received}_messages())
10166 * @message is automatically reallocated message retrieved from ISDS.
10167 * It will miss documents per se. Use isds_get_signed_received_message(),
10168 * if you are interested in documents (content). OTOH, only this function
10169 * can get list events message has gone through. */
10170 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10171 const char *message_id, struct isds_message **message) {
10173 isds_error err = IE_SUCCESS;
10174 #if HAVE_LIBCURL
10175 xmlDocPtr response = NULL;
10176 xmlChar *code = NULL, *status_message = NULL;
10177 void *raw = NULL;
10178 size_t raw_length = 0;
10179 #endif
10181 if (!context) return IE_INVALID_CONTEXT;
10182 zfree(context->long_message);
10184 /* Free former message if any */
10185 if (!message) return IE_INVAL;
10186 isds_message_free(message);
10188 #if HAVE_LIBCURL
10189 /* Do request and check for success */
10190 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10191 BAD_CAST "GetSignedDeliveryInfo", message_id,
10192 &response, NULL, NULL, &code, &status_message);
10193 if (err) goto leave;
10195 /* Find signed delivery info, extract it into raw and maybe free
10196 * response */
10197 err = find_extract_signed_data_free_response(context,
10198 (xmlChar *)message_id, &response,
10199 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10200 if (err) goto leave;
10202 /* Parse delivery info */
10203 err = isds_load_delivery_info(context,
10204 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10205 message, BUFFER_MOVE);
10206 if (err) goto leave;
10208 raw = NULL;
10210 leave:
10211 if (err) {
10212 isds_message_free(message);
10215 free(raw);
10216 free(code);
10217 free(status_message);
10218 xmlFreeDoc(response);
10220 if (!err)
10221 isds_log(ILF_ISDS, ILL_DEBUG,
10222 _("GetSignedDeliveryInfo request processed by server "
10223 "successfully.\n")
10225 #else /* not HAVE_LIBCURL */
10226 err = IE_NOTSUP;
10227 #endif
10228 return err;
10232 /* Download delivery info-sheet of given message identified by ID.
10233 * @context is session context
10234 * @message_id is message identifier (you can get them from
10235 * isds_get_list_of_{sent,received}_messages())
10236 * @message is automatically reallocated message retrieved from ISDS.
10237 * It will miss documents per se. Use isds_get_received_message(), if you are
10238 * interested in documents (content). OTOH, only this function can get list
10239 * of events message has gone through. */
10240 isds_error isds_get_delivery_info(struct isds_ctx *context,
10241 const char *message_id, struct isds_message **message) {
10243 isds_error err = IE_SUCCESS;
10244 #if HAVE_LIBCURL
10245 xmlDocPtr response = NULL;
10246 xmlChar *code = NULL, *status_message = NULL;
10247 xmlNodePtr delivery_node = NULL;
10248 void *raw = NULL;
10249 size_t raw_length = 0;
10250 #endif
10252 if (!context) return IE_INVALID_CONTEXT;
10253 zfree(context->long_message);
10255 /* Free former message if any */
10256 if (!message) return IE_INVAL;
10257 isds_message_free(message);
10259 #if HAVE_LIBCURL
10260 /* Do request and check for success */
10261 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10262 BAD_CAST "GetDeliveryInfo", message_id,
10263 &response, NULL, NULL, &code, &status_message);
10264 if (err) goto leave;
10267 /* Serialize delivery info */
10268 delivery_node = xmlDocGetRootElement(response);
10269 if (!delivery_node) {
10270 char *message_id_locale = _isds_utf82locale((char*) message_id);
10271 isds_printf_message(context,
10272 _("Server did not return any delivery info for ID `%s' "
10273 "on GetDeliveryInfo request"), message_id_locale);
10274 free(message_id_locale);
10275 err = IE_ISDS;
10276 goto leave;
10278 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10279 if (err) goto leave;
10281 /* Parse delivery info */
10282 /* TODO: Here we parse the response second time. We could single delivery
10283 * parser from isds_load_delivery_info() to make things faster. */
10284 err = isds_load_delivery_info(context,
10285 RAWTYPE_DELIVERYINFO, raw, raw_length,
10286 message, BUFFER_MOVE);
10287 if (err) goto leave;
10289 raw = NULL;
10292 leave:
10293 if (err) {
10294 isds_message_free(message);
10297 free(raw);
10298 free(code);
10299 free(status_message);
10300 xmlFreeDoc(response);
10302 if (!err)
10303 isds_log(ILF_ISDS, ILL_DEBUG,
10304 _("GetDeliveryInfo request processed by server "
10305 "successfully.\n")
10307 #else /* not HAVE_LIBCURL */
10308 err = IE_NOTSUP;
10309 #endif
10310 return err;
10314 /* Download incoming message identified by ID.
10315 * @context is session context
10316 * @message_id is message identifier (you can get them from
10317 * isds_get_list_of_received_messages())
10318 * @message is automatically reallocated message retrieved from ISDS */
10319 isds_error isds_get_received_message(struct isds_ctx *context,
10320 const char *message_id, struct isds_message **message) {
10322 isds_error err = IE_SUCCESS;
10323 #if HAVE_LIBCURL
10324 xmlDocPtr response = NULL;
10325 void *xml_stream = NULL;
10326 size_t xml_stream_length;
10327 xmlChar *code = NULL, *status_message = NULL;
10328 xmlXPathContextPtr xpath_ctx = NULL;
10329 xmlXPathObjectPtr result = NULL;
10330 char *phys_path = NULL;
10331 size_t phys_start, phys_end;
10332 #endif
10334 if (!context) return IE_INVALID_CONTEXT;
10335 zfree(context->long_message);
10337 /* Free former message if any */
10338 if (NULL == message) return IE_INVAL;
10339 if (message) isds_message_free(message);
10341 #if HAVE_LIBCURL
10342 /* Do request and check for success */
10343 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10344 BAD_CAST "MessageDownload", message_id,
10345 &response, &xml_stream, &xml_stream_length,
10346 &code, &status_message);
10347 if (err) goto leave;
10349 /* Extract data */
10350 xpath_ctx = xmlXPathNewContext(response);
10351 if (!xpath_ctx) {
10352 err = IE_ERROR;
10353 goto leave;
10355 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10356 err = IE_ERROR;
10357 goto leave;
10359 result = xmlXPathEvalExpression(
10360 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10361 xpath_ctx);
10362 if (!result) {
10363 err = IE_ERROR;
10364 goto leave;
10366 /* Empty response */
10367 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10368 char *message_id_locale = _isds_utf82locale((char*) message_id);
10369 isds_printf_message(context,
10370 _("Server did not return any message for ID `%s' "
10371 "on MessageDownload request"), message_id_locale);
10372 free(message_id_locale);
10373 err = IE_ISDS;
10374 goto leave;
10376 /* More messages */
10377 if (result->nodesetval->nodeNr > 1) {
10378 char *message_id_locale = _isds_utf82locale((char*) message_id);
10379 isds_printf_message(context,
10380 _("Server did return more messages for ID `%s' "
10381 "on MessageDownload request"), message_id_locale);
10382 free(message_id_locale);
10383 err = IE_ISDS;
10384 goto leave;
10386 /* One message */
10387 xpath_ctx->node = result->nodesetval->nodeTab[0];
10389 /* Extract the message */
10390 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10391 if (err) goto leave;
10393 /* Locate raw XML blob */
10394 phys_path = strdup(
10395 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10396 PHYSXML_ELEMENT_SEPARATOR
10397 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10398 PHYSXML_ELEMENT_SEPARATOR
10399 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10401 if (!phys_path) {
10402 err = IE_NOMEM;
10403 goto leave;
10405 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10406 phys_path, &phys_start, &phys_end);
10407 zfree(phys_path);
10408 if (err) {
10409 isds_log_message(context,
10410 _("Substring with isds:MessageDownloadResponse element "
10411 "could not be located in raw SOAP message"));
10412 goto leave;
10414 /* Save XML blob */
10415 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10416 &(*message)->raw_length);*/
10417 /* TODO: Store name space declarations from ancestors */
10418 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10419 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10420 (*message)->raw_length = phys_end - phys_start + 1;
10421 (*message)->raw = malloc((*message)->raw_length);
10422 if (!(*message)->raw) {
10423 err = IE_NOMEM;
10424 goto leave;
10426 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10429 leave:
10430 if (err) {
10431 isds_message_free(message);
10434 free(phys_path);
10436 xmlXPathFreeObject(result);
10437 xmlXPathFreeContext(xpath_ctx);
10439 free(code);
10440 free(status_message);
10441 free(xml_stream);
10442 if (!*message || !(*message)->xml) {
10443 xmlFreeDoc(response);
10446 if (!err)
10447 isds_log(ILF_ISDS, ILL_DEBUG,
10448 _("MessageDownload request processed by server "
10449 "successfully.\n")
10451 #else /* not HAVE_LIBCURL */
10452 err = IE_NOTSUP;
10453 #endif
10454 return err;
10458 /* Load message of any type from buffer.
10459 * @context is session context
10460 * @raw_type defines content type of @buffer. Only message types are allowed.
10461 * @buffer is message raw representation. Format (CMS, plain signed,
10462 * message direction) is defined in @raw_type. You can retrieve such data
10463 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10464 * @length is length of buffer in bytes.
10465 * @message is automatically reallocated message parsed from @buffer.
10466 * @strategy selects how buffer will be attached into raw isds_message member.
10467 * */
10468 isds_error isds_load_message(struct isds_ctx *context,
10469 const isds_raw_type raw_type, const void *buffer, const size_t length,
10470 struct isds_message **message, const isds_buffer_strategy strategy) {
10472 isds_error err = IE_SUCCESS;
10473 void *xml_stream = NULL;
10474 size_t xml_stream_length = 0;
10475 message_ns_type message_ns;
10476 xmlDocPtr message_doc = NULL;
10477 xmlXPathContextPtr xpath_ctx = NULL;
10478 xmlXPathObjectPtr result = NULL;
10480 if (!context) return IE_INVALID_CONTEXT;
10481 zfree(context->long_message);
10482 if (!message) return IE_INVAL;
10483 isds_message_free(message);
10484 if (!buffer) return IE_INVAL;
10487 /* Select buffer format and extract XML from CMS*/
10488 switch (raw_type) {
10489 case RAWTYPE_INCOMING_MESSAGE:
10490 message_ns = MESSAGE_NS_UNSIGNED;
10491 xml_stream = (void *) buffer;
10492 xml_stream_length = length;
10493 break;
10495 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10496 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10497 xml_stream = (void *) buffer;
10498 xml_stream_length = length;
10499 break;
10501 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10502 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10503 err = _isds_extract_cms_data(context, buffer, length,
10504 &xml_stream, &xml_stream_length);
10505 if (err) goto leave;
10506 break;
10508 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10509 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10510 xml_stream = (void *) buffer;
10511 xml_stream_length = length;
10512 break;
10514 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10515 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10516 err = _isds_extract_cms_data(context, buffer, length,
10517 &xml_stream, &xml_stream_length);
10518 if (err) goto leave;
10519 break;
10521 default:
10522 isds_log_message(context, _("Bad raw message representation type"));
10523 return IE_INVAL;
10524 break;
10527 isds_log(ILF_ISDS, ILL_DEBUG,
10528 _("Loading message:\n%.*s\nEnd of message\n"),
10529 xml_stream_length, xml_stream);
10531 /* Convert messages XML stream into XPath context */
10532 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10533 if (!message_doc) {
10534 err = IE_XML;
10535 goto leave;
10537 xpath_ctx = xmlXPathNewContext(message_doc);
10538 if (!xpath_ctx) {
10539 err = IE_ERROR;
10540 goto leave;
10542 /* XXX: Standard name space for unsigned incoming direction:
10543 * http://isds.czechpoint.cz/v20/
10545 * XXX: Name spaces mangled for signed outgoing direction:
10546 * http://isds.czechpoint.cz/v20/SentMessage:
10548 * <q:MessageDownloadResponse
10549 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10550 * <q:dmReturnedMessage>
10551 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10552 * <p:dmID>151916</p:dmID>
10553 * ...
10554 * </p:dmDm>
10555 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10556 * ...
10557 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10558 * </q:dmReturnedMessage>
10559 * </q:MessageDownloadResponse>
10561 * XXX: Name spaces mangled for signed incoming direction:
10562 * http://isds.czechpoint.cz/v20/message:
10564 * <q:MessageDownloadResponse
10565 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10566 * <q:dmReturnedMessage>
10567 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10568 * <p:dmID>151916</p:dmID>
10569 * ...
10570 * </p:dmDm>
10571 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10572 * ...
10573 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10574 * </q:dmReturnedMessage>
10575 * </q:MessageDownloadResponse>
10577 * Stupidity of ISDS developers is unlimited */
10578 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10579 err = IE_ERROR;
10580 goto leave;
10582 result = xmlXPathEvalExpression(
10583 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10584 xpath_ctx);
10585 if (!result) {
10586 err = IE_ERROR;
10587 goto leave;
10589 /* Empty message */
10590 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10591 isds_printf_message(context,
10592 _("XML document does not contain "
10593 "sisds:dmReturnedMessage element"));
10594 err = IE_ISDS;
10595 goto leave;
10597 /* More messages */
10598 if (result->nodesetval->nodeNr > 1) {
10599 isds_printf_message(context,
10600 _("XML document has more sisds:dmReturnedMessage elements"));
10601 err = IE_ISDS;
10602 goto leave;
10604 /* One message */
10605 xpath_ctx->node = result->nodesetval->nodeTab[0];
10607 /* Extract the message */
10608 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10609 if (err) goto leave;
10611 /* Append raw buffer into message */
10612 (*message)->raw_type = raw_type;
10613 switch (strategy) {
10614 case BUFFER_DONT_STORE:
10615 break;
10616 case BUFFER_COPY:
10617 (*message)->raw = malloc(length);
10618 if (!(*message)->raw) {
10619 err = IE_NOMEM;
10620 goto leave;
10622 memcpy((*message)->raw, buffer, length);
10623 (*message)->raw_length = length;
10624 break;
10625 case BUFFER_MOVE:
10626 (*message)->raw = (void *) buffer;
10627 (*message)->raw_length = length;
10628 break;
10629 default:
10630 err = IE_ENUM;
10631 goto leave;
10635 leave:
10636 if (err) {
10637 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10638 isds_message_free(message);
10641 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10642 xmlXPathFreeObject(result);
10643 xmlXPathFreeContext(xpath_ctx);
10644 if (!*message || !(*message)->xml) {
10645 xmlFreeDoc(message_doc);
10648 if (!err)
10649 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10650 return err;
10654 /* Determine type of raw message or delivery info according some heuristics.
10655 * It does not validate the raw blob.
10656 * @context is session context
10657 * @raw_type returns content type of @buffer. Valid only if exit code of this
10658 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10659 * reallocated memory.
10660 * @buffer is message raw representation.
10661 * @length is length of buffer in bytes. */
10662 isds_error isds_guess_raw_type(struct isds_ctx *context,
10663 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10664 isds_error err;
10665 void *xml_stream = NULL;
10666 size_t xml_stream_length = 0;
10667 xmlDocPtr document = NULL;
10668 xmlNodePtr root = NULL;
10670 if (!context) return IE_INVALID_CONTEXT;
10671 zfree(context->long_message);
10672 if (length == 0 || !buffer) return IE_INVAL;
10673 if (!raw_type) return IE_INVAL;
10675 /* Try CMS */
10676 err = _isds_extract_cms_data(context, buffer, length,
10677 &xml_stream, &xml_stream_length);
10678 if (err) {
10679 xml_stream = (void *) buffer;
10680 xml_stream_length = (size_t) length;
10681 err = IE_SUCCESS;
10684 /* Try XML */
10685 document = xmlParseMemory(xml_stream, xml_stream_length);
10686 if (!document) {
10687 isds_printf_message(context,
10688 _("Could not parse data as XML document"));
10689 err = IE_NOTSUP;
10690 goto leave;
10693 /* Get root element */
10694 root = xmlDocGetRootElement(document);
10695 if (!root) {
10696 isds_printf_message(context,
10697 _("XML document is missing root element"));
10698 err = IE_XML;
10699 goto leave;
10702 if (!root->ns || !root->ns->href) {
10703 isds_printf_message(context,
10704 _("Root element does not belong to any name space"));
10705 err = IE_NOTSUP;
10706 goto leave;
10709 /* Test name space */
10710 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10711 if (xml_stream == buffer)
10712 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10713 else
10714 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10715 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10716 if (xml_stream == buffer)
10717 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10718 else
10719 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10720 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10721 if (xml_stream == buffer)
10722 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10723 else
10724 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10725 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10726 if (xml_stream != buffer) {
10727 isds_printf_message(context,
10728 _("Document in ISDS name space is encapsulated into CMS" ));
10729 err = IE_NOTSUP;
10730 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10731 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10732 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10733 *raw_type = RAWTYPE_DELIVERYINFO;
10734 else {
10735 isds_printf_message(context,
10736 _("Unknown root element in ISDS name space"));
10737 err = IE_NOTSUP;
10739 } else {
10740 isds_printf_message(context,
10741 _("Unknown name space"));
10742 err = IE_NOTSUP;
10745 leave:
10746 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10747 xmlFreeDoc(document);
10748 return err;
10752 /* Download signed incoming/outgoing message identified by ID.
10753 * @context is session context
10754 * @output is true for outgoing message, false for incoming message
10755 * @message_id is message identifier (you can get them from
10756 * isds_get_list_of_{sent,received}_messages())
10757 * @message is automatically reallocated message retrieved from ISDS. The raw
10758 * member will be filled with PKCS#7 structure in DER format. */
10759 static isds_error isds_get_signed_message(struct isds_ctx *context,
10760 const _Bool outgoing, const char *message_id,
10761 struct isds_message **message) {
10763 isds_error err = IE_SUCCESS;
10764 #if HAVE_LIBCURL
10765 xmlDocPtr response = NULL;
10766 xmlChar *code = NULL, *status_message = NULL;
10767 xmlXPathContextPtr xpath_ctx = NULL;
10768 xmlXPathObjectPtr result = NULL;
10769 char *encoded_structure = NULL;
10770 void *raw = NULL;
10771 size_t raw_length = 0;
10772 #endif
10774 if (!context) return IE_INVALID_CONTEXT;
10775 zfree(context->long_message);
10776 if (!message) return IE_INVAL;
10777 isds_message_free(message);
10779 #if HAVE_LIBCURL
10780 /* Do request and check for success */
10781 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10782 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10783 BAD_CAST "SignedMessageDownload",
10784 message_id, &response, NULL, NULL, &code, &status_message);
10785 if (err) goto leave;
10787 /* Find signed message, extract it into raw and maybe free
10788 * response */
10789 err = find_extract_signed_data_free_response(context,
10790 (xmlChar *)message_id, &response,
10791 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10792 BAD_CAST "SignedMessageDownload",
10793 &raw, &raw_length);
10794 if (err) goto leave;
10796 /* Parse message */
10797 err = isds_load_message(context,
10798 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10799 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10800 raw, raw_length, message, BUFFER_MOVE);
10801 if (err) goto leave;
10803 raw = NULL;
10805 leave:
10806 if (err) {
10807 isds_message_free(message);
10810 free(encoded_structure);
10811 xmlXPathFreeObject(result);
10812 xmlXPathFreeContext(xpath_ctx);
10813 free(raw);
10815 free(code);
10816 free(status_message);
10817 xmlFreeDoc(response);
10819 if (!err)
10820 isds_log(ILF_ISDS, ILL_DEBUG,
10821 (outgoing) ?
10822 _("SignedSentMessageDownload request processed by server "
10823 "successfully.\n") :
10824 _("SignedMessageDownload request processed by server "
10825 "successfully.\n")
10827 #else /* not HAVE_LIBCURL */
10828 err = IE_NOTSUP;
10829 #endif
10830 return err;
10834 /* Download signed incoming message identified by ID.
10835 * @context is session context
10836 * @message_id is message identifier (you can get them from
10837 * isds_get_list_of_received_messages())
10838 * @message is automatically reallocated message retrieved from ISDS. The raw
10839 * member will be filled with PKCS#7 structure in DER format. */
10840 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10841 const char *message_id, struct isds_message **message) {
10842 return isds_get_signed_message(context, 0, message_id, message);
10846 /* Download signed outgoing message identified by ID.
10847 * @context is session context
10848 * @message_id is message identifier (you can get them from
10849 * isds_get_list_of_sent_messages())
10850 * @message is automatically reallocated message retrieved from ISDS. The raw
10851 * member will be filled with PKCS#7 structure in DER format. */
10852 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10853 const char *message_id, struct isds_message **message) {
10854 return isds_get_signed_message(context, 1, message_id, message);
10858 /* Get type and name of user who sent a message identified by ID.
10859 * @context is session context
10860 * @message_id is message identifier
10861 * @sender_type is pointer to automatically allocated type of sender detected
10862 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10863 * library or to the server, NULL will be returned. Pass NULL if you don't
10864 * care about it.
10865 * @raw_sender_type is automatically reallocated UTF-8 string describing
10866 * sender type or NULL if not known to server. Pass NULL if you don't care.
10867 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10868 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10869 isds_error isds_get_message_sender(struct isds_ctx *context,
10870 const char *message_id, isds_sender_type **sender_type,
10871 char **raw_sender_type, char **sender_name) {
10872 isds_error err = IE_SUCCESS;
10873 #if HAVE_LIBCURL
10874 xmlDocPtr response = NULL;
10875 xmlChar *code = NULL, *status_message = NULL;
10876 xmlXPathContextPtr xpath_ctx = NULL;
10877 xmlXPathObjectPtr result = NULL;
10878 char *type_string = NULL;
10879 #endif
10881 if (!context) return IE_INVALID_CONTEXT;
10882 zfree(context->long_message);
10883 if (sender_type) zfree(*sender_type);
10884 if (raw_sender_type) zfree(*raw_sender_type);
10885 if (sender_name) zfree(*sender_name);
10886 if (!message_id) return IE_INVAL;
10888 #if HAVE_LIBCURL
10889 /* Do request and check for success */
10890 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10891 BAD_CAST "GetMessageAuthor",
10892 message_id, &response, NULL, NULL, &code, &status_message);
10893 if (err) goto leave;
10895 /* Extract data */
10896 xpath_ctx = xmlXPathNewContext(response);
10897 if (!xpath_ctx) {
10898 err = IE_ERROR;
10899 goto leave;
10901 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10902 err = IE_ERROR;
10903 goto leave;
10905 result = xmlXPathEvalExpression(
10906 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10907 if (!result) {
10908 err = IE_ERROR;
10909 goto leave;
10911 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10912 isds_log_message(context,
10913 _("Missing GetMessageAuthorResponse element"));
10914 err = IE_ISDS;
10915 goto leave;
10917 if (result->nodesetval->nodeNr > 1) {
10918 isds_log_message(context,
10919 _("Multiple GetMessageAuthorResponse element"));
10920 err = IE_ISDS;
10921 goto leave;
10923 xpath_ctx->node = result->nodesetval->nodeTab[0];
10924 xmlXPathFreeObject(result); result = NULL;
10926 /* Fill output arguments in */
10927 EXTRACT_STRING("isds:userType", type_string);
10928 if (NULL != type_string) {
10929 if (NULL != sender_type) {
10930 *sender_type = calloc(1, sizeof(**sender_type));
10931 if (NULL == *sender_type) {
10932 err = IE_NOMEM;
10933 goto leave;
10936 err = string2isds_sender_type((xmlChar *)type_string,
10937 *sender_type);
10938 if (err) {
10939 zfree(*sender_type);
10940 if (err == IE_ENUM) {
10941 err = IE_SUCCESS;
10942 char *type_string_locale = _isds_utf82locale(type_string);
10943 isds_log(ILF_ISDS, ILL_WARNING,
10944 _("Unknown isds:userType value: %s"),
10945 type_string_locale);
10946 free(type_string_locale);
10951 if (NULL != sender_name)
10952 EXTRACT_STRING("isds:authorName", *sender_name);
10954 leave:
10955 if (err) {
10956 if (NULL != sender_type) zfree(*sender_type);
10957 zfree(type_string);
10958 if (NULL != sender_name) zfree(*sender_name);
10960 if (NULL != raw_sender_type) *raw_sender_type = type_string;
10962 xmlXPathFreeObject(result);
10963 xmlXPathFreeContext(xpath_ctx);
10965 free(code);
10966 free(status_message);
10967 xmlFreeDoc(response);
10969 if (!err)
10970 isds_log(ILF_ISDS, ILL_DEBUG,
10971 _("GetMessageAuthor request processed by server "
10972 "successfully.\n"));
10973 #else /* not HAVE_LIBCURL */
10974 err = IE_NOTSUP;
10975 #endif
10976 return err;
10980 /* Retrieve hash of message identified by ID stored in ISDS.
10981 * @context is session context
10982 * @message_id is message identifier
10983 * @hash is automatically reallocated message hash downloaded from ISDS.
10984 * Message must exist in system and must not be deleted. */
10985 isds_error isds_download_message_hash(struct isds_ctx *context,
10986 const char *message_id, struct isds_hash **hash) {
10988 isds_error err = IE_SUCCESS;
10989 #if HAVE_LIBCURL
10990 xmlDocPtr response = NULL;
10991 xmlChar *code = NULL, *status_message = NULL;
10992 xmlXPathContextPtr xpath_ctx = NULL;
10993 xmlXPathObjectPtr result = NULL;
10994 #endif
10996 if (!context) return IE_INVALID_CONTEXT;
10997 zfree(context->long_message);
10999 isds_hash_free(hash);
11001 #if HAVE_LIBCURL
11002 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11003 BAD_CAST "VerifyMessage", message_id,
11004 &response, NULL, NULL, &code, &status_message);
11005 if (err) goto leave;
11008 /* Extract data */
11009 xpath_ctx = xmlXPathNewContext(response);
11010 if (!xpath_ctx) {
11011 err = IE_ERROR;
11012 goto leave;
11014 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11015 err = IE_ERROR;
11016 goto leave;
11018 result = xmlXPathEvalExpression(
11019 BAD_CAST "/isds:VerifyMessageResponse",
11020 xpath_ctx);
11021 if (!result) {
11022 err = IE_ERROR;
11023 goto leave;
11025 /* Empty response */
11026 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11027 char *message_id_locale = _isds_utf82locale((char*) message_id);
11028 isds_printf_message(context,
11029 _("Server did not return any response for ID `%s' "
11030 "on VerifyMessage request"), message_id_locale);
11031 free(message_id_locale);
11032 err = IE_ISDS;
11033 goto leave;
11035 /* More responses */
11036 if (result->nodesetval->nodeNr > 1) {
11037 char *message_id_locale = _isds_utf82locale((char*) message_id);
11038 isds_printf_message(context,
11039 _("Server did return more responses for ID `%s' "
11040 "on VerifyMessage request"), message_id_locale);
11041 free(message_id_locale);
11042 err = IE_ISDS;
11043 goto leave;
11045 /* One response */
11046 xpath_ctx->node = result->nodesetval->nodeTab[0];
11048 /* Extract the hash */
11049 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11051 leave:
11052 if (err) {
11053 isds_hash_free(hash);
11056 xmlXPathFreeObject(result);
11057 xmlXPathFreeContext(xpath_ctx);
11059 free(code);
11060 free(status_message);
11061 xmlFreeDoc(response);
11063 if (!err)
11064 isds_log(ILF_ISDS, ILL_DEBUG,
11065 _("VerifyMessage request processed by server "
11066 "successfully.\n")
11068 #else /* not HAVE_LIBCURL */
11069 err = IE_NOTSUP;
11070 #endif
11071 return err;
11075 /* Erase message specified by @message_id from long term storage. Other
11076 * message cannot be erased on user request.
11077 * @context is session context
11078 * @message_id is message identifier.
11079 * @incoming is true for incoming message, false for outgoing message.
11080 * @return
11081 * IE_SUCCESS if message has ben removed
11082 * IE_INVAL if message does not exist in long term storage or message
11083 * belongs to different box
11084 * TODO: IE_NOEPRM if user has no permission to erase a message */
11085 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11086 const char *message_id, _Bool incoming) {
11087 isds_error err = IE_SUCCESS;
11088 #if HAVE_LIBCURL
11089 xmlNodePtr request = NULL, node;
11090 xmlNsPtr isds_ns = NULL;
11091 xmlDocPtr response = NULL;
11092 xmlChar *code = NULL, *status_message = NULL;
11093 #endif
11095 if (!context) return IE_INVALID_CONTEXT;
11096 zfree(context->long_message);
11097 if (NULL == message_id) return IE_INVAL;
11099 /* Check if connection is established
11100 * TODO: This check should be done downstairs. */
11101 if (!context->curl) return IE_CONNECTION_CLOSED;
11103 #if HAVE_LIBCURL
11104 /* Build request */
11105 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11106 if (!request) {
11107 isds_log_message(context,
11108 _("Could build EraseMessage request"));
11109 return IE_ERROR;
11111 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11112 if(!isds_ns) {
11113 isds_log_message(context, _("Could not create ISDS name space"));
11114 xmlFreeNode(request);
11115 return IE_ERROR;
11117 xmlSetNs(request, isds_ns);
11119 err = validate_message_id_length(context, (xmlChar *) message_id);
11120 if (err) goto leave;
11121 INSERT_STRING(request, "dmID", message_id);
11123 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11126 /* Send request */
11127 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11128 "message ID %s to ISDS\n"), message_id);
11129 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11130 xmlFreeNode(request); request = NULL;
11132 if (err) {
11133 isds_log(ILF_ISDS, ILL_DEBUG,
11134 _("Processing ISDS response on EraseMessage request "
11135 "failed\n"));
11136 goto leave;
11139 /* Check for response status */
11140 err = isds_response_status(context, SERVICE_DM_INFO, response,
11141 &code, &status_message, NULL);
11142 if (err) {
11143 isds_log(ILF_ISDS, ILL_DEBUG,
11144 _("ISDS response on EraseMessage request is missing "
11145 "status\n"));
11146 goto leave;
11149 /* Check server status code */
11150 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11151 isds_log_message(context, _("Message to erase belongs to other box"));
11152 err = IE_INVAL;
11153 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11154 isds_log_message(context, _("Message to erase is not saved in "
11155 "long term storage or the direction does not match"));
11156 err = IE_INVAL;
11157 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11158 char *code_locale = _isds_utf82locale((char*) code);
11159 char *message_locale = _isds_utf82locale((char*) status_message);
11160 isds_log(ILF_ISDS, ILL_DEBUG,
11161 _("Server refused EraseMessage request "
11162 "(code=%s, message=%s)\n"),
11163 code_locale, message_locale);
11164 isds_log_message(context, message_locale);
11165 free(code_locale);
11166 free(message_locale);
11167 err = IE_ISDS;
11168 goto leave;
11171 leave:
11172 free(code);
11173 free(status_message);
11174 xmlFreeDoc(response);
11175 xmlFreeNode(request);
11177 if (!err)
11178 isds_log(ILF_ISDS, ILL_DEBUG,
11179 _("EraseMessage request processed by server "
11180 "successfully.\n")
11182 #else /* not HAVE_LIBCURL */
11183 err = IE_NOTSUP;
11184 #endif
11185 return err;
11189 /* Mark message as read. This is a transactional commit function to acknowledge
11190 * to ISDS the message has been downloaded and processed by client properly.
11191 * @context is session context
11192 * @message_id is message identifier. */
11193 isds_error isds_mark_message_read(struct isds_ctx *context,
11194 const char *message_id) {
11196 isds_error err = IE_SUCCESS;
11197 #if HAVE_LIBCURL
11198 xmlDocPtr response = NULL;
11199 xmlChar *code = NULL, *status_message = NULL;
11200 #endif
11202 if (!context) return IE_INVALID_CONTEXT;
11203 zfree(context->long_message);
11205 #if HAVE_LIBCURL
11206 /* Do request and check for success */
11207 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11208 BAD_CAST "MarkMessageAsDownloaded", message_id,
11209 &response, NULL, NULL, &code, &status_message);
11211 free(code);
11212 free(status_message);
11213 xmlFreeDoc(response);
11215 if (!err)
11216 isds_log(ILF_ISDS, ILL_DEBUG,
11217 _("MarkMessageAsDownloaded request processed by server "
11218 "successfully.\n")
11220 #else /* not HAVE_LIBCURL */
11221 err = IE_NOTSUP;
11222 #endif
11223 return err;
11227 /* Mark message as received by recipient. This is applicable only to
11228 * commercial message. Use envelope->dmType message member to distinguish
11229 * commercial message from government message. Government message is
11230 * received automatically (by law), commercial message on recipient request.
11231 * @context is session context
11232 * @message_id is message identifier. */
11233 isds_error isds_mark_message_received(struct isds_ctx *context,
11234 const char *message_id) {
11236 isds_error err = IE_SUCCESS;
11237 #if HAVE_LIBCURL
11238 xmlDocPtr response = NULL;
11239 xmlChar *code = NULL, *status_message = NULL;
11240 #endif
11242 if (!context) return IE_INVALID_CONTEXT;
11243 zfree(context->long_message);
11245 #if HAVE_LIBCURL
11246 /* Do request and check for success */
11247 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11248 BAD_CAST "ConfirmDelivery", message_id,
11249 &response, NULL, NULL, &code, &status_message);
11251 free(code);
11252 free(status_message);
11253 xmlFreeDoc(response);
11255 if (!err)
11256 isds_log(ILF_ISDS, ILL_DEBUG,
11257 _("ConfirmDelivery request processed by server "
11258 "successfully.\n")
11260 #else /* not HAVE_LIBCURL */
11261 err = IE_NOTSUP;
11262 #endif
11263 return err;
11267 /* Send document for authorized conversion into Czech POINT system.
11268 * This is public anonymous service, no log-in necessary. Special context is
11269 * used to reuse keep-a-live HTTPS connection.
11270 * @context is Czech POINT session context. DO NOT use context connected to
11271 * ISDS server. Use new context or context used by this function previously.
11272 * @document is document to convert. Only data, data_length, dmFileDescr and
11273 * is_xml members are significant. Be ware that not all document formats can be
11274 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11275 * @id is reallocated identifier assigned by Czech POINT system to
11276 * your document on submit. Use is to tell it to Czech POINT officer.
11277 * @date is reallocated document submit date (submitted documents
11278 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11279 * value. */
11280 isds_error czp_convert_document(struct isds_ctx *context,
11281 const struct isds_document *document,
11282 char **id, struct tm **date) {
11283 isds_error err = IE_SUCCESS;
11284 #if HAVE_LIBCURL
11285 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11286 xmlNodePtr request = NULL, node;
11287 xmlDocPtr response = NULL;
11289 xmlXPathContextPtr xpath_ctx = NULL;
11290 xmlXPathObjectPtr result = NULL;
11291 long int status = -1;
11292 long int *status_ptr = &status;
11293 char *string = NULL;
11294 #endif
11297 if (!context) return IE_INVALID_CONTEXT;
11298 zfree(context->long_message);
11299 if (!document || !id || !date) return IE_INVAL;
11301 if (document->is_xml) {
11302 isds_log_message(context,
11303 _("XML documents cannot be submitted to conversion"));
11304 return IE_NOTSUP;
11307 /* Free output arguments */
11308 zfree(*id);
11309 zfree(*date);
11311 #if HAVE_LIBCURL
11312 /* Store configuration */
11313 context->type = CTX_TYPE_CZP;
11314 free(context->url);
11315 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11316 if (!(context->url))
11317 return IE_NOMEM;
11319 /* Prepare CURL handle if not yet connected */
11320 if (!context->curl) {
11321 context->curl = curl_easy_init();
11322 if (!(context->curl))
11323 return IE_ERROR;
11326 /* Build conversion request */
11327 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11328 if (!request) {
11329 isds_log_message(context,
11330 _("Could not build Czech POINT conversion request"));
11331 return IE_ERROR;
11333 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11334 if(!deposit_ns) {
11335 isds_log_message(context,
11336 _("Could not create Czech POINT deposit name space"));
11337 xmlFreeNode(request);
11338 return IE_ERROR;
11340 xmlSetNs(request, deposit_ns);
11342 /* Insert children. They are in empty namespace! */
11343 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11344 if(!empty_ns) {
11345 isds_log_message(context, _("Could not create empty name space"));
11346 err = IE_ERROR;
11347 goto leave;
11349 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11350 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11351 document->dmFileDescr);
11353 /* Document encoded in Base64 */
11354 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11355 document->data, document->data_length);
11356 if (err) goto leave;
11358 isds_log(ILF_ISDS, ILL_DEBUG,
11359 _("Submitting document for conversion into Czech POINT deposit"));
11361 /* Send conversion request */
11362 err = _czp_czpdeposit(context, request, &response);
11363 xmlFreeNode(request); request = NULL;
11365 if (err) {
11366 czp_do_close_connection(context);
11367 goto leave;
11371 /* Extract response */
11372 xpath_ctx = xmlXPathNewContext(response);
11373 if (!xpath_ctx) {
11374 err = IE_ERROR;
11375 goto leave;
11377 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11378 err = IE_ERROR;
11379 goto leave;
11381 result = xmlXPathEvalExpression(
11382 BAD_CAST "/deposit:saveDocumentResponse/return",
11383 xpath_ctx);
11384 if (!result) {
11385 err = IE_ERROR;
11386 goto leave;
11388 /* Empty response */
11389 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11390 isds_printf_message(context,
11391 _("Missing `return' element in Czech POINT deposit response"));
11392 err = IE_ISDS;
11393 goto leave;
11395 /* More responses */
11396 if (result->nodesetval->nodeNr > 1) {
11397 isds_printf_message(context,
11398 _("Multiple `return' element in Czech POINT deposit response"));
11399 err = IE_ISDS;
11400 goto leave;
11402 /* One response */
11403 xpath_ctx->node = result->nodesetval->nodeTab[0];
11405 /* Get status */
11406 EXTRACT_LONGINT("status", status_ptr, 1);
11407 if (status) {
11408 EXTRACT_STRING("statusMsg", string);
11409 char *string_locale = _isds_utf82locale(string);
11410 isds_printf_message(context,
11411 _("Czech POINT deposit refused document for conversion "
11412 "(code=%ld, message=%s)"),
11413 status, string_locale);
11414 free(string_locale);
11415 err = IE_ISDS;
11416 goto leave;
11419 /* Get document ID */
11420 EXTRACT_STRING("documentID", *id);
11422 /* Get submit date */
11423 EXTRACT_STRING("dateInserted", string);
11424 if (string) {
11425 *date = calloc(1, sizeof(**date));
11426 if (!*date) {
11427 err = IE_NOMEM;
11428 goto leave;
11430 err = _isds_datestring2tm((xmlChar *)string, *date);
11431 if (err) {
11432 if (err == IE_NOTSUP) {
11433 err = IE_ISDS;
11434 char *string_locale = _isds_utf82locale(string);
11435 isds_printf_message(context,
11436 _("Invalid dateInserted value: %s"), string_locale);
11437 free(string_locale);
11439 goto leave;
11443 leave:
11444 free(string);
11445 xmlXPathFreeObject(result);
11446 xmlXPathFreeContext(xpath_ctx);
11448 xmlFreeDoc(response);
11449 xmlFreeNode(request);
11451 if (!err) {
11452 char *id_locale = _isds_utf82locale((char *) *id);
11453 isds_log(ILF_ISDS, ILL_DEBUG,
11454 _("Document %s has been submitted for conversion "
11455 "to server successfully\n"), id_locale);
11456 free(id_locale);
11458 #else /* not HAVE_LIBCURL */
11459 err = IE_NOTSUP;
11460 #endif
11461 return err;
11465 /* Close possibly opened connection to Czech POINT document deposit.
11466 * @context is Czech POINT session context. */
11467 isds_error czp_close_connection(struct isds_ctx *context) {
11468 if (!context) return IE_INVALID_CONTEXT;
11469 zfree(context->long_message);
11470 #if HAVE_LIBCURL
11471 return czp_do_close_connection(context);
11472 #else
11473 return IE_NOTSUP;
11474 #endif
11478 /* Send request for new box creation in testing ISDS instance.
11479 * It's not possible to request for a production box currently, as it
11480 * communicates via e-mail.
11481 * XXX: This function does not work either. Server complains about invalid
11482 * e-mail address.
11483 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11484 * this function
11485 * @context is special session context for box creation request. DO NOT use
11486 * standard context as it could reveal your password. Use fresh new context or
11487 * context previously used by this function.
11488 * @box is box description to create including single primary user (in case of
11489 * FO box type). It outputs box ID assigned by ISDS in dbID element.
11490 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11491 * box, or contact address of PFO box owner). The email member is mandatory as
11492 * it will be used to deliver credentials.
11493 * @former_names is former name of box owner. Pass NULL if you don't care.
11494 * @approval is optional external approval of box manipulation
11495 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11496 * NULL, if you don't care.*/
11497 isds_error isds_request_new_testing_box(struct isds_ctx *context,
11498 struct isds_DbOwnerInfo *box, const struct isds_list *users,
11499 const char *former_names, const struct isds_approval *approval,
11500 char **refnumber) {
11501 isds_error err = IE_SUCCESS;
11502 #if HAVE_LIBCURL
11503 xmlNodePtr request = NULL;
11504 xmlDocPtr response = NULL;
11505 xmlXPathContextPtr xpath_ctx = NULL;
11506 xmlXPathObjectPtr result = NULL;
11507 #endif
11510 if (!context) return IE_INVALID_CONTEXT;
11511 zfree(context->long_message);
11512 if (!box) return IE_INVAL;
11514 #if HAVE_LIBCURL
11515 if (!box->email || box->email[0] == '\0') {
11516 isds_log_message(context, _("E-mail field is mandatory"));
11517 return IE_INVAL;
11520 /* Scratch box ID */
11521 zfree(box->dbID);
11523 /* Store configuration */
11524 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
11525 free(context->url);
11526 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
11527 if (!(context->url))
11528 return IE_NOMEM;
11530 /* Prepare CURL handle if not yet connected */
11531 if (!context->curl) {
11532 context->curl = curl_easy_init();
11533 if (!(context->curl))
11534 return IE_ERROR;
11537 /* Build CreateDataBox request */
11538 err = build_CreateDBInput_request(context,
11539 &request, BAD_CAST "CreateDataBox",
11540 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
11541 if (err) goto leave;
11543 /* Send it to server and process response */
11544 err = send_destroy_request_check_response(context,
11545 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
11546 &response, (xmlChar **) refnumber, NULL);
11547 if (err) goto leave;
11549 /* Extract box ID */
11550 xpath_ctx = xmlXPathNewContext(response);
11551 if (!xpath_ctx) {
11552 err = IE_ERROR;
11553 goto leave;
11555 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11556 err = IE_ERROR;
11557 goto leave;
11559 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
11561 leave:
11562 xmlXPathFreeObject(result);
11563 xmlXPathFreeContext(xpath_ctx);
11564 xmlFreeDoc(response);
11565 xmlFreeNode(request);
11567 if (!err) {
11568 isds_log(ILF_ISDS, ILL_DEBUG,
11569 _("CreateDataBox request processed by server successfully.\n"));
11571 #else /* not HAVE_LIBCURL */
11572 err = IE_NOTSUP;
11573 #endif
11575 return err;
11579 /* Submit CMS signed message to ISDS to verify its originality. This is
11580 * stronger form of isds_verify_message_hash() because ISDS does more checks
11581 * than simple one (potentialy old weak) hash comparison.
11582 * @context is session context
11583 * @message is memory with raw CMS signed message bit stream
11584 * @length is @message size in bytes
11585 * @return
11586 * IE_SUCCESS if message originates in ISDS
11587 * IE_NOTEQUAL if message is unknown to ISDS
11588 * other code for other errors */
11589 isds_error isds_authenticate_message(struct isds_ctx *context,
11590 const void *message, size_t length) {
11591 isds_error err = IE_SUCCESS;
11592 #if HAVE_LIBCURL
11593 xmlNsPtr isds_ns = NULL;
11594 xmlNodePtr request = NULL;
11595 xmlDocPtr response = NULL;
11596 xmlXPathContextPtr xpath_ctx = NULL;
11597 xmlXPathObjectPtr result = NULL;
11598 _Bool *authentic = NULL;
11599 #endif
11601 if (!context) return IE_INVALID_CONTEXT;
11602 zfree(context->long_message);
11603 if (!message || length == 0) return IE_INVAL;
11605 #if HAVE_LIBCURL
11606 /* Check if connection is established
11607 * TODO: This check should be done downstairs. */
11608 if (!context->curl) return IE_CONNECTION_CLOSED;
11611 /* Build AuthenticateMessage request */
11612 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11613 if (!request) {
11614 isds_log_message(context,
11615 _("Could not build AuthenticateMessage request"));
11616 return IE_ERROR;
11618 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11619 if(!isds_ns) {
11620 isds_log_message(context, _("Could not create ISDS name space"));
11621 xmlFreeNode(request);
11622 return IE_ERROR;
11624 xmlSetNs(request, isds_ns);
11626 /* Insert Base64 encoded message */
11627 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11628 message, length);
11629 if (err) goto leave;
11631 /* Send request to server and process response */
11632 err = send_destroy_request_check_response(context,
11633 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11634 &response, NULL, NULL);
11635 if (err) goto leave;
11638 /* ISDS has decided */
11639 xpath_ctx = xmlXPathNewContext(response);
11640 if (!xpath_ctx) {
11641 err = IE_ERROR;
11642 goto leave;
11644 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11645 err = IE_ERROR;
11646 goto leave;
11649 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11651 if (!authentic) {
11652 isds_log_message(context,
11653 _("Server did not return any response on "
11654 "AuthenticateMessage request"));
11655 err = IE_ISDS;
11656 goto leave;
11658 if (*authentic) {
11659 isds_log(ILF_ISDS, ILL_DEBUG,
11660 _("ISDS authenticated the message successfully\n"));
11661 } else {
11662 isds_log_message(context, _("ISDS does not know the message"));
11663 err = IE_NOTEQUAL;
11667 leave:
11668 free(authentic);
11669 xmlXPathFreeObject(result);
11670 xmlXPathFreeContext(xpath_ctx);
11672 xmlFreeDoc(response);
11673 xmlFreeNode(request);
11674 #else /* not HAVE_LIBCURL */
11675 err = IE_NOTSUP;
11676 #endif
11678 return err;
11682 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11683 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11684 * be re-signed.
11685 * @context is session context
11686 * @input_data is memory with raw CMS signed message or delivery info bit
11687 * stream to re-sign
11688 * @input_length is @input_data size in bytes
11689 * @output_data is pointer to auto-allocated memory where to store re-signed
11690 * input data blob. Caller must free it.
11691 * @output_data is pointer where to store @output_data size in bytes
11692 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11693 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11694 * @return
11695 * IE_SUCCESS if CMS blob has been re-signed successfully
11696 * other code for other errors */
11697 isds_error isds_resign_message(struct isds_ctx *context,
11698 const void *input_data, size_t input_length,
11699 void **output_data, size_t *output_length, struct tm **valid_to) {
11700 isds_error err = IE_SUCCESS;
11701 #if HAVE_LIBCURL
11702 xmlNsPtr isds_ns = NULL;
11703 xmlNodePtr request = NULL;
11704 xmlDocPtr response = NULL;
11705 xmlXPathContextPtr xpath_ctx = NULL;
11706 xmlXPathObjectPtr result = NULL;
11707 char *string = NULL;
11708 const xmlChar *codes[] = {
11709 BAD_CAST "2200",
11710 BAD_CAST "2201",
11711 BAD_CAST "2204",
11712 BAD_CAST "2207",
11713 NULL
11715 const char *meanings[] = {
11716 "Message is bad",
11717 "Message is not original",
11718 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11719 "Time stamp could not been generated in time"
11721 const isds_error errors[] = {
11722 IE_INVAL,
11723 IE_NOTUNIQ,
11724 IE_INVAL,
11725 IE_ISDS,
11727 struct code_map_isds_error map = {
11728 .codes = codes,
11729 .meanings = meanings,
11730 .errors = errors
11732 #endif
11734 if (NULL != output_data) *output_data = NULL;
11735 if (NULL != output_length) *output_length = 0;
11736 if (NULL != valid_to) *valid_to = NULL;
11738 if (NULL == context) return IE_INVALID_CONTEXT;
11739 zfree(context->long_message);
11740 if (NULL == input_data || 0 == input_length) {
11741 isds_log_message(context, _("Empty CMS blob on input"));
11742 return IE_INVAL;
11744 if (NULL == output_data || NULL == output_length) {
11745 isds_log_message(context,
11746 _("NULL pointer provided for output CMS blob"));
11747 return IE_INVAL;
11750 #if HAVE_LIBCURL
11751 /* Check if connection is established
11752 * TODO: This check should be done downstairs. */
11753 if (!context->curl) return IE_CONNECTION_CLOSED;
11756 /* Build Re-signISDSDocument request */
11757 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11758 if (!request) {
11759 isds_log_message(context,
11760 _("Could not build Re-signISDSDocument request"));
11761 return IE_ERROR;
11763 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11764 if(!isds_ns) {
11765 isds_log_message(context, _("Could not create ISDS name space"));
11766 xmlFreeNode(request);
11767 return IE_ERROR;
11769 xmlSetNs(request, isds_ns);
11771 /* Insert Base64 encoded CMS blob */
11772 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11773 input_data, input_length);
11774 if (err) goto leave;
11776 /* Send request to server and process response */
11777 err = send_destroy_request_check_response(context,
11778 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11779 &response, NULL, &map);
11780 if (err) goto leave;
11783 /* Extract re-signed data */
11784 xpath_ctx = xmlXPathNewContext(response);
11785 if (!xpath_ctx) {
11786 err = IE_ERROR;
11787 goto leave;
11789 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11790 err = IE_ERROR;
11791 goto leave;
11793 result = xmlXPathEvalExpression(
11794 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11795 if (!result) {
11796 err = IE_ERROR;
11797 goto leave;
11799 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11800 isds_log_message(context,
11801 _("Missing Re-signISDSDocumentResponse element"));
11802 err = IE_ISDS;
11803 goto leave;
11805 if (result->nodesetval->nodeNr > 1) {
11806 isds_log_message(context,
11807 _("Multiple Re-signISDSDocumentResponse element"));
11808 err = IE_ISDS;
11809 goto leave;
11811 xpath_ctx->node = result->nodesetval->nodeTab[0];
11812 xmlXPathFreeObject(result); result = NULL;
11814 EXTRACT_STRING("isds:dmResultDoc", string);
11815 /* Decode non-empty data */
11816 if (NULL != string && string[0] != '\0') {
11817 *output_length = _isds_b64decode(string, output_data);
11818 if (*output_length == (size_t) -1) {
11819 isds_log_message(context,
11820 _("Error while Base64-decoding re-signed data"));
11821 err = IE_ERROR;
11822 goto leave;
11824 } else {
11825 isds_log_message(context, _("Server did not send re-signed data"));
11826 err = IE_ISDS;
11827 goto leave;
11829 zfree(string);
11831 if (NULL != valid_to) {
11832 /* Get time stamp expiration date */
11833 EXTRACT_STRING("isds:dmValidTo", string);
11834 if (NULL != string) {
11835 *valid_to = calloc(1, sizeof(**valid_to));
11836 if (!*valid_to) {
11837 err = IE_NOMEM;
11838 goto leave;
11840 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11841 if (err) {
11842 if (err == IE_NOTSUP) {
11843 err = IE_ISDS;
11844 char *string_locale = _isds_utf82locale(string);
11845 isds_printf_message(context,
11846 _("Invalid dmValidTo value: %s"), string_locale);
11847 free(string_locale);
11849 goto leave;
11854 leave:
11855 free(string);
11857 xmlXPathFreeObject(result);
11858 xmlXPathFreeContext(xpath_ctx);
11860 xmlFreeDoc(response);
11861 xmlFreeNode(request);
11862 #else /* not HAVE_LIBCURL */
11863 err = IE_NOTSUP;
11864 #endif
11866 return err;
11869 #undef INSERT_ELEMENT
11870 #undef CHECK_FOR_STRING_LENGTH
11871 #undef INSERT_STRING_ATTRIBUTE
11872 #undef INSERT_ULONGINTNOPTR
11873 #undef INSERT_ULONGINT
11874 #undef INSERT_LONGINT
11875 #undef INSERT_BOOLEAN
11876 #undef INSERT_SCALAR_BOOLEAN
11877 #undef INSERT_STRING
11878 #undef INSERT_STRING_WITH_NS
11879 #undef EXTRACT_STRING_ATTRIBUTE
11880 #undef EXTRACT_ULONGINT
11881 #undef EXTRACT_LONGINT
11882 #undef EXTRACT_BOOLEAN
11883 #undef EXTRACT_STRING
11886 /* Compute hash of message from raw representation and store it into envelope.
11887 * Original hash structure will be destroyed in envelope.
11888 * @context is session context
11889 * @message is message carrying raw XML message blob
11890 * @algorithm is desired hash algorithm to use */
11891 isds_error isds_compute_message_hash(struct isds_ctx *context,
11892 struct isds_message *message, const isds_hash_algorithm algorithm) {
11893 isds_error err = IE_SUCCESS;
11894 const char *nsuri;
11895 void *xml_stream = NULL;
11896 size_t xml_stream_length;
11897 size_t phys_start, phys_end;
11898 char *phys_path = NULL;
11899 struct isds_hash *new_hash = NULL;
11902 if (!context) return IE_INVALID_CONTEXT;
11903 zfree(context->long_message);
11904 if (!message) return IE_INVAL;
11906 if (!message->raw) {
11907 isds_log_message(context,
11908 _("Message does not carry raw representation"));
11909 return IE_INVAL;
11912 switch (message->raw_type) {
11913 case RAWTYPE_INCOMING_MESSAGE:
11914 nsuri = ISDS_NS;
11915 xml_stream = message->raw;
11916 xml_stream_length = message->raw_length;
11917 break;
11919 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11920 nsuri = SISDS_INCOMING_NS;
11921 xml_stream = message->raw;
11922 xml_stream_length = message->raw_length;
11923 break;
11925 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11926 nsuri = SISDS_INCOMING_NS;
11927 err = _isds_extract_cms_data(context,
11928 message->raw, message->raw_length,
11929 &xml_stream, &xml_stream_length);
11930 if (err) goto leave;
11931 break;
11933 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11934 nsuri = SISDS_OUTGOING_NS;
11935 xml_stream = message->raw;
11936 xml_stream_length = message->raw_length;
11937 break;
11939 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11940 nsuri = SISDS_OUTGOING_NS;
11941 err = _isds_extract_cms_data(context,
11942 message->raw, message->raw_length,
11943 &xml_stream, &xml_stream_length);
11944 if (err) goto leave;
11945 break;
11947 default:
11948 isds_log_message(context, _("Bad raw representation type"));
11949 return IE_INVAL;
11950 break;
11954 /* XXX: Hash is computed from original string representing isds:dmDm
11955 * subtree. That means no encoding, white space, xmlns attributes changes.
11956 * In other words, input for hash can be invalid XML stream. */
11957 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
11958 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11959 PHYSXML_ELEMENT_SEPARATOR,
11960 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
11961 PHYSXML_ELEMENT_SEPARATOR
11962 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
11963 err = IE_NOMEM;
11964 goto leave;
11966 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11967 phys_path, &phys_start, &phys_end);
11968 zfree(phys_path);
11969 if (err) {
11970 isds_log_message(context,
11971 _("Substring with isds:dmDM element could not be located "
11972 "in raw message"));
11973 goto leave;
11977 /* Compute hash */
11978 new_hash = calloc(1, sizeof(*new_hash));
11979 if (!new_hash) {
11980 err = IE_NOMEM;
11981 goto leave;
11983 new_hash->algorithm = algorithm;
11984 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
11985 new_hash);
11986 if (err) {
11987 isds_log_message(context, _("Could not compute message hash"));
11988 goto leave;
11991 /* Save computed hash */
11992 if (!message->envelope) {
11993 message->envelope = calloc(1, sizeof(*message->envelope));
11994 if (!message->envelope) {
11995 err = IE_NOMEM;
11996 goto leave;
11999 isds_hash_free(&message->envelope->hash);
12000 message->envelope->hash = new_hash;
12002 leave:
12003 if (err) {
12004 isds_hash_free(&new_hash);
12007 free(phys_path);
12008 if (xml_stream != message->raw) free(xml_stream);
12009 return err;
12013 /* Compare two hashes.
12014 * @h1 is first hash
12015 * @h2 is another hash
12016 * @return
12017 * IE_SUCCESS if hashes equal
12018 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12019 * IE_ENUM if not comparable, but both structures defined
12020 * IE_INVAL if some of the structures are undefined (NULL)
12021 * IE_ERROR if internal error occurs */
12022 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
12023 if (h1 == NULL || h2 == NULL) return IE_INVAL;
12024 if (h1->algorithm != h2->algorithm) return IE_ENUM;
12025 if (h1->length != h2->length) return IE_ERROR;
12026 if (h1->length > 0 && !h1->value) return IE_ERROR;
12027 if (h2->length > 0 && !h2->value) return IE_ERROR;
12029 for (size_t i = 0; i < h1->length; i++) {
12030 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12031 return IE_NOTEQUAL;
12033 return IE_SUCCESS;
12037 /* Check message has gone through ISDS by comparing message hash stored in
12038 * ISDS and locally computed hash. You must provide message with valid raw
12039 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12040 * This is convenient wrapper for isds_download_message_hash(),
12041 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12042 * @context is session context
12043 * @message is message with valid raw and envelope member; envelope->hash
12044 * member will be changed during function run. Use envelope on heap only.
12045 * @return
12046 * IE_SUCCESS if message originates in ISDS
12047 * IE_NOTEQUAL if message is unknown to ISDS
12048 * other code for other errors */
12049 isds_error isds_verify_message_hash(struct isds_ctx *context,
12050 struct isds_message *message) {
12051 isds_error err = IE_SUCCESS;
12052 struct isds_hash *downloaded_hash = NULL;
12054 if (!context) return IE_INVALID_CONTEXT;
12055 zfree(context->long_message);
12056 if (!message) return IE_INVAL;
12058 if (!message->envelope) {
12059 isds_log_message(context,
12060 _("Given message structure is missing envelope"));
12061 return IE_INVAL;
12063 if (!message->raw) {
12064 isds_log_message(context,
12065 _("Given message structure is missing raw representation"));
12066 return IE_INVAL;
12069 err = isds_download_message_hash(context, message->envelope->dmID,
12070 &downloaded_hash);
12071 if (err) goto leave;
12073 err = isds_compute_message_hash(context, message,
12074 downloaded_hash->algorithm);
12075 if (err) goto leave;
12077 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12079 leave:
12080 isds_hash_free(&downloaded_hash);
12081 return err;
12085 /* Search for document by document ID in list of documents. IDs are compared
12086 * as UTF-8 string.
12087 * @documents is list of isds_documents
12088 * @id is document identifier
12089 * @return first matching document or NULL. */
12090 const struct isds_document *isds_find_document_by_id(
12091 const struct isds_list *documents, const char *id) {
12092 const struct isds_list *item;
12093 const struct isds_document *document;
12095 for (item = documents; item; item = item->next) {
12096 document = (struct isds_document *) item->data;
12097 if (!document) continue;
12099 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12100 return document;
12103 return NULL;
12107 /* Normalize @mime_type to be proper MIME type.
12108 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12109 * guess regular MIME type (e.g. "application/pdf").
12110 * @mime_type is UTF-8 encoded MIME type to fix
12111 * @return original @mime_type if no better interpretation exists, or
12112 * constant static UTF-8 encoded string with proper MIME type. */
12113 const char *isds_normalize_mime_type(const char *mime_type) {
12114 if (!mime_type) return NULL;
12116 for (size_t offset = 0;
12117 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12118 offset += 2) {
12119 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12120 extension_map_mime[offset]))
12121 return (const char *) extension_map_mime[offset + 1];
12124 return mime_type;
12128 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12129 struct isds_message **message);
12130 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12131 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12132 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12133 struct isds_address **address);
12135 int isds_message_free(struct isds_message **message);
12136 int isds_address_free(struct isds_address **address);
12140 /* Makes known all relevant namespaces to given XPath context
12141 * @xpath_ctx is XPath context
12142 * @message_ns selects proper message name space. Unsigned and signed
12143 * messages and delivery info's differ in prefix and URI. */
12144 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12145 const message_ns_type message_ns) {
12146 const xmlChar *message_namespace = NULL;
12148 if (!xpath_ctx) return IE_ERROR;
12150 switch(message_ns) {
12151 case MESSAGE_NS_1:
12152 message_namespace = BAD_CAST ISDS1_NS; break;
12153 case MESSAGE_NS_UNSIGNED:
12154 message_namespace = BAD_CAST ISDS_NS; break;
12155 case MESSAGE_NS_SIGNED_INCOMING:
12156 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12157 case MESSAGE_NS_SIGNED_OUTGOING:
12158 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12159 case MESSAGE_NS_SIGNED_DELIVERY:
12160 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12161 default:
12162 return IE_ENUM;
12165 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12166 return IE_ERROR;
12167 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12168 return IE_ERROR;
12169 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12170 return IE_ERROR;
12171 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12172 return IE_ERROR;
12173 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12174 return IE_ERROR;
12175 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12176 return IE_ERROR;
12177 return IE_SUCCESS;