Use PRIdMAX instead of %jd formatting string
[libisds.git] / src / isds.c
blobc8f4ff45909c95c3f5ef36c88b38d0ee0688c3ed
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;
2228 if (!time || !string) return IE_INVAL;
2230 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
2231 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2233 /* TODO: small negative year should be formatted as "-0012". This is not
2234 * true for glibc "%04d". We should implement it.
2235 * time->tv_usec type is su_seconds_t which is required to be signed
2236 * integer to accomodate values from range [-1, 1000000].
2237 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2238 if (-1 == isds_asprintf((char **) string,
2239 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRIdMAX,
2240 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2241 broken.tm_hour, broken.tm_min, broken.tm_sec,
2242 (intmax_t)time->tv_usec))
2243 return IE_ERROR;
2245 return IE_SUCCESS;
2247 #endif /* HAVE_LIBCURL */
2250 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2251 * It respects microseconds too. Microseconds are rounded half up.
2252 * In case of error, @time will be freed. */
2253 static isds_error timestring2timeval(const xmlChar *string,
2254 struct timeval **time) {
2255 struct tm broken;
2256 char *offset, *delim, *endptr;
2257 const int subsecond_resolution = 6;
2258 char subseconds[subsecond_resolution + 1];
2259 _Bool round_up = 0;
2260 int offset_hours, offset_minutes;
2261 int i;
2262 long int long_number;
2263 #ifdef _WIN32
2264 int tmp;
2265 #endif
2267 if (!time) return IE_INVAL;
2268 if (!string) {
2269 zfree(*time);
2270 return IE_INVAL;
2273 memset(&broken, 0, sizeof(broken));
2275 if (!*time) {
2276 *time = calloc(1, sizeof(**time));
2277 if (!*time) return IE_NOMEM;
2278 } else {
2279 memset(*time, 0, sizeof(**time));
2283 /* xsd:date is ISO 8601 string, thus ASCII */
2284 /*TODO: negative year */
2286 #ifdef _WIN32
2287 i = 0;
2288 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2289 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2290 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2291 &i)) < 6) {
2292 zfree(*time);
2293 return IE_DATE;
2296 broken.tm_year -= 1900;
2297 broken.tm_mon--;
2298 offset = (char*)string + i;
2299 #else
2300 /* Parse date and time without subseconds and offset */
2301 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2302 if (!offset) {
2303 zfree(*time);
2304 return IE_DATE;
2306 #endif
2308 /* Get subseconds */
2309 if (*offset == '.' ) {
2310 offset++;
2312 /* Copy first 6 digits, pad it with zeros.
2313 * Current server implementation uses only millisecond resolution. */
2314 /* TODO: isdigit() is locale sensitive */
2315 for (i = 0;
2316 i < subsecond_resolution && isdigit(*offset);
2317 i++, offset++) {
2318 subseconds[i] = *offset;
2320 if (subsecond_resolution == i && isdigit(*offset)) {
2321 /* Check 7th digit for rounding */
2322 if (*offset >= '5') round_up = 1;
2323 offset++;
2325 for (; i < subsecond_resolution; i++) {
2326 subseconds[i] = '0';
2328 subseconds[subsecond_resolution] = '\0';
2330 /* Convert it into integer */
2331 long_number = strtol(subseconds, &endptr, 10);
2332 if (*endptr != '\0' || long_number == LONG_MIN ||
2333 long_number == LONG_MAX) {
2334 zfree(*time);
2335 return IE_DATE;
2337 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2338 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2339 * microseconds" and "the type shall be a signed integer capable of
2340 * storing values at least in the range [-1, 1000000]. */
2341 if (long_number < -1 || long_number >= 1000000) {
2342 zfree(*time);
2343 return IE_DATE;
2345 (*time)->tv_usec = long_number;
2347 /* Round the subseconds */
2348 if (round_up) {
2349 if (999999 == (*time)->tv_usec) {
2350 (*time)->tv_usec = 0;
2351 broken.tm_sec++;
2352 } else {
2353 (*time)->tv_usec++;
2357 /* move to the zone offset delimiter or signal NULL*/
2358 delim = strchr(offset, '-');
2359 if (!delim)
2360 delim = strchr(offset, '+');
2361 if (!delim)
2362 delim = strchr(offset, 'Z');
2363 offset = delim;
2366 /* Get zone offset */
2367 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2368 * "" equals to "Z" and it means UTC zone. */
2369 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2370 * colon separator */
2371 if (offset && (*offset == '-' || *offset == '+')) {
2372 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2373 zfree(*time);
2374 return IE_DATE;
2376 if (*offset == '+') {
2377 broken.tm_hour -= offset_hours;
2378 broken.tm_min -= offset_minutes;
2379 } else {
2380 broken.tm_hour += offset_hours;
2381 broken.tm_min += offset_minutes;
2385 /* Convert to time_t */
2386 (*time)->tv_sec = _isds_timegm(&broken);
2387 if ((*time)->tv_sec == (time_t) -1) {
2388 zfree(*time);
2389 return IE_DATE;
2392 return IE_SUCCESS;
2396 /* Convert unsigned int into isds_message_status.
2397 * @context is session context
2398 * @number is pointer to number value. NULL will be treated as invalid value.
2399 * @status is automatically reallocated status
2400 * @return IE_SUCCESS, or error code and free status */
2401 static isds_error uint2isds_message_status(struct isds_ctx *context,
2402 const unsigned long int *number, isds_message_status **status) {
2403 if (!context) return IE_INVALID_CONTEXT;
2404 if (!status) return IE_INVAL;
2406 free(*status); *status = NULL;
2407 if (!number) return IE_INVAL;
2409 if (*number < 1 || *number > 10) {
2410 isds_printf_message(context, _("Invalid message status value: %lu"),
2411 *number);
2412 return IE_ENUM;
2415 *status = malloc(sizeof(**status));
2416 if (!*status) return IE_NOMEM;
2418 **status = 1 << *number;
2419 return IE_SUCCESS;
2423 /* Convert event description string into isds_event members type and
2424 * description
2425 * @string is raw event description starting with event prefix
2426 * @event is structure where to store type and stripped description to
2427 * @return standard error code, unknown prefix is not classified as an error.
2428 * */
2429 static isds_error eventstring2event(const xmlChar *string,
2430 struct isds_event* event) {
2431 const xmlChar *known_prefixes[] = {
2432 BAD_CAST "EV0:",
2433 BAD_CAST "EV1:",
2434 BAD_CAST "EV2:",
2435 BAD_CAST "EV3:",
2436 BAD_CAST "EV4:",
2437 BAD_CAST "EV5:",
2438 BAD_CAST "EV11:",
2439 BAD_CAST "EV12:",
2440 BAD_CAST "EV13:"
2442 const isds_event_type types[] = {
2443 EVENT_ENTERED_SYSTEM,
2444 EVENT_ACCEPTED_BY_RECIPIENT,
2445 EVENT_ACCEPTED_BY_FICTION,
2446 EVENT_UNDELIVERABLE,
2447 EVENT_COMMERCIAL_ACCEPTED,
2448 EVENT_DELIVERED,
2449 EVENT_PRIMARY_LOGIN,
2450 EVENT_ENTRUSTED_LOGIN,
2451 EVENT_SYSCERT_LOGIN
2453 unsigned int index;
2454 size_t length;
2456 if (!string || !event) return IE_INVAL;
2458 if (!event->type) {
2459 event->type = malloc(sizeof(*event->type));
2460 if (!(event->type)) return IE_NOMEM;
2462 zfree(event->description);
2464 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2465 index++) {
2466 length = xmlUTF8Strlen(known_prefixes[index]);
2468 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2469 /* Prefix is known */
2470 *event->type = types[index];
2472 /* Strip prefix from description and spaces */
2473 /* TODO: Recognize all white spaces from UCS blank class and
2474 * operate on UTF-8 chars. */
2475 for (; string[length] != '\0' && string[length] == ' '; length++);
2476 event->description = strdup((char *) (string + length));
2477 if (!(event->description)) return IE_NOMEM;
2479 return IE_SUCCESS;
2483 /* Unknown event prefix.
2484 * XSD allows any string */
2485 char *string_locale = _isds_utf82locale((char *) string);
2486 isds_log(ILF_ISDS, ILL_WARNING,
2487 _("Unknown delivery info event prefix: %s\n"), string_locale);
2488 free(string_locale);
2490 *event->type = EVENT_UKNOWN;
2491 event->description = strdup((char *) string);
2492 if (!(event->description)) return IE_NOMEM;
2494 return IE_SUCCESS;
2498 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2499 * and leave label */
2500 #define EXTRACT_STRING(element, string) { \
2501 xmlXPathFreeObject(result); \
2502 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2503 if (NULL == (result)) { \
2504 err = IE_ERROR; \
2505 goto leave; \
2507 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2508 if (result->nodesetval->nodeNr > 1) { \
2509 isds_printf_message(context, _("Multiple %s element"), element); \
2510 err = IE_ERROR; \
2511 goto leave; \
2513 (string) = (char *) \
2514 xmlXPathCastNodeSetToString(result->nodesetval); \
2515 if (NULL == (string)) { \
2516 err = IE_ERROR; \
2517 goto leave; \
2522 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2524 char *string = NULL; \
2525 EXTRACT_STRING(element, string); \
2527 if (string) { \
2528 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2529 if (!(booleanPtr)) { \
2530 free(string); \
2531 err = IE_NOMEM; \
2532 goto leave; \
2535 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2536 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2537 *(booleanPtr) = 1; \
2538 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2539 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2540 *(booleanPtr) = 0; \
2541 else { \
2542 char *string_locale = _isds_utf82locale((char*)string); \
2543 isds_printf_message(context, \
2544 _("%s value is not valid boolean: %s"), \
2545 element, string_locale); \
2546 free(string_locale); \
2547 free(string); \
2548 err = IE_ERROR; \
2549 goto leave; \
2552 free(string); \
2556 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2558 char *string = NULL; \
2559 EXTRACT_STRING(element, string); \
2561 if (NULL == string) { \
2562 isds_printf_message(context, _("%s element is empty"), element); \
2563 err = IE_ERROR; \
2564 goto leave; \
2566 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2567 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2568 (boolean) = 1; \
2569 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2570 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2571 (boolean) = 0; \
2572 else { \
2573 char *string_locale = _isds_utf82locale((char*)string); \
2574 isds_printf_message(context, \
2575 _("%s value is not valid boolean: %s"), \
2576 element, string_locale); \
2577 free(string_locale); \
2578 free(string); \
2579 err = IE_ERROR; \
2580 goto leave; \
2583 free(string); \
2586 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2588 char *string = NULL; \
2589 EXTRACT_STRING(element, string); \
2590 if (string) { \
2591 long int number; \
2592 char *endptr; \
2594 number = strtol((char*)string, &endptr, 10); \
2596 if (*endptr != '\0') { \
2597 char *string_locale = _isds_utf82locale((char *)string); \
2598 isds_printf_message(context, \
2599 _("%s is not valid integer: %s"), \
2600 element, string_locale); \
2601 free(string_locale); \
2602 free(string); \
2603 err = IE_ISDS; \
2604 goto leave; \
2607 if (number == LONG_MIN || number == LONG_MAX) { \
2608 char *string_locale = _isds_utf82locale((char *)string); \
2609 isds_printf_message(context, \
2610 _("%s value out of range of long int: %s"), \
2611 element, string_locale); \
2612 free(string_locale); \
2613 free(string); \
2614 err = IE_ERROR; \
2615 goto leave; \
2618 free(string); string = NULL; \
2620 if (!(preallocated)) { \
2621 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2622 if (!(longintPtr)) { \
2623 err = IE_NOMEM; \
2624 goto leave; \
2627 *(longintPtr) = number; \
2631 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2633 char *string = NULL; \
2634 EXTRACT_STRING(element, string); \
2635 if (string) { \
2636 long int number; \
2637 char *endptr; \
2639 number = strtol((char*)string, &endptr, 10); \
2641 if (*endptr != '\0') { \
2642 char *string_locale = _isds_utf82locale((char *)string); \
2643 isds_printf_message(context, \
2644 _("%s is not valid integer: %s"), \
2645 element, string_locale); \
2646 free(string_locale); \
2647 free(string); \
2648 err = IE_ISDS; \
2649 goto leave; \
2652 if (number == LONG_MIN || number == LONG_MAX) { \
2653 char *string_locale = _isds_utf82locale((char *)string); \
2654 isds_printf_message(context, \
2655 _("%s value out of range of long int: %s"), \
2656 element, string_locale); \
2657 free(string_locale); \
2658 free(string); \
2659 err = IE_ERROR; \
2660 goto leave; \
2663 free(string); string = NULL; \
2664 if (number < 0) { \
2665 isds_printf_message(context, \
2666 _("%s value is negative: %ld"), element, number); \
2667 err = IE_ERROR; \
2668 goto leave; \
2671 if (!(preallocated)) { \
2672 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2673 if (!(ulongintPtr)) { \
2674 err = IE_NOMEM; \
2675 goto leave; \
2678 *(ulongintPtr) = number; \
2682 #define EXTRACT_DATE(element, tmPtr) { \
2683 char *string = NULL; \
2684 EXTRACT_STRING(element, string); \
2685 if (NULL != string) { \
2686 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2687 if (NULL == (tmPtr)) { \
2688 free(string); \
2689 err = IE_NOMEM; \
2690 goto leave; \
2692 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2693 if (err) { \
2694 if (err == IE_NOTSUP) { \
2695 err = IE_ISDS; \
2696 char *string_locale = _isds_utf82locale(string); \
2697 char *element_locale = _isds_utf82locale(element); \
2698 isds_printf_message(context, _("Invalid %s value: %s"), \
2699 element_locale, string_locale); \
2700 free(string_locale); \
2701 free(element_locale); \
2703 free(string); \
2704 goto leave; \
2706 free(string); \
2710 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2711 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2712 NULL); \
2713 if ((required) && (!string)) { \
2714 char *attribute_locale = _isds_utf82locale(attribute); \
2715 char *element_locale = \
2716 _isds_utf82locale((char *)xpath_ctx->node->name); \
2717 isds_printf_message(context, \
2718 _("Could not extract required %s attribute value from " \
2719 "%s element"), attribute_locale, element_locale); \
2720 free(element_locale); \
2721 free(attribute_locale); \
2722 err = IE_ERROR; \
2723 goto leave; \
2728 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2730 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2731 (xmlChar *) (string)); \
2732 if (!node) { \
2733 isds_printf_message(context, \
2734 _("Could not add %s child to %s element"), \
2735 element, (parent)->name); \
2736 err = IE_ERROR; \
2737 goto leave; \
2741 #define INSERT_STRING(parent, element, string) \
2742 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2744 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2746 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2747 else { INSERT_STRING(parent, element, "false"); } \
2750 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2752 if (booleanPtr) { \
2753 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2754 } else { \
2755 INSERT_STRING(parent, element, NULL); \
2759 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2760 if ((longintPtr)) { \
2761 /* FIXME: locale sensitive */ \
2762 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2763 err = IE_NOMEM; \
2764 goto leave; \
2766 INSERT_STRING(parent, element, buffer) \
2767 free(buffer); (buffer) = NULL; \
2768 } else { INSERT_STRING(parent, element, NULL) } \
2771 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2772 if ((ulongintPtr)) { \
2773 /* FIXME: locale sensitive */ \
2774 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2775 err = IE_NOMEM; \
2776 goto leave; \
2778 INSERT_STRING(parent, element, buffer) \
2779 free(buffer); (buffer) = NULL; \
2780 } else { INSERT_STRING(parent, element, NULL) } \
2783 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2785 /* FIXME: locale sensitive */ \
2786 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2787 err = IE_NOMEM; \
2788 goto leave; \
2790 INSERT_STRING(parent, element, buffer) \
2791 free(buffer); (buffer) = NULL; \
2794 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2795 * new attribute. */
2796 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2798 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2799 (xmlChar *) (string)); \
2800 if (!attribute_node) { \
2801 isds_printf_message(context, _("Could not add %s " \
2802 "attribute to %s element"), \
2803 (attribute), (parent)->name); \
2804 err = IE_ERROR; \
2805 goto leave; \
2809 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2810 if (string) { \
2811 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2812 if (length > (maximum)) { \
2813 isds_printf_message(context, \
2814 ngettext("%s has more than %d characters", \
2815 "%s has more than %d characters", (maximum)), \
2816 (name), (maximum)); \
2817 err = IE_2BIG; \
2818 goto leave; \
2820 if (length < (minimum)) { \
2821 isds_printf_message(context, \
2822 ngettext("%s has less than %d characters", \
2823 "%s has less than %d characters", (minimum)), \
2824 (name), (minimum)); \
2825 err = IE_2SMALL; \
2826 goto leave; \
2831 #define INSERT_ELEMENT(child, parent, element) \
2833 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2834 if (!(child)) { \
2835 isds_printf_message(context, \
2836 _("Could not add %s child to %s element"), \
2837 (element), (parent)->name); \
2838 err = IE_ERROR; \
2839 goto leave; \
2844 /* Find child element by name in given XPath context and switch context onto
2845 * it. The child must be uniq and must exist. Otherwise fails.
2846 * @context is ISDS context
2847 * @child is child element name
2848 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2849 * into it child. In error case, the @xpath_ctx keeps original value. */
2850 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2851 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2852 isds_error err = IE_SUCCESS;
2853 xmlXPathObjectPtr result = NULL;
2855 if (!context) return IE_INVALID_CONTEXT;
2856 if (!child || !xpath_ctx) return IE_INVAL;
2858 /* Find child */
2859 result = xmlXPathEvalExpression(child, xpath_ctx);
2860 if (!result) {
2861 err = IE_XML;
2862 goto leave;
2865 /* No match */
2866 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2867 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2868 char *child_locale = _isds_utf82locale((char*) child);
2869 isds_printf_message(context,
2870 _("%s element does not contain %s child"),
2871 parent_locale, child_locale);
2872 free(child_locale);
2873 free(parent_locale);
2874 err = IE_NOEXIST;
2875 goto leave;
2878 /* More matches */
2879 if (result->nodesetval->nodeNr > 1) {
2880 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2881 char *child_locale = _isds_utf82locale((char*) child);
2882 isds_printf_message(context,
2883 _("%s element contains multiple %s children"),
2884 parent_locale, child_locale);
2885 free(child_locale);
2886 free(parent_locale);
2887 err = IE_NOTUNIQ;
2888 goto leave;
2891 /* Switch context */
2892 xpath_ctx->node = result->nodesetval->nodeTab[0];
2894 leave:
2895 xmlXPathFreeObject(result);
2896 return err;
2901 #if HAVE_LIBCURL
2902 /* Find and convert XSD:gPersonName group in current node into structure
2903 * @context is ISDS context
2904 * @personName is automatically reallocated person name structure. If no member
2905 * value is found, will be freed.
2906 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2907 * elements
2908 * In case of error @personName will be freed. */
2909 static isds_error extract_gPersonName(struct isds_ctx *context,
2910 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2911 isds_error err = IE_SUCCESS;
2912 xmlXPathObjectPtr result = NULL;
2914 if (!context) return IE_INVALID_CONTEXT;
2915 if (!personName) return IE_INVAL;
2916 isds_PersonName_free(personName);
2917 if (!xpath_ctx) return IE_INVAL;
2920 *personName = calloc(1, sizeof(**personName));
2921 if (!*personName) {
2922 err = IE_NOMEM;
2923 goto leave;
2926 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2927 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2928 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2929 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2931 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2932 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2933 isds_PersonName_free(personName);
2935 leave:
2936 if (err) isds_PersonName_free(personName);
2937 xmlXPathFreeObject(result);
2938 return err;
2942 /* Find and convert XSD:gAddress group in current node into structure
2943 * @context is ISDS context
2944 * @address is automatically reallocated address structure. If no member
2945 * value is found, will be freed.
2946 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2947 * elements
2948 * In case of error @address will be freed. */
2949 static isds_error extract_gAddress(struct isds_ctx *context,
2950 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2951 isds_error err = IE_SUCCESS;
2952 xmlXPathObjectPtr result = NULL;
2954 if (!context) return IE_INVALID_CONTEXT;
2955 if (!address) return IE_INVAL;
2956 isds_Address_free(address);
2957 if (!xpath_ctx) return IE_INVAL;
2960 *address = calloc(1, sizeof(**address));
2961 if (!*address) {
2962 err = IE_NOMEM;
2963 goto leave;
2966 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2967 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2968 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2969 EXTRACT_STRING("isds:adNumberInMunicipality",
2970 (*address)->adNumberInMunicipality);
2971 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2972 EXTRACT_STRING("isds:adState", (*address)->adState);
2974 if (!(*address)->adCity && !(*address)->adStreet &&
2975 !(*address)->adNumberInStreet &&
2976 !(*address)->adNumberInMunicipality &&
2977 !(*address)->adZipCode && !(*address)->adState)
2978 isds_Address_free(address);
2980 leave:
2981 if (err) isds_Address_free(address);
2982 xmlXPathFreeObject(result);
2983 return err;
2987 /* Find and convert isds:biDate element in current node into structure
2988 * @context is ISDS context
2989 * @biDate is automatically reallocated birth date structure. If no member
2990 * value is found, will be freed.
2991 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2992 * element
2993 * In case of error @biDate will be freed. */
2994 static isds_error extract_BiDate(struct isds_ctx *context,
2995 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2996 isds_error err = IE_SUCCESS;
2997 xmlXPathObjectPtr result = NULL;
2998 char *string = NULL;
3000 if (!context) return IE_INVALID_CONTEXT;
3001 if (!biDate) return IE_INVAL;
3002 zfree(*biDate);
3003 if (!xpath_ctx) return IE_INVAL;
3005 EXTRACT_STRING("isds:biDate", string);
3006 if (string) {
3007 *biDate = calloc(1, sizeof(**biDate));
3008 if (!*biDate) {
3009 err = IE_NOMEM;
3010 goto leave;
3012 err = _isds_datestring2tm((xmlChar *)string, *biDate);
3013 if (err) {
3014 if (err == IE_NOTSUP) {
3015 err = IE_ISDS;
3016 char *string_locale = _isds_utf82locale(string);
3017 isds_printf_message(context,
3018 _("Invalid isds:biDate value: %s"), string_locale);
3019 free(string_locale);
3021 goto leave;
3025 leave:
3026 if (err) zfree(*biDate);
3027 free(string);
3028 xmlXPathFreeObject(result);
3029 return err;
3033 /* Convert isds:dBOwnerInfo XML tree into structure
3034 * @context is ISDS context
3035 * @db_owner_info is automatically reallocated box owner info structure
3036 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
3037 * In case of error @db_owner_info will be freed. */
3038 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
3039 struct isds_DbOwnerInfo **db_owner_info,
3040 xmlXPathContextPtr xpath_ctx) {
3041 isds_error err = IE_SUCCESS;
3042 xmlXPathObjectPtr result = NULL;
3043 char *string = NULL;
3045 if (!context) return IE_INVALID_CONTEXT;
3046 if (!db_owner_info) return IE_INVAL;
3047 isds_DbOwnerInfo_free(db_owner_info);
3048 if (!xpath_ctx) return IE_INVAL;
3051 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3052 if (!*db_owner_info) {
3053 err = IE_NOMEM;
3054 goto leave;
3057 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
3059 EXTRACT_STRING("isds:dbType", string);
3060 if (string) {
3061 (*db_owner_info)->dbType =
3062 calloc(1, sizeof(*((*db_owner_info)->dbType)));
3063 if (!(*db_owner_info)->dbType) {
3064 err = IE_NOMEM;
3065 goto leave;
3067 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
3068 if (err) {
3069 zfree((*db_owner_info)->dbType);
3070 if (err == IE_ENUM) {
3071 err = IE_ISDS;
3072 char *string_locale = _isds_utf82locale(string);
3073 isds_printf_message(context, _("Unknown isds:dbType: %s"),
3074 string_locale);
3075 free(string_locale);
3077 goto leave;
3079 zfree(string);
3082 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
3084 err = extract_gPersonName(context, &(*db_owner_info)->personName,
3085 xpath_ctx);
3086 if (err) goto leave;
3088 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
3090 (*db_owner_info)->birthInfo =
3091 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
3092 if (!(*db_owner_info)->birthInfo) {
3093 err = IE_NOMEM;
3094 goto leave;
3096 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
3097 xpath_ctx);
3098 if (err) goto leave;
3099 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3100 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3101 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3102 if (!(*db_owner_info)->birthInfo->biDate &&
3103 !(*db_owner_info)->birthInfo->biCity &&
3104 !(*db_owner_info)->birthInfo->biCounty &&
3105 !(*db_owner_info)->birthInfo->biState)
3106 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3108 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3109 if (err) goto leave;
3111 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3112 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3113 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3114 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3115 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3117 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3119 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3120 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3121 (*db_owner_info)->dbOpenAddressing);
3123 leave:
3124 if (err) isds_DbOwnerInfo_free(db_owner_info);
3125 free(string);
3126 xmlXPathFreeObject(result);
3127 return err;
3131 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3132 * @context is session context
3133 * @owner is libisds structure with box description
3134 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3135 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3136 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
3138 isds_error err = IE_SUCCESS;
3139 xmlNodePtr node;
3140 xmlChar *string = NULL;
3142 if (!context) return IE_INVALID_CONTEXT;
3143 if (!owner || !db_owner_info) return IE_INVAL;
3146 /* Build XSD:tDbOwnerInfo */
3147 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3148 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3150 /* dbType */
3151 if (owner->dbType) {
3152 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
3153 if (!type_string) {
3154 isds_printf_message(context, _("Invalid dbType value: %d"),
3155 *(owner->dbType));
3156 err = IE_ENUM;
3157 goto leave;
3159 INSERT_STRING(db_owner_info, "dbType", type_string);
3161 INSERT_STRING(db_owner_info, "ic", owner->ic);
3162 if (owner->personName) {
3163 INSERT_STRING(db_owner_info, "pnFirstName",
3164 owner->personName->pnFirstName);
3165 INSERT_STRING(db_owner_info, "pnMiddleName",
3166 owner->personName->pnMiddleName);
3167 INSERT_STRING(db_owner_info, "pnLastName",
3168 owner->personName->pnLastName);
3169 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3170 owner->personName->pnLastNameAtBirth);
3172 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3173 if (owner->birthInfo) {
3174 if (owner->birthInfo->biDate) {
3175 if (!tm2datestring(owner->birthInfo->biDate, &string))
3176 INSERT_STRING(db_owner_info, "biDate", string);
3177 free(string); string = NULL;
3179 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
3180 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
3181 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
3183 if (owner->address) {
3184 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
3185 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
3186 INSERT_STRING(db_owner_info, "adNumberInStreet",
3187 owner->address->adNumberInStreet);
3188 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3189 owner->address->adNumberInMunicipality);
3190 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
3191 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
3193 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3194 INSERT_STRING(db_owner_info, "email", owner->email);
3195 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3197 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3198 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3200 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3201 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3203 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3205 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3206 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3207 owner->dbOpenAddressing);
3209 leave:
3210 free(string);
3211 return err;
3215 /* Convert XSD:tDbUserInfo XML tree into structure
3216 * @context is ISDS context
3217 * @db_user_info is automatically reallocated user info structure
3218 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3219 * In case of error @db_user_info will be freed. */
3220 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3221 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3222 isds_error err = IE_SUCCESS;
3223 xmlXPathObjectPtr result = NULL;
3224 char *string = NULL;
3226 if (!context) return IE_INVALID_CONTEXT;
3227 if (!db_user_info) return IE_INVAL;
3228 isds_DbUserInfo_free(db_user_info);
3229 if (!xpath_ctx) return IE_INVAL;
3232 *db_user_info = calloc(1, sizeof(**db_user_info));
3233 if (!*db_user_info) {
3234 err = IE_NOMEM;
3235 goto leave;
3238 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3240 EXTRACT_STRING("isds:userType", string);
3241 if (string) {
3242 (*db_user_info)->userType =
3243 calloc(1, sizeof(*((*db_user_info)->userType)));
3244 if (!(*db_user_info)->userType) {
3245 err = IE_NOMEM;
3246 goto leave;
3248 err = string2isds_UserType((xmlChar *)string,
3249 (*db_user_info)->userType);
3250 if (err) {
3251 zfree((*db_user_info)->userType);
3252 if (err == IE_ENUM) {
3253 err = IE_ISDS;
3254 char *string_locale = _isds_utf82locale(string);
3255 isds_printf_message(context,
3256 _("Unknown isds:userType value: %s"), string_locale);
3257 free(string_locale);
3259 goto leave;
3261 zfree(string);
3264 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3266 (*db_user_info)->personName =
3267 calloc(1, sizeof(*((*db_user_info)->personName)));
3268 if (!(*db_user_info)->personName) {
3269 err = IE_NOMEM;
3270 goto leave;
3273 err = extract_gPersonName(context, &(*db_user_info)->personName,
3274 xpath_ctx);
3275 if (err) goto leave;
3277 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3278 if (err) goto leave;
3280 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3281 if (err) goto leave;
3283 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3284 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3286 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3287 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3288 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3290 /* ???: Default value is "CZ" according specification. Should we provide
3291 * it? */
3292 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3294 leave:
3295 if (err) isds_DbUserInfo_free(db_user_info);
3296 free(string);
3297 xmlXPathFreeObject(result);
3298 return err;
3302 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3303 * @context is session context
3304 * @user is libisds structure with user description
3305 * @db_user_info is XML element of XSD:tDbUserInfo */
3306 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3307 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3309 isds_error err = IE_SUCCESS;
3310 xmlNodePtr node;
3311 xmlChar *string = NULL;
3313 if (!context) return IE_INVALID_CONTEXT;
3314 if (!user || !db_user_info) return IE_INVAL;
3316 /* Build XSD:tDbUserInfo */
3317 if (user->personName) {
3318 INSERT_STRING(db_user_info, "pnFirstName",
3319 user->personName->pnFirstName);
3320 INSERT_STRING(db_user_info, "pnMiddleName",
3321 user->personName->pnMiddleName);
3322 INSERT_STRING(db_user_info, "pnLastName",
3323 user->personName->pnLastName);
3324 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3325 user->personName->pnLastNameAtBirth);
3327 if (user->address) {
3328 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3329 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3330 INSERT_STRING(db_user_info, "adNumberInStreet",
3331 user->address->adNumberInStreet);
3332 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3333 user->address->adNumberInMunicipality);
3334 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3335 INSERT_STRING(db_user_info, "adState", user->address->adState);
3337 if (user->biDate) {
3338 if (!tm2datestring(user->biDate, &string))
3339 INSERT_STRING(db_user_info, "biDate", string);
3340 zfree(string);
3342 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3343 INSERT_STRING(db_user_info, "userID", user->userID);
3345 /* userType */
3346 if (user->userType) {
3347 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3348 if (!type_string) {
3349 isds_printf_message(context, _("Invalid userType value: %d"),
3350 *(user->userType));
3351 err = IE_ENUM;
3352 goto leave;
3354 INSERT_STRING(db_user_info, "userType", type_string);
3357 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3358 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3359 INSERT_STRING(db_user_info, "ic", user->ic);
3360 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3361 INSERT_STRING(db_user_info, "firmName", user->firmName);
3362 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3363 INSERT_STRING(db_user_info, "caCity", user->caCity);
3364 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3365 INSERT_STRING(db_user_info, "caState", user->caState);
3367 leave:
3368 free(string);
3369 return err;
3373 /* Convert XSD:tPDZRec XML tree into structure
3374 * @context is ISDS context
3375 * @permission is automatically reallocated commercial permission structure
3376 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3377 * In case of error @permission will be freed. */
3378 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3379 struct isds_commercial_permission **permission,
3380 xmlXPathContextPtr xpath_ctx) {
3381 isds_error err = IE_SUCCESS;
3382 xmlXPathObjectPtr result = NULL;
3383 char *string = NULL;
3385 if (!context) return IE_INVALID_CONTEXT;
3386 if (!permission) return IE_INVAL;
3387 isds_commercial_permission_free(permission);
3388 if (!xpath_ctx) return IE_INVAL;
3391 *permission = calloc(1, sizeof(**permission));
3392 if (!*permission) {
3393 err = IE_NOMEM;
3394 goto leave;
3397 EXTRACT_STRING("isds:PDZType", string);
3398 if (string) {
3399 err = string2isds_payment_type((xmlChar *)string,
3400 &(*permission)->type);
3401 if (err) {
3402 if (err == IE_ENUM) {
3403 err = IE_ISDS;
3404 char *string_locale = _isds_utf82locale(string);
3405 isds_printf_message(context,
3406 _("Unknown isds:PDZType value: %s"), string_locale);
3407 free(string_locale);
3409 goto leave;
3411 zfree(string);
3414 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3415 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3417 EXTRACT_STRING("isds:PDZExpire", string);
3418 if (string) {
3419 err = timestring2timeval((xmlChar *) string,
3420 &((*permission)->expiration));
3421 if (err) {
3422 char *string_locale = _isds_utf82locale(string);
3423 if (err == IE_DATE) err = IE_ISDS;
3424 isds_printf_message(context,
3425 _("Could not convert PDZExpire as ISO time: %s"),
3426 string_locale);
3427 free(string_locale);
3428 goto leave;
3430 zfree(string);
3433 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3434 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3436 leave:
3437 if (err) isds_commercial_permission_free(permission);
3438 free(string);
3439 xmlXPathFreeObject(result);
3440 return err;
3444 /* Convert XSD:tCiRecord XML tree into structure
3445 * @context is ISDS context
3446 * @event is automatically reallocated commercial credit event structure
3447 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3448 * In case of error @event will be freed. */
3449 static isds_error extract_CiRecord(struct isds_ctx *context,
3450 struct isds_credit_event **event,
3451 xmlXPathContextPtr xpath_ctx) {
3452 isds_error err = IE_SUCCESS;
3453 xmlXPathObjectPtr result = NULL;
3454 char *string = NULL;
3455 long int *number_ptr;
3457 if (!context) return IE_INVALID_CONTEXT;
3458 if (!event) return IE_INVAL;
3459 isds_credit_event_free(event);
3460 if (!xpath_ctx) return IE_INVAL;
3463 *event = calloc(1, sizeof(**event));
3464 if (!*event) {
3465 err = IE_NOMEM;
3466 goto leave;
3469 EXTRACT_STRING("isds:ciEventTime", string);
3470 if (string) {
3471 err = timestring2timeval((xmlChar *) string,
3472 &(*event)->time);
3473 if (err) {
3474 char *string_locale = _isds_utf82locale(string);
3475 if (err == IE_DATE) err = IE_ISDS;
3476 isds_printf_message(context,
3477 _("Could not convert ciEventTime as ISO time: %s"),
3478 string_locale);
3479 free(string_locale);
3480 goto leave;
3482 zfree(string);
3485 EXTRACT_STRING("isds:ciEventType", string);
3486 if (string) {
3487 err = string2isds_credit_event_type((xmlChar *)string,
3488 &(*event)->type);
3489 if (err) {
3490 if (err == IE_ENUM) {
3491 err = IE_ISDS;
3492 char *string_locale = _isds_utf82locale(string);
3493 isds_printf_message(context,
3494 _("Unknown isds:ciEventType value: %s"), string_locale);
3495 free(string_locale);
3497 goto leave;
3499 zfree(string);
3502 number_ptr = &((*event)->credit_change);
3503 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3504 number_ptr = &(*event)->new_credit;
3505 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3507 switch((*event)->type) {
3508 case ISDS_CREDIT_CHARGED:
3509 EXTRACT_STRING("isds:ciTransID",
3510 (*event)->details.charged.transaction);
3511 break;
3512 case ISDS_CREDIT_DISCHARGED:
3513 EXTRACT_STRING("isds:ciTransID",
3514 (*event)->details.discharged.transaction);
3515 break;
3516 case ISDS_CREDIT_MESSAGE_SENT:
3517 EXTRACT_STRING("isds:ciRecipientID",
3518 (*event)->details.message_sent.recipient);
3519 EXTRACT_STRING("isds:ciPDZID",
3520 (*event)->details.message_sent.message_id);
3521 break;
3522 case ISDS_CREDIT_STORAGE_SET:
3523 number_ptr = &((*event)->details.storage_set.new_capacity);
3524 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3525 EXTRACT_DATE("isds:ciNewFrom",
3526 (*event)->details.storage_set.new_valid_from);
3527 EXTRACT_DATE("isds:ciNewTo",
3528 (*event)->details.storage_set.new_valid_to);
3529 EXTRACT_LONGINT("isds:ciOldCapacity",
3530 (*event)->details.storage_set.old_capacity, 0);
3531 EXTRACT_DATE("isds:ciOldFrom",
3532 (*event)->details.storage_set.old_valid_from);
3533 EXTRACT_DATE("isds:ciOldTo",
3534 (*event)->details.storage_set.old_valid_to);
3535 EXTRACT_STRING("isds:ciDoneBy",
3536 (*event)->details.storage_set.initiator);
3537 break;
3538 case ISDS_CREDIT_EXPIRED:
3539 break;
3542 leave:
3543 if (err) isds_credit_event_free(event);
3544 free(string);
3545 xmlXPathFreeObject(result);
3546 return err;
3550 #endif /* HAVE_LIBCURL */
3553 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3554 * isds_envelope structure. The envelope is automatically allocated but not
3555 * reallocated. The date are just appended into envelope structure.
3556 * @context is ISDS context
3557 * @envelope is automatically allocated message envelope structure
3558 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3559 * In case of error @envelope will be freed. */
3560 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3561 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3562 isds_error err = IE_SUCCESS;
3563 xmlXPathObjectPtr result = NULL;
3565 if (!context) return IE_INVALID_CONTEXT;
3566 if (!envelope) return IE_INVAL;
3567 if (!xpath_ctx) return IE_INVAL;
3570 if (!*envelope) {
3571 /* Allocate envelope */
3572 *envelope = calloc(1, sizeof(**envelope));
3573 if (!*envelope) {
3574 err = IE_NOMEM;
3575 goto leave;
3577 } else {
3578 /* Else free former data */
3579 zfree((*envelope)->dmSenderOrgUnit);
3580 zfree((*envelope)->dmSenderOrgUnitNum);
3581 zfree((*envelope)->dbIDRecipient);
3582 zfree((*envelope)->dmRecipientOrgUnit);
3583 zfree((*envelope)->dmRecipientOrgUnitNum);
3584 zfree((*envelope)->dmToHands);
3585 zfree((*envelope)->dmAnnotation);
3586 zfree((*envelope)->dmRecipientRefNumber);
3587 zfree((*envelope)->dmSenderRefNumber);
3588 zfree((*envelope)->dmRecipientIdent);
3589 zfree((*envelope)->dmSenderIdent);
3590 zfree((*envelope)->dmLegalTitleLaw);
3591 zfree((*envelope)->dmLegalTitleYear);
3592 zfree((*envelope)->dmLegalTitleSect);
3593 zfree((*envelope)->dmLegalTitlePar);
3594 zfree((*envelope)->dmLegalTitlePoint);
3595 zfree((*envelope)->dmPersonalDelivery);
3596 zfree((*envelope)->dmAllowSubstDelivery);
3599 /* Extract envelope elements added by sender or ISDS
3600 * (XSD: gMessageEnvelopeSub type) */
3601 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3602 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3603 (*envelope)->dmSenderOrgUnitNum, 0);
3604 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3605 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3606 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3607 (*envelope)->dmRecipientOrgUnitNum, 0);
3608 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3609 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3610 EXTRACT_STRING("isds:dmRecipientRefNumber",
3611 (*envelope)->dmRecipientRefNumber);
3612 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3613 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3614 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3616 /* Extract envelope elements regarding law reference */
3617 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3618 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3619 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3620 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3621 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3623 /* Extract envelope other elements */
3624 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3625 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3626 (*envelope)->dmAllowSubstDelivery);
3628 leave:
3629 if (err) isds_envelope_free(envelope);
3630 xmlXPathFreeObject(result);
3631 return err;
3636 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3637 * isds_envelope structure. The envelope is automatically allocated but not
3638 * reallocated. The date are just appended into envelope structure.
3639 * @context is ISDS context
3640 * @envelope is automatically allocated message envelope structure
3641 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3642 * In case of error @envelope will be freed. */
3643 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3644 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3645 isds_error err = IE_SUCCESS;
3646 xmlXPathObjectPtr result = NULL;
3648 if (!context) return IE_INVALID_CONTEXT;
3649 if (!envelope) return IE_INVAL;
3650 if (!xpath_ctx) return IE_INVAL;
3653 if (!*envelope) {
3654 /* Allocate envelope */
3655 *envelope = calloc(1, sizeof(**envelope));
3656 if (!*envelope) {
3657 err = IE_NOMEM;
3658 goto leave;
3660 } else {
3661 /* Else free former data */
3662 zfree((*envelope)->dmID);
3663 zfree((*envelope)->dbIDSender);
3664 zfree((*envelope)->dmSender);
3665 zfree((*envelope)->dmSenderAddress);
3666 zfree((*envelope)->dmSenderType);
3667 zfree((*envelope)->dmRecipient);
3668 zfree((*envelope)->dmRecipientAddress);
3669 zfree((*envelope)->dmAmbiguousRecipient);
3672 /* Extract envelope elements added by ISDS
3673 * (XSD: gMessageEnvelope type) */
3674 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3675 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3676 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3677 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3678 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3679 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3680 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3681 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3682 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3683 (*envelope)->dmAmbiguousRecipient);
3685 /* Extract envelope elements added by sender and ISDS
3686 * (XSD: gMessageEnvelope type) */
3687 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3688 if (err) goto leave;
3690 leave:
3691 if (err) isds_envelope_free(envelope);
3692 xmlXPathFreeObject(result);
3693 return err;
3697 /* Convert other envelope elements from XML tree into isds_envelope structure:
3698 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3699 * The envelope is automatically allocated but not reallocated.
3700 * The data are just appended into envelope structure.
3701 * @context is ISDS context
3702 * @envelope is automatically allocated message envelope structure
3703 * @xpath_ctx is XPath context with current node as parent desired elements
3704 * In case of error @envelope will be freed. */
3705 static isds_error append_status_size_times(struct isds_ctx *context,
3706 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3707 isds_error err = IE_SUCCESS;
3708 xmlXPathObjectPtr result = NULL;
3709 char *string = NULL;
3710 unsigned long int *unumber = NULL;
3712 if (!context) return IE_INVALID_CONTEXT;
3713 if (!envelope) return IE_INVAL;
3714 if (!xpath_ctx) return IE_INVAL;
3717 if (!*envelope) {
3718 /* Allocate new */
3719 *envelope = calloc(1, sizeof(**envelope));
3720 if (!*envelope) {
3721 err = IE_NOMEM;
3722 goto leave;
3724 } else {
3725 /* Free old data */
3726 zfree((*envelope)->dmMessageStatus);
3727 zfree((*envelope)->dmAttachmentSize);
3728 zfree((*envelope)->dmDeliveryTime);
3729 zfree((*envelope)->dmAcceptanceTime);
3733 /* dmMessageStatus element is mandatory */
3734 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3735 if (!unumber) {
3736 isds_log_message(context,
3737 _("Missing mandatory sisds:dmMessageStatus integer"));
3738 err = IE_ISDS;
3739 goto leave;
3741 err = uint2isds_message_status(context, unumber,
3742 &((*envelope)->dmMessageStatus));
3743 if (err) {
3744 if (err == IE_ENUM) err = IE_ISDS;
3745 goto leave;
3747 free(unumber); unumber = NULL;
3749 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3752 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3753 if (string) {
3754 err = timestring2timeval((xmlChar *) string,
3755 &((*envelope)->dmDeliveryTime));
3756 if (err) {
3757 char *string_locale = _isds_utf82locale(string);
3758 if (err == IE_DATE) err = IE_ISDS;
3759 isds_printf_message(context,
3760 _("Could not convert dmDeliveryTime as ISO time: %s"),
3761 string_locale);
3762 free(string_locale);
3763 goto leave;
3765 zfree(string);
3768 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3769 if (string) {
3770 err = timestring2timeval((xmlChar *) string,
3771 &((*envelope)->dmAcceptanceTime));
3772 if (err) {
3773 char *string_locale = _isds_utf82locale(string);
3774 if (err == IE_DATE) err = IE_ISDS;
3775 isds_printf_message(context,
3776 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3777 string_locale);
3778 free(string_locale);
3779 goto leave;
3781 zfree(string);
3784 leave:
3785 if (err) isds_envelope_free(envelope);
3786 free(unumber);
3787 free(string);
3788 xmlXPathFreeObject(result);
3789 return err;
3793 /* Convert message type attribute of current element into isds_envelope
3794 * structure.
3795 * TODO: This function can be incorporated into append_status_size_times() as
3796 * they are called always together.
3797 * The envelope is automatically allocated but not reallocated.
3798 * The data are just appended into envelope structure.
3799 * @context is ISDS context
3800 * @envelope is automatically allocated message envelope structure
3801 * @xpath_ctx is XPath context with current node as parent of attribute
3802 * carrying message type
3803 * In case of error @envelope will be freed. */
3804 static isds_error append_message_type(struct isds_ctx *context,
3805 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3806 isds_error err = IE_SUCCESS;
3808 if (!context) return IE_INVALID_CONTEXT;
3809 if (!envelope) return IE_INVAL;
3810 if (!xpath_ctx) return IE_INVAL;
3813 if (!*envelope) {
3814 /* Allocate new */
3815 *envelope = calloc(1, sizeof(**envelope));
3816 if (!*envelope) {
3817 err = IE_NOMEM;
3818 goto leave;
3820 } else {
3821 /* Free old data */
3822 zfree((*envelope)->dmType);
3826 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3828 if (!(*envelope)->dmType) {
3829 /* Use default value */
3830 (*envelope)->dmType = strdup("V");
3831 if (!(*envelope)->dmType) {
3832 err = IE_NOMEM;
3833 goto leave;
3835 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3836 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3837 isds_printf_message(context,
3838 _("Message type in dmType attribute is not 1 character long: "
3839 "%s"),
3840 type_locale);
3841 free(type_locale);
3842 err = IE_ISDS;
3843 goto leave;
3846 leave:
3847 if (err) isds_envelope_free(envelope);
3848 return err;
3852 #if HAVE_LIBCURL
3853 /* Convert dmType isds_envelope member into XML attribute and append it to
3854 * current node.
3855 * @context is ISDS context
3856 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3857 * @dm_envelope is XML element the resulting attribute will be appended to.
3858 * @return error code, in case of error context' message is filled. */
3859 static isds_error insert_message_type(struct isds_ctx *context,
3860 const char *type, xmlNodePtr dm_envelope) {
3861 isds_error err = IE_SUCCESS;
3862 xmlAttrPtr attribute_node;
3864 if (!context) return IE_INVALID_CONTEXT;
3865 if (!dm_envelope) return IE_INVAL;
3867 /* Insert optional message type */
3868 if (type) {
3869 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3870 char *type_locale = _isds_utf82locale(type);
3871 isds_printf_message(context,
3872 _("Message type in envelope is not 1 character long: %s"),
3873 type_locale);
3874 free(type_locale);
3875 err = IE_INVAL;
3876 goto leave;
3878 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3881 leave:
3882 return err;
3884 #endif /* HAVE_LIBCURL */
3887 /* Extract message document into reallocated document structure
3888 * @context is ISDS context
3889 * @document is automatically reallocated message documents structure
3890 * @xpath_ctx is XPath context with current node as isds:dmFile
3891 * In case of error @document will be freed. */
3892 static isds_error extract_document(struct isds_ctx *context,
3893 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3894 isds_error err = IE_SUCCESS;
3895 xmlXPathObjectPtr result = NULL;
3896 xmlNodePtr file_node;
3897 char *string = NULL;
3899 if (!context) return IE_INVALID_CONTEXT;
3900 if (!document) return IE_INVAL;
3901 isds_document_free(document);
3902 if (!xpath_ctx) return IE_INVAL;
3903 file_node = xpath_ctx->node;
3905 *document = calloc(1, sizeof(**document));
3906 if (!*document) {
3907 err = IE_NOMEM;
3908 goto leave;
3911 /* Extract document meta data */
3912 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3913 if (context->normalize_mime_type) {
3914 const char *normalized_type =
3915 isds_normalize_mime_type((*document)->dmMimeType);
3916 if (NULL != normalized_type &&
3917 normalized_type != (*document)->dmMimeType) {
3918 char *new_type = strdup(normalized_type);
3919 if (NULL == new_type) {
3920 isds_printf_message(context,
3921 _("Not enough memory to normalize document MIME type"));
3922 err = IE_NOMEM;
3923 goto leave;
3925 free((*document)->dmMimeType);
3926 (*document)->dmMimeType = new_type;
3930 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3931 err = string2isds_FileMetaType((xmlChar*)string,
3932 &((*document)->dmFileMetaType));
3933 if (err) {
3934 char *meta_type_locale = _isds_utf82locale(string);
3935 isds_printf_message(context,
3936 _("Document has invalid dmFileMetaType attribute value: %s"),
3937 meta_type_locale);
3938 free(meta_type_locale);
3939 err = IE_ISDS;
3940 goto leave;
3942 zfree(string);
3944 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3945 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3946 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3947 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3950 /* Extract document data.
3951 * Base64 encoded blob or XML subtree must be presented. */
3953 /* Check for dmEncodedContent */
3954 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3955 xpath_ctx);
3956 if (!result) {
3957 err = IE_XML;
3958 goto leave;
3961 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3962 /* Here we have Base64 blob */
3963 (*document)->is_xml = 0;
3965 if (result->nodesetval->nodeNr > 1) {
3966 isds_printf_message(context,
3967 _("Document has more dmEncodedContent elements"));
3968 err = IE_ISDS;
3969 goto leave;
3972 xmlXPathFreeObject(result); result = NULL;
3973 EXTRACT_STRING("isds:dmEncodedContent", string);
3975 /* Decode non-empty document */
3976 if (string && string[0] != '\0') {
3977 (*document)->data_length =
3978 _isds_b64decode(string, &((*document)->data));
3979 if ((*document)->data_length == (size_t) -1) {
3980 isds_printf_message(context,
3981 _("Error while Base64-decoding document content"));
3982 err = IE_ERROR;
3983 goto leave;
3986 } else {
3987 /* No Base64 blob, try XML document */
3988 xmlXPathFreeObject(result); result = NULL;
3989 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3990 xpath_ctx);
3991 if (!result) {
3992 err = IE_XML;
3993 goto leave;
3996 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3997 /* Here we have XML document */
3998 (*document)->is_xml = 1;
4000 if (result->nodesetval->nodeNr > 1) {
4001 isds_printf_message(context,
4002 _("Document has more dmXMLContent elements"));
4003 err = IE_ISDS;
4004 goto leave;
4007 /* XXX: We cannot serialize the content simply because:
4008 * - XML document may point out of its scope (e.g. to message
4009 * envelope)
4010 * - isds:dmXMLContent can contain more elements, no element,
4011 * a text node only
4012 * - it's not the XML way
4013 * Thus we provide the only right solution: XML DOM. Let's
4014 * application to cope with this hot potato :) */
4015 (*document)->xml_node_list =
4016 result->nodesetval->nodeTab[0]->children;
4017 } else {
4018 /* No base64 blob, nor XML document */
4019 isds_printf_message(context,
4020 _("Document has no dmEncodedContent, nor dmXMLContent "
4021 "element"));
4022 err = IE_ISDS;
4023 goto leave;
4028 leave:
4029 if (err) isds_document_free(document);
4030 free(string);
4031 xmlXPathFreeObject(result);
4032 xpath_ctx->node = file_node;
4033 return err;
4038 /* Extract message documents into reallocated list of documents
4039 * @context is ISDS context
4040 * @documents is automatically reallocated message documents list structure
4041 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4042 * In case of error @documents will be freed. */
4043 static isds_error extract_documents(struct isds_ctx *context,
4044 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
4045 isds_error err = IE_SUCCESS;
4046 xmlXPathObjectPtr result = NULL;
4047 xmlNodePtr files_node;
4048 struct isds_list *document, *prev_document = NULL;
4050 if (!context) return IE_INVALID_CONTEXT;
4051 if (!documents) return IE_INVAL;
4052 isds_list_free(documents);
4053 if (!xpath_ctx) return IE_INVAL;
4054 files_node = xpath_ctx->node;
4056 /* Find documents */
4057 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
4058 if (!result) {
4059 err = IE_XML;
4060 goto leave;
4063 /* No match */
4064 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4065 isds_printf_message(context,
4066 _("Message does not contain any document"));
4067 err = IE_ISDS;
4068 goto leave;
4072 /* Iterate over documents */
4073 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4075 /* Allocate and append list item */
4076 document = calloc(1, sizeof(*document));
4077 if (!document) {
4078 err = IE_NOMEM;
4079 goto leave;
4081 document->destructor = (void (*)(void **))isds_document_free;
4082 if (i == 0) *documents = document;
4083 else prev_document->next = document;
4084 prev_document = document;
4086 /* Extract document */
4087 xpath_ctx->node = result->nodesetval->nodeTab[i];
4088 err = extract_document(context,
4089 (struct isds_document **) &(document->data), xpath_ctx);
4090 if (err) goto leave;
4094 leave:
4095 if (err) isds_list_free(documents);
4096 xmlXPathFreeObject(result);
4097 xpath_ctx->node = files_node;
4098 return err;
4102 #if HAVE_LIBCURL
4103 /* Convert isds:dmRecord XML tree into structure
4104 * @context is ISDS context
4105 * @envelope is automatically reallocated message envelope structure
4106 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4107 * In case of error @envelope will be freed. */
4108 static isds_error extract_DmRecord(struct isds_ctx *context,
4109 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4110 isds_error err = IE_SUCCESS;
4111 xmlXPathObjectPtr result = NULL;
4113 if (!context) return IE_INVALID_CONTEXT;
4114 if (!envelope) return IE_INVAL;
4115 isds_envelope_free(envelope);
4116 if (!xpath_ctx) return IE_INVAL;
4119 *envelope = calloc(1, sizeof(**envelope));
4120 if (!*envelope) {
4121 err = IE_NOMEM;
4122 goto leave;
4126 /* Extract tRecord data */
4127 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4129 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4130 * dmAcceptanceTime. */
4131 err = append_status_size_times(context, envelope, xpath_ctx);
4132 if (err) goto leave;
4134 /* Extract envelope elements added by sender and ISDS
4135 * (XSD: gMessageEnvelope type) */
4136 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4137 if (err) goto leave;
4139 /* Get message type */
4140 err = append_message_type(context, envelope, xpath_ctx);
4141 if (err) goto leave;
4144 leave:
4145 if (err) isds_envelope_free(envelope);
4146 xmlXPathFreeObject(result);
4147 return err;
4151 /* Convert XSD:tStateChangesRecord type XML tree into structure
4152 * @context is ISDS context
4153 * @changed_status is automatically reallocated message state change structure
4154 * @xpath_ctx is XPath context with current node as element of
4155 * XSD:tStateChangesRecord type
4156 * In case of error @changed_status will be freed. */
4157 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4158 struct isds_message_status_change **changed_status,
4159 xmlXPathContextPtr xpath_ctx) {
4160 isds_error err = IE_SUCCESS;
4161 xmlXPathObjectPtr result = NULL;
4162 unsigned long int *unumber = NULL;
4163 char *string = NULL;
4165 if (!context) return IE_INVALID_CONTEXT;
4166 if (!changed_status) return IE_INVAL;
4167 isds_message_status_change_free(changed_status);
4168 if (!xpath_ctx) return IE_INVAL;
4171 *changed_status = calloc(1, sizeof(**changed_status));
4172 if (!*changed_status) {
4173 err = IE_NOMEM;
4174 goto leave;
4178 /* Extract tGetStateChangesInput data */
4179 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4181 /* dmEventTime is mandatory */
4182 EXTRACT_STRING("isds:dmEventTime", string);
4183 if (string) {
4184 err = timestring2timeval((xmlChar *) string,
4185 &((*changed_status)->time));
4186 if (err) {
4187 char *string_locale = _isds_utf82locale(string);
4188 if (err == IE_DATE) err = IE_ISDS;
4189 isds_printf_message(context,
4190 _("Could not convert dmEventTime as ISO time: %s"),
4191 string_locale);
4192 free(string_locale);
4193 goto leave;
4195 zfree(string);
4198 /* dmMessageStatus element is mandatory */
4199 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4200 if (!unumber) {
4201 isds_log_message(context,
4202 _("Missing mandatory isds:dmMessageStatus integer"));
4203 err = IE_ISDS;
4204 goto leave;
4206 err = uint2isds_message_status(context, unumber,
4207 &((*changed_status)->dmMessageStatus));
4208 if (err) {
4209 if (err == IE_ENUM) err = IE_ISDS;
4210 goto leave;
4212 zfree(unumber);
4215 leave:
4216 free(unumber);
4217 free(string);
4218 if (err) isds_message_status_change_free(changed_status);
4219 xmlXPathFreeObject(result);
4220 return err;
4222 #endif /* HAVE_LIBCURL */
4225 /* Find and convert isds:dmHash XML tree into structure
4226 * @context is ISDS context
4227 * @envelope is automatically reallocated message hash structure
4228 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4229 * In case of error @hash will be freed. */
4230 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4231 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4232 isds_error err = IE_SUCCESS;
4233 xmlNodePtr old_ctx_node;
4234 xmlXPathObjectPtr result = NULL;
4235 char *string = NULL;
4237 if (!context) return IE_INVALID_CONTEXT;
4238 if (!hash) return IE_INVAL;
4239 isds_hash_free(hash);
4240 if (!xpath_ctx) return IE_INVAL;
4242 old_ctx_node = xpath_ctx->node;
4244 *hash = calloc(1, sizeof(**hash));
4245 if (!*hash) {
4246 err = IE_NOMEM;
4247 goto leave;
4250 /* Locate dmHash */
4251 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4252 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4253 err = IE_ISDS;
4254 goto leave;
4256 if (err) {
4257 err = IE_ERROR;
4258 goto leave;
4261 /* Get hash algorithm */
4262 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4263 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4264 if (err) {
4265 if (err == IE_ENUM) {
4266 char *string_locale = _isds_utf82locale(string);
4267 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4268 string_locale);
4269 free(string_locale);
4271 goto leave;
4273 zfree(string);
4275 /* Get hash value */
4276 EXTRACT_STRING(".", string);
4277 if (!string) {
4278 isds_printf_message(context,
4279 _("sisds:dmHash element is missing hash value"));
4280 err = IE_ISDS;
4281 goto leave;
4283 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4284 if ((*hash)->length == (size_t) -1) {
4285 isds_printf_message(context,
4286 _("Error while Base64-decoding hash value"));
4287 err = IE_ERROR;
4288 goto leave;
4291 leave:
4292 if (err) isds_hash_free(hash);
4293 free(string);
4294 xmlXPathFreeObject(result);
4295 xpath_ctx->node = old_ctx_node;
4296 return err;
4300 /* Find and append isds:dmQTimestamp XML tree into envelope.
4301 * Because one service is allowed to miss time-stamp content, and we think
4302 * other could too (flaw in specification), this function is deliberated and
4303 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4304 * @context is ISDS context
4305 * @envelope is automatically allocated envelope structure
4306 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4307 * child
4308 * In case of error @envelope will be freed. */
4309 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4310 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4311 isds_error err = IE_SUCCESS;
4312 xmlXPathObjectPtr result = NULL;
4313 char *string = NULL;
4315 if (!context) return IE_INVALID_CONTEXT;
4316 if (!envelope) return IE_INVAL;
4317 if (!xpath_ctx) {
4318 isds_envelope_free(envelope);
4319 return IE_INVAL;
4322 if (!*envelope) {
4323 *envelope = calloc(1, sizeof(**envelope));
4324 if (!*envelope) {
4325 err = IE_NOMEM;
4326 goto leave;
4328 } else {
4329 zfree((*envelope)->timestamp);
4330 (*envelope)->timestamp_length = 0;
4333 /* Get dmQTimestamp */
4334 EXTRACT_STRING("sisds:dmQTimestamp", string);
4335 if (!string) {
4336 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4337 goto leave;
4339 (*envelope)->timestamp_length =
4340 _isds_b64decode(string, &((*envelope)->timestamp));
4341 if ((*envelope)->timestamp_length == (size_t) -1) {
4342 isds_printf_message(context,
4343 _("Error while Base64-decoding time stamp value"));
4344 err = IE_ERROR;
4345 goto leave;
4348 leave:
4349 if (err) isds_envelope_free(envelope);
4350 free(string);
4351 xmlXPathFreeObject(result);
4352 return err;
4356 /* Convert XSD tReturnedMessage XML tree into message structure.
4357 * It does not store serialized XML tree into message->raw.
4358 * It does store (pointer to) parsed XML tree into message->xml if needed.
4359 * @context is ISDS context
4360 * @include_documents Use true if documents must be extracted
4361 * (tReturnedMessage XSD type), use false if documents shall be omitted
4362 * (tReturnedMessageEnvelope).
4363 * @message is automatically reallocated message structure
4364 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4365 * type
4366 * In case of error @message will be freed. */
4367 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4368 const _Bool include_documents, struct isds_message **message,
4369 xmlXPathContextPtr xpath_ctx) {
4370 isds_error err = IE_SUCCESS;
4371 xmlNodePtr message_node;
4373 if (!context) return IE_INVALID_CONTEXT;
4374 if (!message) return IE_INVAL;
4375 isds_message_free(message);
4376 if (!xpath_ctx) return IE_INVAL;
4379 *message = calloc(1, sizeof(**message));
4380 if (!*message) {
4381 err = IE_NOMEM;
4382 goto leave;
4385 /* Save message XPATH context node */
4386 message_node = xpath_ctx->node;
4389 /* Extract dmDM */
4390 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4391 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4392 if (err) { err = IE_ERROR; goto leave; }
4393 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4394 if (err) goto leave;
4396 if (include_documents) {
4397 struct isds_list *item;
4399 /* Extract dmFiles */
4400 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4401 xpath_ctx);
4402 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4403 err = IE_ISDS; goto leave;
4405 if (err) { err = IE_ERROR; goto leave; }
4406 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4407 if (err) goto leave;
4409 /* Store xmlDoc of this message if needed */
4410 /* Only if we got a XML document in all the documents. */
4411 for (item = (*message)->documents; item; item = item->next) {
4412 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4413 (*message)->xml = xpath_ctx->doc;
4414 break;
4420 /* Restore context to message */
4421 xpath_ctx->node = message_node;
4423 /* Extract dmHash */
4424 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4425 xpath_ctx);
4426 if (err) goto leave;
4428 /* Extract dmQTimestamp, */
4429 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4430 xpath_ctx);
4431 if (err) goto leave;
4433 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4434 * dmAcceptanceTime. */
4435 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4436 if (err) goto leave;
4438 /* Get message type */
4439 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4440 if (err) goto leave;
4442 leave:
4443 if (err) isds_message_free(message);
4444 return err;
4448 /* Extract message event into reallocated isds_event structure
4449 * @context is ISDS context
4450 * @event is automatically reallocated message event structure
4451 * @xpath_ctx is XPath context with current node as isds:dmEvent
4452 * In case of error @event will be freed. */
4453 static isds_error extract_event(struct isds_ctx *context,
4454 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4455 isds_error err = IE_SUCCESS;
4456 xmlXPathObjectPtr result = NULL;
4457 xmlNodePtr event_node;
4458 char *string = NULL;
4460 if (!context) return IE_INVALID_CONTEXT;
4461 if (!event) return IE_INVAL;
4462 isds_event_free(event);
4463 if (!xpath_ctx) return IE_INVAL;
4464 event_node = xpath_ctx->node;
4466 *event = calloc(1, sizeof(**event));
4467 if (!*event) {
4468 err = IE_NOMEM;
4469 goto leave;
4472 /* Extract event data.
4473 * All elements are optional according XSD. That's funny. */
4474 EXTRACT_STRING("sisds:dmEventTime", string);
4475 if (string) {
4476 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4477 if (err) {
4478 char *string_locale = _isds_utf82locale(string);
4479 if (err == IE_DATE) err = IE_ISDS;
4480 isds_printf_message(context,
4481 _("Could not convert dmEventTime as ISO time: %s"),
4482 string_locale);
4483 free(string_locale);
4484 goto leave;
4486 zfree(string);
4489 /* dmEventDescr element has prefix and the rest */
4490 EXTRACT_STRING("sisds:dmEventDescr", string);
4491 if (string) {
4492 err = eventstring2event((xmlChar *) string, *event);
4493 if (err) goto leave;
4494 zfree(string);
4497 leave:
4498 if (err) isds_event_free(event);
4499 free(string);
4500 xmlXPathFreeObject(result);
4501 xpath_ctx->node = event_node;
4502 return err;
4506 /* Convert element of XSD tEventsArray type from XML tree into
4507 * isds_list of isds_event's structure. The list is automatically reallocated.
4508 * @context is ISDS context
4509 * @events is automatically reallocated list of event structures
4510 * @xpath_ctx is XPath context with current node as tEventsArray
4511 * In case of error @events will be freed. */
4512 static isds_error extract_events(struct isds_ctx *context,
4513 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4514 isds_error err = IE_SUCCESS;
4515 xmlXPathObjectPtr result = NULL;
4516 xmlNodePtr events_node;
4517 struct isds_list *event, *prev_event = NULL;
4519 if (!context) return IE_INVALID_CONTEXT;
4520 if (!events) return IE_INVAL;
4521 if (!xpath_ctx) return IE_INVAL;
4522 events_node = xpath_ctx->node;
4524 /* Free old list */
4525 isds_list_free(events);
4527 /* Find events */
4528 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4529 if (!result) {
4530 err = IE_XML;
4531 goto leave;
4534 /* No match */
4535 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4536 isds_printf_message(context,
4537 _("Delivery info does not contain any event"));
4538 err = IE_ISDS;
4539 goto leave;
4543 /* Iterate over events */
4544 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4546 /* Allocate and append list item */
4547 event = calloc(1, sizeof(*event));
4548 if (!event) {
4549 err = IE_NOMEM;
4550 goto leave;
4552 event->destructor = (void (*)(void **))isds_event_free;
4553 if (i == 0) *events = event;
4554 else prev_event->next = event;
4555 prev_event = event;
4557 /* Extract event */
4558 xpath_ctx->node = result->nodesetval->nodeTab[i];
4559 err = extract_event(context,
4560 (struct isds_event **) &(event->data), xpath_ctx);
4561 if (err) goto leave;
4565 leave:
4566 if (err) isds_list_free(events);
4567 xmlXPathFreeObject(result);
4568 xpath_ctx->node = events_node;
4569 return err;
4573 #if HAVE_LIBCURL
4574 /* Insert Base64 encoded data as element with text child.
4575 * @context is session context
4576 * @parent is XML node to append @element with @data as child
4577 * @ns is XML namespace of @element, use NULL to inherit from @parent
4578 * @element is UTF-8 encoded name of new element
4579 * @data is bit stream to encode into @element
4580 * @length is size of @data in bytes
4581 * @return standard error code and fill long error message if needed */
4582 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4583 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4584 const void *data, size_t length) {
4585 isds_error err = IE_SUCCESS;
4586 xmlNodePtr node;
4588 if (!context) return IE_INVALID_CONTEXT;
4589 if (!data && length > 0) return IE_INVAL;
4590 if (!parent || !element) return IE_INVAL;
4592 xmlChar *base64data = NULL;
4593 base64data = (xmlChar *) _isds_b64encode(data, length);
4594 if (!base64data) {
4595 isds_printf_message(context,
4596 ngettext("Not enough memory to encode %zd byte into Base64",
4597 "Not enough memory to encode %zd bytes into Base64",
4598 length),
4599 length);
4600 err = IE_NOMEM;
4601 goto leave;
4603 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4605 leave:
4606 free(base64data);
4607 return err;
4611 /* Convert isds_document structure into XML tree and append to dmFiles node.
4612 * @context is session context
4613 * @document is ISDS document
4614 * @dm_files is XML element the resulting tree will be appended to as a child.
4615 * @return error code, in case of error context' message is filled. */
4616 static isds_error insert_document(struct isds_ctx *context,
4617 struct isds_document *document, xmlNodePtr dm_files) {
4618 isds_error err = IE_SUCCESS;
4619 xmlNodePtr new_file = NULL, file = NULL, node;
4620 xmlAttrPtr attribute_node;
4622 if (!context) return IE_INVALID_CONTEXT;
4623 if (!document || !dm_files) return IE_INVAL;
4625 /* Allocate new dmFile */
4626 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4627 if (!new_file) {
4628 isds_printf_message(context, _("Could not allocate main dmFile"));
4629 err = IE_ERROR;
4630 goto leave;
4632 /* Append the new dmFile.
4633 * XXX: Main document must go first */
4634 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4635 file = xmlAddPrevSibling(dm_files->children, new_file);
4636 else
4637 file = xmlAddChild(dm_files, new_file);
4639 if (!file) {
4640 xmlFreeNode(new_file); new_file = NULL;
4641 isds_printf_message(context, _("Could not add dmFile child to "
4642 "%s element"), dm_files->name);
4643 err = IE_ERROR;
4644 goto leave;
4647 /* @dmMimeType is required */
4648 if (!document->dmMimeType) {
4649 isds_log_message(context,
4650 _("Document is missing mandatory MIME type definition"));
4651 err = IE_INVAL;
4652 goto leave;
4654 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4656 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4657 if (!string) {
4658 isds_printf_message(context,
4659 _("Document has unknown dmFileMetaType: %ld"),
4660 document->dmFileMetaType);
4661 err = IE_ENUM;
4662 goto leave;
4664 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4666 if (document->dmFileGuid) {
4667 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4669 if (document->dmUpFileGuid) {
4670 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4673 /* @dmFileDescr is required */
4674 if (!document->dmFileDescr) {
4675 isds_log_message(context,
4676 _("Document is missing mandatory description (title)"));
4677 err = IE_INVAL;
4678 goto leave;
4680 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4682 if (document->dmFormat) {
4683 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4687 /* Insert content (body) of the document. */
4688 if (document->is_xml) {
4689 /* XML document requested */
4691 /* Allocate new dmXMLContent */
4692 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4693 if (!xmlcontent) {
4694 isds_printf_message(context,
4695 _("Could not allocate dmXMLContent element"));
4696 err = IE_ERROR;
4697 goto leave;
4699 /* Append it */
4700 node = xmlAddChild(file, xmlcontent);
4701 if (!node) {
4702 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4703 isds_printf_message(context,
4704 _("Could not add dmXMLContent child to %s element"),
4705 file->name);
4706 err = IE_ERROR;
4707 goto leave;
4710 /* Copy non-empty node list */
4711 if (document->xml_node_list) {
4712 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4713 document->xml_node_list);
4714 if (!content) {
4715 isds_printf_message(context,
4716 _("Not enough memory to copy XML document"));
4717 err = IE_NOMEM;
4718 goto leave;
4721 if (!xmlAddChildList(node, content)) {
4722 xmlFreeNodeList(content);
4723 isds_printf_message(context,
4724 _("Error while adding XML document into dmXMLContent"));
4725 err = IE_XML;
4726 goto leave;
4728 /* XXX: We cannot free the content here because it's part of node's
4729 * document since now. It will be freed with it automatically. */
4731 } else {
4732 /* Binary document requested */
4733 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4734 document->data, document->data_length);
4735 if (err) goto leave;
4738 leave:
4739 return err;
4743 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4744 * The copy must be preallocated, the date are just appended into structure.
4745 * @context is ISDS context
4746 * @copy is message copy structure
4747 * @xpath_ctx is XPath context with current node as tMStatus */
4748 static isds_error append_TMStatus(struct isds_ctx *context,
4749 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4750 isds_error err = IE_SUCCESS;
4751 xmlXPathObjectPtr result = NULL;
4752 char *code = NULL, *message = NULL;
4754 if (!context) return IE_INVALID_CONTEXT;
4755 if (!copy || !xpath_ctx) return IE_INVAL;
4757 /* Free old values */
4758 zfree(copy->dmStatus);
4759 zfree(copy->dmID);
4761 /* Get error specific to this copy */
4762 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4763 if (!code) {
4764 isds_log_message(context,
4765 _("Missing isds:dmStatusCode under "
4766 "XSD:tMStatus type element"));
4767 err = IE_ISDS;
4768 goto leave;
4771 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4772 /* This copy failed */
4773 copy->error = IE_ISDS;
4774 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4775 if (message) {
4776 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4777 if (!copy->dmStatus) {
4778 copy->dmStatus = code;
4779 code = NULL;
4781 } else {
4782 copy->dmStatus = code;
4783 code = NULL;
4785 } else {
4786 /* This copy succeeded. In this case only, message ID is valid */
4787 copy->error = IE_SUCCESS;
4789 EXTRACT_STRING("isds:dmID", copy->dmID);
4790 if (!copy->dmID) {
4791 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4792 "but did not returned assigned message ID\n"));
4793 err = IE_ISDS;
4797 leave:
4798 free(code);
4799 free(message);
4800 xmlXPathFreeObject(result);
4801 return err;
4805 /* Insert struct isds_approval data (box approval) into XML tree
4806 * @context is session context
4807 * @approval is libisds structure with approval description. NULL is
4808 * acceptable.
4809 * @parent is XML element to append @approval to */
4810 static isds_error insert_GExtApproval(struct isds_ctx *context,
4811 const struct isds_approval *approval, xmlNodePtr parent) {
4813 isds_error err = IE_SUCCESS;
4814 xmlNodePtr node;
4816 if (!context) return IE_INVALID_CONTEXT;
4817 if (!parent) return IE_INVAL;
4819 if (!approval) return IE_SUCCESS;
4821 /* Build XSD:gExtApproval */
4822 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4823 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4825 leave:
4826 return err;
4830 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4831 * code
4832 * @context is session context
4833 * @service_name is name of SERVICE_DB_ACCESS
4834 * @response is reallocated server SOAP body response as XML document
4835 * @raw_response is reallocated bit stream with response body. Use
4836 * NULL if you don't care
4837 * @raw_response_length is size of @raw_response in bytes
4838 * @code is reallocated ISDS status code
4839 * @status_message is reallocated ISDS status message
4840 * @return error coded from lower layer, context message will be set up
4841 * appropriately. */
4842 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4843 const xmlChar *service_name,
4844 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4845 xmlChar **code, xmlChar **status_message) {
4847 isds_error err = IE_SUCCESS;
4848 char *service_name_locale = NULL;
4849 xmlNodePtr request = NULL, node;
4850 xmlNsPtr isds_ns = NULL;
4852 if (!context) return IE_INVALID_CONTEXT;
4853 if (!service_name) return IE_INVAL;
4854 if (!response || !code || !status_message) return IE_INVAL;
4855 if (!raw_response_length && raw_response) return IE_INVAL;
4857 /* Free output argument */
4858 xmlFreeDoc(*response); *response = NULL;
4859 if (raw_response) zfree(*raw_response);
4860 zfree(*code);
4861 zfree(*status_message);
4864 /* Check if connection is established
4865 * TODO: This check should be done downstairs. */
4866 if (!context->curl) return IE_CONNECTION_CLOSED;
4868 service_name_locale = _isds_utf82locale((char*)service_name);
4869 if (!service_name_locale) {
4870 err = IE_NOMEM;
4871 goto leave;
4874 /* Build request */
4875 request = xmlNewNode(NULL, service_name);
4876 if (!request) {
4877 isds_printf_message(context,
4878 _("Could not build %s request"), service_name_locale);
4879 err = IE_ERROR;
4880 goto leave;
4882 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4883 if(!isds_ns) {
4884 isds_log_message(context, _("Could not create ISDS name space"));
4885 err = IE_ERROR;
4886 goto leave;
4888 xmlSetNs(request, isds_ns);
4891 /* Add XSD:tDummyInput child */
4892 INSERT_STRING(request, "dbDummy", NULL);
4895 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4896 service_name_locale);
4898 /* Send request */
4899 err = _isds(context, SERVICE_DB_ACCESS, request, response,
4900 raw_response, raw_response_length);
4901 xmlFreeNode(request); request = NULL;
4903 if (err) {
4904 isds_log(ILF_ISDS, ILL_DEBUG,
4905 _("Processing ISDS response on %s request failed\n"),
4906 service_name_locale);
4907 goto leave;
4910 /* Check for response status */
4911 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4912 code, status_message, NULL);
4913 if (err) {
4914 isds_log(ILF_ISDS, ILL_DEBUG,
4915 _("ISDS response on %s request is missing status\n"),
4916 service_name_locale);
4917 goto leave;
4920 /* Request processed, but nothing found */
4921 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4922 char *code_locale = _isds_utf82locale((char*) *code);
4923 char *status_message_locale =
4924 _isds_utf82locale((char*) *status_message);
4925 isds_log(ILF_ISDS, ILL_DEBUG,
4926 _("Server refused %s request (code=%s, message=%s)\n"),
4927 service_name_locale, code_locale, status_message_locale);
4928 isds_log_message(context, status_message_locale);
4929 free(code_locale);
4930 free(status_message_locale);
4931 err = IE_ISDS;
4932 goto leave;
4935 leave:
4936 free(service_name_locale);
4937 xmlFreeNode(request);
4938 return err;
4940 #endif
4943 /* Get data about logged in user and his box. */
4944 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4945 struct isds_DbOwnerInfo **db_owner_info) {
4946 isds_error err = IE_SUCCESS;
4947 #if HAVE_LIBCURL
4948 xmlDocPtr response = NULL;
4949 xmlChar *code = NULL, *message = NULL;
4950 xmlXPathContextPtr xpath_ctx = NULL;
4951 xmlXPathObjectPtr result = NULL;
4952 char *string = NULL;
4953 #endif
4955 if (!context) return IE_INVALID_CONTEXT;
4956 zfree(context->long_message);
4957 if (!db_owner_info) return IE_INVAL;
4958 isds_DbOwnerInfo_free(db_owner_info);
4960 #if HAVE_LIBCURL
4961 /* Check if connection is established */
4962 if (!context->curl) return IE_CONNECTION_CLOSED;
4965 /* Do request and check for success */
4966 err = build_send_check_dbdummy_request(context,
4967 BAD_CAST "GetOwnerInfoFromLogin",
4968 &response, NULL, NULL, &code, &message);
4969 if (err) goto leave;
4972 /* Extract data */
4973 /* Prepare structure */
4974 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4975 if (!*db_owner_info) {
4976 err = IE_NOMEM;
4977 goto leave;
4979 xpath_ctx = xmlXPathNewContext(response);
4980 if (!xpath_ctx) {
4981 err = IE_ERROR;
4982 goto leave;
4984 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4985 err = IE_ERROR;
4986 goto leave;
4989 /* Set context node */
4990 result = xmlXPathEvalExpression(BAD_CAST
4991 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4992 if (!result) {
4993 err = IE_ERROR;
4994 goto leave;
4996 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4997 isds_log_message(context, _("Missing dbOwnerInfo element"));
4998 err = IE_ISDS;
4999 goto leave;
5001 if (result->nodesetval->nodeNr > 1) {
5002 isds_log_message(context, _("Multiple dbOwnerInfo element"));
5003 err = IE_ISDS;
5004 goto leave;
5006 xpath_ctx->node = result->nodesetval->nodeTab[0];
5007 xmlXPathFreeObject(result); result = NULL;
5009 /* Extract it */
5010 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
5013 leave:
5014 if (err) {
5015 isds_DbOwnerInfo_free(db_owner_info);
5018 free(string);
5019 xmlXPathFreeObject(result);
5020 xmlXPathFreeContext(xpath_ctx);
5022 free(code);
5023 free(message);
5024 xmlFreeDoc(response);
5026 if (!err)
5027 isds_log(ILF_ISDS, ILL_DEBUG,
5028 _("GetOwnerInfoFromLogin request processed by server "
5029 "successfully.\n"));
5030 #else /* not HAVE_LIBCURL */
5031 err = IE_NOTSUP;
5032 #endif
5034 return err;
5038 /* Get data about logged in user. */
5039 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
5040 struct isds_DbUserInfo **db_user_info) {
5041 isds_error err = IE_SUCCESS;
5042 #if HAVE_LIBCURL
5043 xmlDocPtr response = NULL;
5044 xmlChar *code = NULL, *message = NULL;
5045 xmlXPathContextPtr xpath_ctx = NULL;
5046 xmlXPathObjectPtr result = NULL;
5047 #endif
5049 if (!context) return IE_INVALID_CONTEXT;
5050 zfree(context->long_message);
5051 if (!db_user_info) return IE_INVAL;
5052 isds_DbUserInfo_free(db_user_info);
5054 #if HAVE_LIBCURL
5055 /* Check if connection is established */
5056 if (!context->curl) return IE_CONNECTION_CLOSED;
5059 /* Do request and check for success */
5060 err = build_send_check_dbdummy_request(context,
5061 BAD_CAST "GetUserInfoFromLogin",
5062 &response, NULL, NULL, &code, &message);
5063 if (err) goto leave;
5066 /* Extract data */
5067 /* Prepare structure */
5068 *db_user_info = calloc(1, sizeof(**db_user_info));
5069 if (!*db_user_info) {
5070 err = IE_NOMEM;
5071 goto leave;
5073 xpath_ctx = xmlXPathNewContext(response);
5074 if (!xpath_ctx) {
5075 err = IE_ERROR;
5076 goto leave;
5078 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5079 err = IE_ERROR;
5080 goto leave;
5083 /* Set context node */
5084 result = xmlXPathEvalExpression(BAD_CAST
5085 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
5086 if (!result) {
5087 err = IE_ERROR;
5088 goto leave;
5090 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5091 isds_log_message(context, _("Missing dbUserInfo element"));
5092 err = IE_ISDS;
5093 goto leave;
5095 if (result->nodesetval->nodeNr > 1) {
5096 isds_log_message(context, _("Multiple dbUserInfo element"));
5097 err = IE_ISDS;
5098 goto leave;
5100 xpath_ctx->node = result->nodesetval->nodeTab[0];
5101 xmlXPathFreeObject(result); result = NULL;
5103 /* Extract it */
5104 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5106 leave:
5107 if (err) {
5108 isds_DbUserInfo_free(db_user_info);
5111 xmlXPathFreeObject(result);
5112 xmlXPathFreeContext(xpath_ctx);
5114 free(code);
5115 free(message);
5116 xmlFreeDoc(response);
5118 if (!err)
5119 isds_log(ILF_ISDS, ILL_DEBUG,
5120 _("GetUserInfoFromLogin request processed by server "
5121 "successfully.\n"));
5122 #else /* not HAVE_LIBCURL */
5123 err = IE_NOTSUP;
5124 #endif
5126 return err;
5130 /* Get expiration time of current password
5131 * @context is session context
5132 * @expiration is automatically reallocated time when password expires. If
5133 * password expiration is disabled, NULL will be returned. In case of error
5134 * it will be nulled too. */
5135 isds_error isds_get_password_expiration(struct isds_ctx *context,
5136 struct timeval **expiration) {
5137 isds_error err = IE_SUCCESS;
5138 #if HAVE_LIBCURL
5139 xmlDocPtr response = NULL;
5140 xmlChar *code = NULL, *message = NULL;
5141 xmlXPathContextPtr xpath_ctx = NULL;
5142 xmlXPathObjectPtr result = NULL;
5143 char *string = NULL;
5144 #endif
5146 if (!context) return IE_INVALID_CONTEXT;
5147 zfree(context->long_message);
5148 if (!expiration) return IE_INVAL;
5149 zfree(*expiration);
5151 #if HAVE_LIBCURL
5152 /* Check if connection is established */
5153 if (!context->curl) return IE_CONNECTION_CLOSED;
5156 /* Do request and check for success */
5157 err = build_send_check_dbdummy_request(context,
5158 BAD_CAST "GetPasswordInfo",
5159 &response, NULL, NULL, &code, &message);
5160 if (err) goto leave;
5163 /* Extract data */
5164 xpath_ctx = xmlXPathNewContext(response);
5165 if (!xpath_ctx) {
5166 err = IE_ERROR;
5167 goto leave;
5169 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5170 err = IE_ERROR;
5171 goto leave;
5174 /* Set context node */
5175 result = xmlXPathEvalExpression(BAD_CAST
5176 "/isds:GetPasswordInfoResponse", xpath_ctx);
5177 if (!result) {
5178 err = IE_ERROR;
5179 goto leave;
5181 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5182 isds_log_message(context,
5183 _("Missing GetPasswordInfoResponse element"));
5184 err = IE_ISDS;
5185 goto leave;
5187 if (result->nodesetval->nodeNr > 1) {
5188 isds_log_message(context,
5189 _("Multiple GetPasswordInfoResponse element"));
5190 err = IE_ISDS;
5191 goto leave;
5193 xpath_ctx->node = result->nodesetval->nodeTab[0];
5194 xmlXPathFreeObject(result); result = NULL;
5196 /* Extract expiration date */
5197 EXTRACT_STRING("isds:pswExpDate", string);
5198 if (string) {
5199 /* And convert it if any returned. Otherwise expiration is disabled. */
5200 err = timestring2timeval((xmlChar *) string, expiration);
5201 if (err) {
5202 char *string_locale = _isds_utf82locale(string);
5203 if (err == IE_DATE) err = IE_ISDS;
5204 isds_printf_message(context,
5205 _("Could not convert pswExpDate as ISO time: %s"),
5206 string_locale);
5207 free(string_locale);
5208 goto leave;
5212 leave:
5213 if (err) {
5214 if (*expiration) {
5215 zfree(*expiration);
5219 free(string);
5220 xmlXPathFreeObject(result);
5221 xmlXPathFreeContext(xpath_ctx);
5223 free(code);
5224 free(message);
5225 xmlFreeDoc(response);
5227 if (!err)
5228 isds_log(ILF_ISDS, ILL_DEBUG,
5229 _("GetPasswordInfo request processed by server "
5230 "successfully.\n"));
5231 #else /* not HAVE_LIBCURL */
5232 err = IE_NOTSUP;
5233 #endif
5235 return err;
5239 #if HAVE_LIBCURL
5240 /* Request delivering new TOTP code from ISDS through side channel before
5241 * changing password.
5242 * @context is session context
5243 * @password is current password.
5244 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5245 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5246 * function for more details.
5247 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5248 * NULL, if you don't care.
5249 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5250 * error code. */
5251 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5252 const char *password, struct isds_otp *otp, char **refnumber) {
5253 isds_error err = IE_SUCCESS;
5254 char *saved_url = NULL; /* No copy */
5255 #if HAVE_CURL_REAUTHORIZATION_BUG
5256 CURL *saved_curl = NULL; /* No copy */
5257 #endif
5258 xmlNsPtr isds_ns = NULL;
5259 xmlNodePtr request = NULL;
5260 xmlDocPtr response = NULL;
5261 xmlChar *code = NULL, *message = NULL;
5262 const xmlChar *codes[] = {
5263 BAD_CAST "2300",
5264 BAD_CAST "2301",
5265 BAD_CAST "2302"
5267 const char *meanings[] = {
5268 N_("Unexpected error"),
5269 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5270 N_("One-time code could not been sent. Try later again.")
5272 const isds_otp_resolution resolutions[] = {
5273 OTP_RESOLUTION_UNKNOWN,
5274 OTP_RESOLUTION_TO_FAST,
5275 OTP_RESOLUTION_TOTP_NOT_SENT
5278 if (NULL == context) return IE_INVALID_CONTEXT;
5279 zfree(context->long_message);
5280 if (NULL == password) {
5281 isds_log_message(context,
5282 _("Second argument (password) of isds_change_password() "
5283 "is NULL"));
5284 return IE_INVAL;
5287 /* Check if connection is established
5288 * TODO: This check should be done downstairs. */
5289 if (!context->curl) return IE_CONNECTION_CLOSED;
5291 if (!context->otp) {
5292 isds_log_message(context, _("This function requires OTP-authenticated "
5293 "context"));
5294 return IE_INVALID_CONTEXT;
5296 if (NULL == otp) {
5297 isds_log_message(context, _("If one-time password authentication "
5298 "method is in use, requesting new OTP code requires "
5299 "one-time credentials argument either"));
5300 return IE_INVAL;
5302 if (otp->method != OTP_TIME) {
5303 isds_log_message(context, _("Requesting new time-based OTP code from "
5304 "server requires one-time password authentication "
5305 "method"));
5306 return IE_INVAL;
5308 if (otp->otp_code != NULL) {
5309 isds_log_message(context, _("Requesting new time-based OTP code from "
5310 "server requires undefined OTP code member in "
5311 "one-time credentials argument"));
5312 return IE_INVAL;
5316 /* Build request */
5317 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5318 if (!request) {
5319 isds_log_message(context, _("Could not build SendSMSCode request"));
5320 return IE_ERROR;
5322 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5323 if(!isds_ns) {
5324 isds_log_message(context, _("Could not create ISDS name space"));
5325 xmlFreeNode(request);
5326 return IE_ERROR;
5328 xmlSetNs(request, isds_ns);
5330 /* Change URL temporarily for sending this request only */
5332 char *new_url = NULL;
5333 if ((err = _isds_build_url_from_context(context,
5334 "%1$.*2$sasws/changePassword", &new_url))) {
5335 goto leave;
5337 saved_url = context->url;
5338 context->url = new_url;
5341 /* Store credentials for sending this request only */
5342 context->otp_credentials = otp;
5343 _isds_discard_credentials(context, 0);
5344 if ((err = _isds_store_credentials(context, context->saved_username,
5345 password, NULL))) {
5346 _isds_discard_credentials(context, 0);
5347 goto leave;
5349 #if HAVE_CURL_REAUTHORIZATION_BUG
5350 saved_curl = context->curl;
5351 context->curl = curl_easy_init();
5352 if (NULL == context->curl) {
5353 err = IE_ERROR;
5354 goto leave;
5356 if (context->timeout) {
5357 err = isds_set_timeout(context, context->timeout);
5358 if (err) goto leave;
5360 #endif
5362 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5364 /* Sent request */
5365 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5367 /* Remove temporal credentials */
5368 _isds_discard_credentials(context, 0);
5369 /* Detach pointer to OTP credentials from context */
5370 context->otp_credentials = NULL;
5371 /* Keep context->otp true to keep signaling this is OTP session */
5373 /* Destroy request */
5374 xmlFreeNode(request); request = NULL;
5376 if (err) {
5377 isds_log(ILF_ISDS, ILL_DEBUG,
5378 _("Processing ISDS response on SendSMSCode request failed\n"));
5379 goto leave;
5382 /* Check for response status */
5383 err = isds_response_status(context, SERVICE_ASWS, response,
5384 &code, &message, (xmlChar **)refnumber);
5385 if (err) {
5386 isds_log(ILF_ISDS, ILL_DEBUG,
5387 _("ISDS response on SendSMSCode request is missing "
5388 "status\n"));
5389 goto leave;
5392 /* Check for error */
5393 if (xmlStrcmp(code, BAD_CAST "0000")) {
5394 char *code_locale = _isds_utf82locale((char*)code);
5395 char *message_locale = _isds_utf82locale((char*)message);
5396 size_t i;
5397 isds_log(ILF_ISDS, ILL_DEBUG,
5398 _("Server refused to send new code on SendSMSCode "
5399 "request (code=%s, message=%s)\n"),
5400 code_locale, message_locale);
5402 /* Check for known error codes */
5403 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5404 if (!xmlStrcmp(code, codes[i])) break;
5406 if (i < sizeof(codes)/sizeof(*codes)) {
5407 isds_log_message(context, _(meanings[i]));
5408 /* Mimic otp->resolution according to the code, specification does
5409 * prescribe OTP header to be available. */
5410 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5411 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5412 otp->resolution = resolutions[i];
5413 } else
5414 isds_log_message(context, message_locale);
5416 free(code_locale);
5417 free(message_locale);
5419 err = IE_ISDS;
5420 goto leave;
5423 /* Otherwise new code sent successfully */
5424 /* Mimic otp->resolution according to the code, specification does
5425 * prescribe OTP header to be available. */
5426 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5427 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5429 leave:
5430 if (NULL != saved_url) {
5431 /* Revert URL to original one */
5432 zfree(context->url);
5433 context->url = saved_url;
5435 #if HAVE_CURL_REAUTHORIZATION_BUG
5436 if (NULL != saved_curl) {
5437 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5438 context->curl = saved_curl;
5440 #endif
5442 free(code);
5443 free(message);
5444 xmlFreeDoc(response);
5445 xmlFreeNode(request);
5447 if (!err)
5448 isds_log(ILF_ISDS, ILL_DEBUG,
5449 _("New OTP code has been sent successfully on SendSMSCode "
5450 "request.\n"));
5451 return err;
5455 /* Convert response status code to isds_error code and set long message
5456 * @context is context to save long message to
5457 * @map is mapping from codes to errors and messages. Pass NULL for generic
5458 * handling.
5459 * @code is status code to translate
5460 * @message is non-localized status message to put into long message in case
5461 * of uknown error. It can be NULL if server did not provide any.
5462 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5463 * invalid invocation. */
5464 static isds_error statuscode2isds_error(struct isds_ctx *context,
5465 const struct code_map_isds_error *map,
5466 const xmlChar *code, const xmlChar *message) {
5467 if (NULL == code) {
5468 isds_log_message(context,
5469 _("NULL status code passed to statuscode2isds_error()"));
5470 return IE_INVAL;
5473 if (NULL != map) {
5474 /* Check for known error codes */
5475 for (int i=0; map->codes[i] != NULL; i++) {
5476 if (!xmlStrcmp(code, map->codes[i])) {
5477 isds_log_message(context, _(map->meanings[i]));
5478 return map->errors[i];
5483 /* Other error */
5484 if (xmlStrcmp(code, BAD_CAST "0000")) {
5485 char *message_locale = _isds_utf82locale((char*)message);
5486 if (NULL == message_locale)
5487 isds_log_message(context, _("ISDS server returned unknown error"));
5488 else
5489 isds_log_message(context, message_locale);
5490 free(message_locale);
5491 return IE_ISDS;
5494 return IE_SUCCESS;
5496 #endif
5499 /* Change user password in ISDS.
5500 * User must supply old password, new password will takes effect after some
5501 * time, current session can continue. Password must fulfill some constraints.
5502 * @context is session context
5503 * @old_password is current password.
5504 * @new_password is requested new password
5505 * @otp auxiliary data required if one-time password authentication is in use,
5506 * defines OTP code (if known) and returns fine grade resolution of OTP
5507 * procedure. Pass NULL, if one-time password authentication is not needed.
5508 * Please note the @otp argument must match OTP method used at log-in time. See
5509 * isds_login() function for more details.
5510 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5511 * NULL, if you don't care.
5512 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5513 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5514 * awaiting OTP code that has been delivered by side channel to the user. */
5515 isds_error isds_change_password(struct isds_ctx *context,
5516 const char *old_password, const char *new_password,
5517 struct isds_otp *otp, char **refnumber) {
5518 isds_error err = IE_SUCCESS;
5519 #if HAVE_LIBCURL
5520 char *saved_url = NULL; /* No copy */
5521 #if HAVE_CURL_REAUTHORIZATION_BUG
5522 CURL *saved_curl = NULL; /* No copy */
5523 #endif
5524 xmlNsPtr isds_ns = NULL;
5525 xmlNodePtr request = NULL, node;
5526 xmlDocPtr response = NULL;
5527 xmlChar *code = NULL, *message = NULL;
5528 const xmlChar *codes[] = {
5529 BAD_CAST "1066",
5530 BAD_CAST "1067",
5531 BAD_CAST "1079",
5532 BAD_CAST "1080",
5533 BAD_CAST "1081",
5534 BAD_CAST "1082",
5535 BAD_CAST "1083",
5536 BAD_CAST "1090",
5537 BAD_CAST "1091",
5538 BAD_CAST "2300",
5539 BAD_CAST "9204"
5541 const char *meanings[] = {
5542 N_("Password length must be between 8 and 32 characters"),
5543 N_("Password cannot be reused"), /* Server does not distinguish 1067
5544 and 1091 on ChangePasswordOTP */
5545 N_("Password contains forbidden character"),
5546 N_("Password must contain at least one upper-case letter, "
5547 "one lower-case, and one digit"),
5548 N_("Password cannot contain sequence of three identical characters"),
5549 N_("Password cannot contain user identifier"),
5550 N_("Password is too simmple"),
5551 N_("Old password is not valid"),
5552 N_("Password cannot be reused"),
5553 N_("Unexpected error"),
5554 N_("LDAP update error")
5556 #endif
5558 if (!context) return IE_INVALID_CONTEXT;
5559 zfree(context->long_message);
5560 if (NULL != refnumber)
5561 zfree(*refnumber);
5562 if (NULL == old_password) {
5563 isds_log_message(context,
5564 _("Second argument (old password) of isds_change_password() "
5565 "is NULL"));
5566 return IE_INVAL;
5568 if (NULL == otp && NULL == new_password) {
5569 isds_log_message(context,
5570 _("Third argument (new password) of isds_change_password() "
5571 "is NULL"));
5572 return IE_INVAL;
5575 #if HAVE_LIBCURL
5576 /* Check if connection is established
5577 * TODO: This check should be done downstairs. */
5578 if (!context->curl) return IE_CONNECTION_CLOSED;
5580 if (context->otp && NULL == otp) {
5581 isds_log_message(context, _("If one-time password authentication "
5582 "method is in use, changing password requires one-time "
5583 "credentials either"));
5584 return IE_INVAL;
5587 /* Build ChangeISDSPassword request */
5588 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5589 BAD_CAST "ChangePasswordOTP");
5590 if (!request) {
5591 isds_log_message(context, (NULL == otp) ?
5592 _("Could not build ChangeISDSPassword request") :
5593 _("Could not build ChangePasswordOTP request"));
5594 return IE_ERROR;
5596 isds_ns = xmlNewNs(request,
5597 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5598 NULL);
5599 if(!isds_ns) {
5600 isds_log_message(context, _("Could not create ISDS name space"));
5601 xmlFreeNode(request);
5602 return IE_ERROR;
5604 xmlSetNs(request, isds_ns);
5606 INSERT_STRING(request, "dbOldPassword", old_password);
5607 INSERT_STRING(request, "dbNewPassword", new_password);
5609 if (NULL != otp) {
5610 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5611 switch (otp->method) {
5612 case OTP_HMAC:
5613 isds_log(ILF_SEC, ILL_INFO,
5614 _("Selected authentication method: "
5615 "HMAC-based one-time password\n"));
5616 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5617 break;
5618 case OTP_TIME:
5619 isds_log(ILF_SEC, ILL_INFO,
5620 _("Selected authentication method: "
5621 "Time-based one-time password\n"));
5622 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5623 if (otp->otp_code == NULL) {
5624 isds_log(ILF_SEC, ILL_INFO,
5625 _("OTP code has not been provided by "
5626 "application, requesting server for "
5627 "new one.\n"));
5628 err = _isds_request_totp_code(context, old_password, otp,
5629 refnumber);
5630 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5631 goto leave;
5633 } else {
5634 isds_log(ILF_SEC, ILL_INFO,
5635 _("OTP code has been provided by "
5636 "application, not requesting server "
5637 "for new one.\n"));
5639 break;
5640 default:
5641 isds_log_message(context,
5642 _("Unknown one-time password authentication "
5643 "method requested by application"));
5644 err = IE_ENUM;
5645 goto leave;
5648 /* Change URL temporarily for sending this request only */
5650 char *new_url = NULL;
5651 if ((err = _isds_build_url_from_context(context,
5652 "%1$.*2$sasws/changePassword", &new_url))) {
5653 goto leave;
5655 saved_url = context->url;
5656 context->url = new_url;
5659 /* Store credentials for sending this request only */
5660 context->otp_credentials = otp;
5661 _isds_discard_credentials(context, 0);
5662 if ((err = _isds_store_credentials(context, context->saved_username,
5663 old_password, NULL))) {
5664 _isds_discard_credentials(context, 0);
5665 goto leave;
5667 #if HAVE_CURL_REAUTHORIZATION_BUG
5668 saved_curl = context->curl;
5669 context->curl = curl_easy_init();
5670 if (NULL == context->curl) {
5671 err = IE_ERROR;
5672 goto leave;
5674 if (context->timeout) {
5675 err = isds_set_timeout(context, context->timeout);
5676 if (err) goto leave;
5678 #endif
5681 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5682 _("Sending ChangeISDSPassword request to ISDS\n") :
5683 _("Sending ChangePasswordOTP request to ISDS\n"));
5685 /* Sent request */
5686 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5687 request, &response, NULL, NULL);
5689 if (otp) {
5690 /* Remove temporal credentials */
5691 _isds_discard_credentials(context, 0);
5692 /* Detach pointer to OTP credentials from context */
5693 context->otp_credentials = NULL;
5694 /* Keep context->otp true to keep signaling this is OTP session */
5697 /* Destroy request */
5698 xmlFreeNode(request); request = NULL;
5700 if (err) {
5701 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5702 _("Processing ISDS response on ChangeISDSPassword "
5703 "request failed\n") :
5704 _("Processing ISDS response on ChangePasswordOTP "
5705 "request failed\n"));
5706 goto leave;
5709 /* Check for response status */
5710 err = isds_response_status(context,
5711 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5712 &code, &message, (xmlChar **)refnumber);
5713 if (err) {
5714 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5715 _("ISDS response on ChangeISDSPassword request is missing "
5716 "status\n") :
5717 _("ISDS response on ChangePasswordOTP request is missing "
5718 "status\n"));
5719 goto leave;
5722 /* Check for known error codes */
5723 for (size_t i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5724 if (!xmlStrcmp(code, codes[i])) {
5725 char *code_locale = _isds_utf82locale((char*)code);
5726 char *message_locale = _isds_utf82locale((char*)message);
5727 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5728 _("Server refused to change password on ChangeISDSPassword "
5729 "request (code=%s, message=%s)\n") :
5730 _("Server refused to change password on ChangePasswordOTP "
5731 "request (code=%s, message=%s)\n"),
5732 code_locale, message_locale);
5733 free(code_locale);
5734 free(message_locale);
5735 isds_log_message(context, _(meanings[i]));
5736 err = IE_INVAL;
5737 goto leave;
5741 /* Other error */
5742 if (xmlStrcmp(code, BAD_CAST "0000")) {
5743 char *code_locale = _isds_utf82locale((char*)code);
5744 char *message_locale = _isds_utf82locale((char*)message);
5745 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5746 _("Server refused to change password on ChangeISDSPassword "
5747 "request (code=%s, message=%s)\n") :
5748 _("Server refused to change password on ChangePasswordOTP "
5749 "request (code=%s, message=%s)\n"),
5750 code_locale, message_locale);
5751 isds_log_message(context, message_locale);
5752 free(code_locale);
5753 free(message_locale);
5754 err = IE_ISDS;
5755 goto leave;
5758 /* Otherwise password changed successfully */
5760 leave:
5761 if (NULL != saved_url) {
5762 /* Revert URL to original one */
5763 zfree(context->url);
5764 context->url = saved_url;
5766 #if HAVE_CURL_REAUTHORIZATION_BUG
5767 if (NULL != saved_curl) {
5768 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5769 context->curl = saved_curl;
5771 #endif
5773 free(code);
5774 free(message);
5775 xmlFreeDoc(response);
5776 xmlFreeNode(request);
5778 if (!err)
5779 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5780 _("Password changed successfully on ChangeISDSPassword "
5781 "request.\n") :
5782 _("Password changed successfully on ChangePasswordOTP "
5783 "request.\n"));
5784 #else /* not HAVE_LIBCURL */
5785 err = IE_NOTSUP;
5786 #endif
5788 return err;
5792 #if HAVE_LIBCURL
5793 /* Generic middle part with request sending and response check.
5794 * It sends prepared request and checks for error code.
5795 * @context is ISDS session context.
5796 * @service is ISDS service handler
5797 * @service_name is name in scope of given @service
5798 * @request is XML tree with request. Will be freed to save memory.
5799 * @response is XML document outputting ISDS response.
5800 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5801 * @map is mapping from status code to library error. Pass NULL if no special
5802 * handling is requested.
5803 * NULL, if you don't care. */
5804 static isds_error send_destroy_request_check_response(
5805 struct isds_ctx *context,
5806 const isds_service service, const xmlChar *service_name,
5807 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5808 const struct code_map_isds_error *map) {
5809 isds_error err = IE_SUCCESS;
5810 char *service_name_locale = NULL;
5811 xmlChar *code = NULL, *message = NULL;
5814 if (!context) return IE_INVALID_CONTEXT;
5815 if (!service_name || *service_name == '\0' || !request || !*request ||
5816 !response)
5817 return IE_INVAL;
5819 /* Check if connection is established
5820 * TODO: This check should be done downstairs. */
5821 if (!context->curl) return IE_CONNECTION_CLOSED;
5823 service_name_locale = _isds_utf82locale((char*) service_name);
5824 if (!service_name_locale) {
5825 err = IE_NOMEM;
5826 goto leave;
5829 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5830 service_name_locale);
5832 /* Send request */
5833 err = _isds(context, service, *request, response, NULL, NULL);
5834 xmlFreeNode(*request); *request = NULL;
5836 if (err) {
5837 isds_log(ILF_ISDS, ILL_DEBUG,
5838 _("Processing ISDS response on %s request failed\n"),
5839 service_name_locale);
5840 goto leave;
5843 /* Check for response status */
5844 err = isds_response_status(context, service, *response,
5845 &code, &message, refnumber);
5846 if (err) {
5847 isds_log(ILF_ISDS, ILL_DEBUG,
5848 _("ISDS response on %s request is missing status\n"),
5849 service_name_locale);
5850 goto leave;
5853 err = statuscode2isds_error(context, map, code, message);
5855 /* Request processed, but server failed */
5856 if (xmlStrcmp(code, BAD_CAST "0000")) {
5857 char *code_locale = _isds_utf82locale((char*) code);
5858 char *message_locale = _isds_utf82locale((char*) message);
5859 isds_log(ILF_ISDS, ILL_DEBUG,
5860 _("Server refused %s request (code=%s, message=%s)\n"),
5861 service_name_locale, code_locale, message_locale);
5862 free(code_locale);
5863 free(message_locale);
5864 goto leave;
5868 leave:
5869 free(code);
5870 free(message);
5871 if (err && *response) {
5872 xmlFreeDoc(*response);
5873 *response = NULL;
5875 if (*request) {
5876 xmlFreeNode(*request);
5877 *request = NULL;
5879 free(service_name_locale);
5881 return err;
5885 /* Generic bottom half with request sending.
5886 * It sends prepared request, checks for error code, destroys response and
5887 * request and log success or failure.
5888 * @context is ISDS session context.
5889 * @service is ISDS service handler
5890 * @service_name is name in scope of given @service
5891 * @request is XML tree with request. Will be freed to save memory.
5892 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5893 * NULL, if you don't care. */
5894 static isds_error send_request_check_drop_response(
5895 struct isds_ctx *context,
5896 const isds_service service, const xmlChar *service_name,
5897 xmlNodePtr *request, xmlChar **refnumber) {
5898 isds_error err = IE_SUCCESS;
5899 xmlDocPtr response = NULL;
5902 if (!context) return IE_INVALID_CONTEXT;
5903 if (!service_name || *service_name == '\0' || !request || !*request)
5904 return IE_INVAL;
5906 /* Send request and check response*/
5907 err = send_destroy_request_check_response(context,
5908 service, service_name, request, &response, refnumber, NULL);
5910 xmlFreeDoc(response);
5912 if (*request) {
5913 xmlFreeNode(*request);
5914 *request = NULL;
5917 if (!err) {
5918 char *service_name_locale = _isds_utf82locale((char *) service_name);
5919 isds_log(ILF_ISDS, ILL_DEBUG,
5920 _("%s request processed by server successfully.\n"),
5921 service_name_locale);
5922 free(service_name_locale);
5925 return err;
5929 /* Insert isds_credentials_delivery structure into XML request if not NULL
5930 * @context is session context
5931 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5932 * credentials delivery. The email field is passed.
5933 * @parent is XML element where to insert */
5934 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5935 const struct isds_credentials_delivery *credentials_delivery,
5936 xmlNodePtr parent) {
5937 isds_error err = IE_SUCCESS;
5938 xmlNodePtr node;
5940 if (!context) return IE_INVALID_CONTEXT;
5941 if (!parent) return IE_INVAL;
5943 if (credentials_delivery) {
5944 /* Following elements are valid only for services:
5945 * NewAccessData, AddDataBoxUser, CreateDataBox */
5946 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5947 INSERT_STRING(parent, "email", credentials_delivery->email);
5950 leave:
5951 return err;
5955 /* Extract credentials delivery from ISDS response.
5956 * @context is session context
5957 * @credentials_delivery is pointer to valid structure to fill in returned
5958 * user's password (and new log-in name). If NULL, do not extract the data.
5959 * @response is pointer to XML document with ISDS response
5960 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5961 * @return IE_SUCCESS even if new user name has not been found because it's not
5962 * clear whether it's returned always. */
5963 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5964 struct isds_credentials_delivery *credentials_delivery,
5965 xmlDocPtr response, const char *request_name) {
5966 isds_error err = IE_SUCCESS;
5967 xmlXPathContextPtr xpath_ctx = NULL;
5968 xmlXPathObjectPtr result = NULL;
5969 char *xpath_query = NULL;
5971 if (!context) return IE_INVALID_CONTEXT;
5972 if (credentials_delivery) {
5973 zfree(credentials_delivery->token);
5974 zfree(credentials_delivery->new_user_name);
5976 if (!response || !request_name || !*request_name) return IE_INVAL;
5979 /* Extract optional token */
5980 if (credentials_delivery) {
5981 xpath_ctx = xmlXPathNewContext(response);
5982 if (!xpath_ctx) {
5983 err = IE_ERROR;
5984 goto leave;
5986 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5987 err = IE_ERROR;
5988 goto leave;
5991 /* Verify root element */
5992 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
5993 request_name)) {
5994 err = IE_NOMEM;
5995 goto leave;
5997 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
5998 if (!result) {
5999 err = IE_ERROR;
6000 goto leave;
6002 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6003 char *request_name_locale = _isds_utf82locale(request_name);
6004 isds_log(ILF_ISDS, ILL_WARNING,
6005 _("Wrong element in ISDS response for %s request "
6006 "while extracting credentials delivery details\n"),
6007 request_name_locale);
6008 free(request_name_locale);
6009 err = IE_ERROR;
6010 goto leave;
6012 xpath_ctx->node = result->nodesetval->nodeTab[0];
6015 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6016 * optional. */
6017 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
6019 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
6020 if (!credentials_delivery->token) {
6021 char *request_name_locale = _isds_utf82locale(request_name);
6022 isds_log(ILF_ISDS, ILL_ERR,
6023 _("ISDS did not return token on %s request "
6024 "even if requested\n"), request_name_locale);
6025 free(request_name_locale);
6026 err = IE_ERROR;
6030 leave:
6031 free(xpath_query);
6032 xmlXPathFreeObject(result);
6033 xmlXPathFreeContext(xpath_ctx);
6035 return err;
6039 /* Build XSD:tCreateDBInput request type for box creating.
6040 * @context is session context
6041 * @request outputs built XML tree
6042 * @service_name is request name of SERVICE_DB_MANIPULATION service
6043 * @box is box description to create including single primary user (in case of
6044 * FO box type)
6045 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6046 * box, or contact address of PFO box owner)
6047 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6048 * @upper_box_id is optional ID of supper box if currently created box is
6049 * subordinated.
6050 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6051 * don't care.
6052 * @credentials_delivery is valid pointer if ISDS should return token that box
6053 * owner can use to obtain his new credentials in on-line way. Then valid email
6054 * member value should be supplied.
6055 * @approval is optional external approval of box manipulation */
6056 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
6057 xmlNodePtr *request, const xmlChar *service_name,
6058 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6059 const xmlChar *former_names, const xmlChar *upper_box_id,
6060 const xmlChar *ceo_label,
6061 const struct isds_credentials_delivery *credentials_delivery,
6062 const struct isds_approval *approval) {
6063 isds_error err = IE_SUCCESS;
6064 xmlNsPtr isds_ns = NULL;
6065 xmlNodePtr node, dbPrimaryUsers;
6066 xmlChar *string = NULL;
6067 const struct isds_list *item;
6070 if (!context) return IE_INVALID_CONTEXT;
6071 if (!request || !service_name || service_name[0] == '\0' || !box)
6072 return IE_INVAL;
6075 /* Build CreateDataBox-similar request */
6076 *request = xmlNewNode(NULL, service_name);
6077 if (!*request) {
6078 char *service_name_locale = _isds_utf82locale((char*) service_name);
6079 isds_printf_message(context, _("Could build %s request"),
6080 service_name_locale);
6081 free(service_name_locale);
6082 return IE_ERROR;
6084 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
6085 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
6086 if (!isds_ns) {
6087 isds_log_message(context, _("Could not create ISDS1 name space"));
6088 xmlFreeNode(*request);
6089 return IE_ERROR;
6091 } else {
6092 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6093 if (!isds_ns) {
6094 isds_log_message(context, _("Could not create ISDS name space"));
6095 xmlFreeNode(*request);
6096 return IE_ERROR;
6099 xmlSetNs(*request, isds_ns);
6101 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6102 err = insert_DbOwnerInfo(context, box, node);
6103 if (err) goto leave;
6105 /* Insert users */
6106 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6107 * verbose documentation allows none dbUserInfo */
6108 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6109 for (item = users; item; item = item->next) {
6110 if (item->data) {
6111 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6112 err = insert_DbUserInfo(context,
6113 (struct isds_DbUserInfo *) item->data, node);
6114 if (err) goto leave;
6118 INSERT_STRING(*request, "dbFormerNames", former_names);
6119 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6120 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6122 err = insert_credentials_delivery(context, credentials_delivery, *request);
6123 if (err) goto leave;
6125 err = insert_GExtApproval(context, approval, *request);
6126 if (err) goto leave;
6128 leave:
6129 if (err) {
6130 xmlFreeNode(*request);
6131 *request = NULL;
6133 free(string);
6134 return err;
6136 #endif /* HAVE_LIBCURL */
6139 /* Create new box.
6140 * @context is session context
6141 * @box is box description to create including single primary user (in case of
6142 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6143 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6144 * box, or contact address of PFO box owner)
6145 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6146 * @upper_box_id is optional ID of supper box if currently created box is
6147 * subordinated.
6148 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6149 * @credentials_delivery is NULL if new password should be delivered off-line
6150 * to box owner. It is valid pointer if owner should obtain new password on-line
6151 * on dedicated web server. Then input @credentials_delivery.email value is
6152 * his e-mail address he must provide to dedicated web server together
6153 * with output reallocated @credentials_delivery.token member. Output
6154 * member @credentials_delivery.new_user_name is unused up on this call.
6155 * @approval is optional external approval of box manipulation
6156 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6157 * NULL, if you don't care.*/
6158 isds_error isds_add_box(struct isds_ctx *context,
6159 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6160 const char *former_names, const char *upper_box_id,
6161 const char *ceo_label,
6162 struct isds_credentials_delivery *credentials_delivery,
6163 const struct isds_approval *approval, char **refnumber) {
6164 isds_error err = IE_SUCCESS;
6165 #if HAVE_LIBCURL
6166 xmlNodePtr request = NULL;
6167 xmlDocPtr response = NULL;
6168 xmlXPathContextPtr xpath_ctx = NULL;
6169 xmlXPathObjectPtr result = NULL;
6170 #endif
6173 if (!context) return IE_INVALID_CONTEXT;
6174 zfree(context->long_message);
6175 if (credentials_delivery) {
6176 zfree(credentials_delivery->token);
6177 zfree(credentials_delivery->new_user_name);
6179 if (!box) return IE_INVAL;
6181 #if HAVE_LIBCURL
6182 /* Scratch box ID */
6183 zfree(box->dbID);
6185 /* Build CreateDataBox request */
6186 err = build_CreateDBInput_request(context,
6187 &request, BAD_CAST "CreateDataBox",
6188 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6189 (xmlChar *) ceo_label, credentials_delivery, approval);
6190 if (err) goto leave;
6192 /* Send it to server and process response */
6193 err = send_destroy_request_check_response(context,
6194 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6195 &response, (xmlChar **) refnumber, NULL);
6197 /* Extract box ID */
6198 xpath_ctx = xmlXPathNewContext(response);
6199 if (!xpath_ctx) {
6200 err = IE_ERROR;
6201 goto leave;
6203 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6204 err = IE_ERROR;
6205 goto leave;
6207 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6209 /* Extract optional token */
6210 err = extract_credentials_delivery(context, credentials_delivery, response,
6211 "CreateDataBox");
6213 leave:
6214 xmlXPathFreeObject(result);
6215 xmlXPathFreeContext(xpath_ctx);
6216 xmlFreeDoc(response);
6217 xmlFreeNode(request);
6219 if (!err) {
6220 isds_log(ILF_ISDS, ILL_DEBUG,
6221 _("CreateDataBox request processed by server successfully.\n"));
6223 #else /* not HAVE_LIBCURL */
6224 err = IE_NOTSUP;
6225 #endif
6227 return err;
6231 /* Notify ISDS about new PFO entity.
6232 * This function has no real effect.
6233 * @context is session context
6234 * @box is PFO description including single primary user.
6235 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6236 * @former_names is optional undocumented string. Pass NULL if you don't care.
6237 * @upper_box_id is optional ID of supper box if currently created box is
6238 * subordinated.
6239 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6240 * @approval is optional external approval of box manipulation
6241 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6242 * NULL, if you don't care.*/
6243 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6244 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6245 const char *former_names, const char *upper_box_id,
6246 const char *ceo_label, const struct isds_approval *approval,
6247 char **refnumber) {
6248 isds_error err = IE_SUCCESS;
6249 #if HAVE_LIBCURL
6250 xmlNodePtr request = NULL;
6251 #endif
6253 if (!context) return IE_INVALID_CONTEXT;
6254 zfree(context->long_message);
6255 if (!box) return IE_INVAL;
6257 #if HAVE_LIBCURL
6258 /* Build CreateDataBoxPFOInfo request */
6259 err = build_CreateDBInput_request(context,
6260 &request, BAD_CAST "CreateDataBoxPFOInfo",
6261 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6262 (xmlChar *) ceo_label, NULL, approval);
6263 if (err) goto leave;
6265 /* Send it to server and process response */
6266 err = send_request_check_drop_response(context,
6267 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6268 (xmlChar **) refnumber);
6269 /* XXX: XML Schema names output dbID element but textual documentation
6270 * states no box identifier is returned. */
6271 leave:
6272 xmlFreeNode(request);
6273 #else /* not HAVE_LIBCURL */
6274 err = IE_NOTSUP;
6275 #endif
6276 return err;
6280 /* Common implementation for removing given box.
6281 * @context is session context
6282 * @service_name is UTF-8 encoded name fo ISDS service
6283 * @box is box description to delete
6284 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6285 * carry sane value. If NULL, do not inject this information into request.
6286 * @approval is optional external approval of box manipulation
6287 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6288 * NULL, if you don't care.*/
6289 isds_error _isds_delete_box_common(struct isds_ctx *context,
6290 const xmlChar *service_name,
6291 const struct isds_DbOwnerInfo *box, const struct tm *since,
6292 const struct isds_approval *approval, char **refnumber) {
6293 isds_error err = IE_SUCCESS;
6294 #if HAVE_LIBCURL
6295 xmlNsPtr isds_ns = NULL;
6296 xmlNodePtr request = NULL;
6297 xmlNodePtr node;
6298 xmlChar *string = NULL;
6299 #endif
6302 if (!context) return IE_INVALID_CONTEXT;
6303 zfree(context->long_message);
6304 if (!service_name || !*service_name || !box) return IE_INVAL;
6307 #if HAVE_LIBCURL
6308 /* Build DeleteDataBox(Promptly) request */
6309 request = xmlNewNode(NULL, service_name);
6310 if (!request) {
6311 char *service_name_locale = _isds_utf82locale((char*)service_name);
6312 isds_printf_message(context,
6313 _("Could build %s request"), service_name_locale);
6314 free(service_name_locale);
6315 return IE_ERROR;
6317 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6318 if(!isds_ns) {
6319 isds_log_message(context, _("Could not create ISDS name space"));
6320 xmlFreeNode(request);
6321 return IE_ERROR;
6323 xmlSetNs(request, isds_ns);
6325 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6326 err = insert_DbOwnerInfo(context, box, node);
6327 if (err) goto leave;
6329 if (since) {
6330 err = tm2datestring(since, &string);
6331 if (err) {
6332 isds_log_message(context,
6333 _("Could not convert `since' argument to ISO date string"));
6334 goto leave;
6336 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6337 zfree(string);
6340 err = insert_GExtApproval(context, approval, request);
6341 if (err) goto leave;
6344 /* Send it to server and process response */
6345 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6346 service_name, &request, (xmlChar **) refnumber);
6348 leave:
6349 xmlFreeNode(request);
6350 free(string);
6351 #else /* not HAVE_LIBCURL */
6352 err = IE_NOTSUP;
6353 #endif
6354 return err;
6358 /* Remove given box permanently.
6359 * @context is session context
6360 * @box is box description to delete
6361 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6362 * carry sane value.
6363 * @approval is optional external approval of box manipulation
6364 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6365 * NULL, if you don't care.*/
6366 isds_error isds_delete_box(struct isds_ctx *context,
6367 const struct isds_DbOwnerInfo *box, const struct tm *since,
6368 const struct isds_approval *approval, char **refnumber) {
6369 if (!context) return IE_INVALID_CONTEXT;
6370 zfree(context->long_message);
6371 if (!box || !since) return IE_INVAL;
6373 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6374 box, since, approval, refnumber);
6378 /* Undocumented function.
6379 * @context is session context
6380 * @box is box description to delete
6381 * @approval is optional external approval of box manipulation
6382 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6383 * NULL, if you don't care.*/
6384 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6385 const struct isds_DbOwnerInfo *box,
6386 const struct isds_approval *approval, char **refnumber) {
6387 if (!context) return IE_INVALID_CONTEXT;
6388 zfree(context->long_message);
6389 if (!box) return IE_INVAL;
6391 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6392 box, NULL, approval, refnumber);
6396 /* Update data about given box.
6397 * @context is session context
6398 * @old_box current box description
6399 * @new_box are updated data about @old_box
6400 * @approval is optional external approval of box manipulation
6401 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6402 * NULL, if you don't care.*/
6403 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6404 const struct isds_DbOwnerInfo *old_box,
6405 const struct isds_DbOwnerInfo *new_box,
6406 const struct isds_approval *approval, char **refnumber) {
6407 isds_error err = IE_SUCCESS;
6408 #if HAVE_LIBCURL
6409 xmlNsPtr isds_ns = NULL;
6410 xmlNodePtr request = NULL;
6411 xmlNodePtr node;
6412 #endif
6415 if (!context) return IE_INVALID_CONTEXT;
6416 zfree(context->long_message);
6417 if (!old_box || !new_box) return IE_INVAL;
6420 #if HAVE_LIBCURL
6421 /* Build UpdateDataBoxDescr request */
6422 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6423 if (!request) {
6424 isds_log_message(context,
6425 _("Could build UpdateDataBoxDescr request"));
6426 return IE_ERROR;
6428 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6429 if(!isds_ns) {
6430 isds_log_message(context, _("Could not create ISDS name space"));
6431 xmlFreeNode(request);
6432 return IE_ERROR;
6434 xmlSetNs(request, isds_ns);
6436 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6437 err = insert_DbOwnerInfo(context, old_box, node);
6438 if (err) goto leave;
6440 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6441 err = insert_DbOwnerInfo(context, new_box, node);
6442 if (err) goto leave;
6444 err = insert_GExtApproval(context, approval, request);
6445 if (err) goto leave;
6448 /* Send it to server and process response */
6449 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6450 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6452 leave:
6453 xmlFreeNode(request);
6454 #else /* not HAVE_LIBCURL */
6455 err = IE_NOTSUP;
6456 #endif
6458 return err;
6462 #if HAVE_LIBCURL
6463 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6464 * code
6465 * @context is session context
6466 * @service is SOAP service
6467 * @service_name is name of request in @service
6468 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6469 * @box_id is box ID of interest
6470 * @approval is optional external approval of box manipulation
6471 * @response is server SOAP body response as XML document
6472 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6473 * NULL, if you don't care.
6474 * @return error coded from lower layer, context message will be set up
6475 * appropriately. */
6476 static isds_error build_send_dbid_request_check_response(
6477 struct isds_ctx *context, const isds_service service,
6478 const xmlChar *service_name, const xmlChar *box_id_element,
6479 const xmlChar *box_id, const struct isds_approval *approval,
6480 xmlDocPtr *response, xmlChar **refnumber) {
6482 isds_error err = IE_SUCCESS;
6483 char *service_name_locale = NULL, *box_id_locale = NULL;
6484 xmlNodePtr request = NULL, node;
6485 xmlNsPtr isds_ns = NULL;
6487 if (!context) return IE_INVALID_CONTEXT;
6488 if (!service_name || !box_id) return IE_INVAL;
6489 if (!response) return IE_INVAL;
6491 /* Free output argument */
6492 xmlFreeDoc(*response); *response = NULL;
6494 /* Prepare strings */
6495 service_name_locale = _isds_utf82locale((char*)service_name);
6496 if (!service_name_locale) {
6497 err = IE_NOMEM;
6498 goto leave;
6500 box_id_locale = _isds_utf82locale((char*)box_id);
6501 if (!box_id_locale) {
6502 err = IE_NOMEM;
6503 goto leave;
6506 /* Build request */
6507 request = xmlNewNode(NULL, service_name);
6508 if (!request) {
6509 isds_printf_message(context,
6510 _("Could not build %s request for %s box"), service_name_locale,
6511 box_id_locale);
6512 err = IE_ERROR;
6513 goto leave;
6515 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6516 if(!isds_ns) {
6517 isds_log_message(context, _("Could not create ISDS name space"));
6518 err = IE_ERROR;
6519 goto leave;
6521 xmlSetNs(request, isds_ns);
6523 /* Add XSD:tIdDbInput children */
6524 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6525 INSERT_STRING(request, box_id_element, box_id);
6526 err = insert_GExtApproval(context, approval, request);
6527 if (err) goto leave;
6529 /* Send request and check response*/
6530 err = send_destroy_request_check_response(context,
6531 service, service_name, &request, response, refnumber, NULL);
6533 leave:
6534 free(service_name_locale);
6535 free(box_id_locale);
6536 xmlFreeNode(request);
6537 return err;
6539 #endif /* HAVE_LIBCURL */
6542 /* Get data about all users assigned to given box.
6543 * @context is session context
6544 * @box_id is box ID
6545 * @users is automatically reallocated list of struct isds_DbUserInfo */
6546 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6547 struct isds_list **users) {
6548 isds_error err = IE_SUCCESS;
6549 #if HAVE_LIBCURL
6550 xmlDocPtr response = NULL;
6551 xmlXPathContextPtr xpath_ctx = NULL;
6552 xmlXPathObjectPtr result = NULL;
6553 int i;
6554 struct isds_list *item, *prev_item = NULL;
6555 #endif
6557 if (!context) return IE_INVALID_CONTEXT;
6558 zfree(context->long_message);
6559 if (!users || !box_id) return IE_INVAL;
6560 isds_list_free(users);
6563 #if HAVE_LIBCURL
6564 /* Do request and check for success */
6565 err = build_send_dbid_request_check_response(context,
6566 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6567 BAD_CAST box_id, NULL, &response, NULL);
6568 if (err) goto leave;
6571 /* Extract data */
6572 /* Prepare structure */
6573 xpath_ctx = xmlXPathNewContext(response);
6574 if (!xpath_ctx) {
6575 err = IE_ERROR;
6576 goto leave;
6578 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6579 err = IE_ERROR;
6580 goto leave;
6583 /* Set context node */
6584 result = xmlXPathEvalExpression(BAD_CAST
6585 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6586 xpath_ctx);
6587 if (!result) {
6588 err = IE_ERROR;
6589 goto leave;
6591 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6592 /* Iterate over all users */
6593 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6595 /* Prepare structure */
6596 item = calloc(1, sizeof(*item));
6597 if (!item) {
6598 err = IE_NOMEM;
6599 goto leave;
6601 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6602 if (i == 0) *users = item;
6603 else prev_item->next = item;
6604 prev_item = item;
6606 /* Extract it */
6607 xpath_ctx->node = result->nodesetval->nodeTab[i];
6608 err = extract_DbUserInfo(context,
6609 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6610 if (err) goto leave;
6614 leave:
6615 if (err) {
6616 isds_list_free(users);
6619 xmlXPathFreeObject(result);
6620 xmlXPathFreeContext(xpath_ctx);
6621 xmlFreeDoc(response);
6623 if (!err)
6624 isds_log(ILF_ISDS, ILL_DEBUG,
6625 _("GetDataBoxUsers request processed by server "
6626 "successfully.\n"));
6627 #else /* not HAVE_LIBCURL */
6628 err = IE_NOTSUP;
6629 #endif
6631 return err;
6635 /* Update data about user assigned to given box.
6636 * @context is session context
6637 * @box is box identification
6638 * @old_user identifies user to update
6639 * @new_user are updated data about @old_user
6640 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6641 * NULL, if you don't care.*/
6642 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6643 const struct isds_DbOwnerInfo *box,
6644 const struct isds_DbUserInfo *old_user,
6645 const struct isds_DbUserInfo *new_user,
6646 char **refnumber) {
6647 isds_error err = IE_SUCCESS;
6648 #if HAVE_LIBCURL
6649 xmlNsPtr isds_ns = NULL;
6650 xmlNodePtr request = NULL;
6651 xmlNodePtr node;
6652 #endif
6655 if (!context) return IE_INVALID_CONTEXT;
6656 zfree(context->long_message);
6657 if (!box || !old_user || !new_user) return IE_INVAL;
6660 #if HAVE_LIBCURL
6661 /* Build UpdateDataBoxUser request */
6662 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6663 if (!request) {
6664 isds_log_message(context,
6665 _("Could build UpdateDataBoxUser request"));
6666 return IE_ERROR;
6668 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6669 if(!isds_ns) {
6670 isds_log_message(context, _("Could not create ISDS name space"));
6671 xmlFreeNode(request);
6672 return IE_ERROR;
6674 xmlSetNs(request, isds_ns);
6676 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6677 err = insert_DbOwnerInfo(context, box, node);
6678 if (err) goto leave;
6680 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6681 err = insert_DbUserInfo(context, old_user, node);
6682 if (err) goto leave;
6684 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6685 err = insert_DbUserInfo(context, new_user, node);
6686 if (err) goto leave;
6688 /* Send it to server and process response */
6689 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6690 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6692 leave:
6693 xmlFreeNode(request);
6694 #else /* not HAVE_LIBCURL */
6695 err = IE_NOTSUP;
6696 #endif
6698 return err;
6702 /* Undocumented function.
6703 * @context is session context
6704 * @box_id is UTF-8 encoded box identifier
6705 * @token is UTF-8 encoded temporary password
6706 * @user_id outputs UTF-8 encoded reallocated user identifier
6707 * @password outpus UTF-8 encoded reallocated user password
6708 * Output arguments will be nulled in case of error */
6709 isds_error isds_activate(struct isds_ctx *context,
6710 const char *box_id, const char *token,
6711 char **user_id, char **password) {
6712 isds_error err = IE_SUCCESS;
6713 #if HAVE_LIBCURL
6714 xmlNsPtr isds_ns = NULL;
6715 xmlNodePtr request = NULL, node;
6716 xmlDocPtr response = NULL;
6717 xmlXPathContextPtr xpath_ctx = NULL;
6718 xmlXPathObjectPtr result = NULL;
6719 #endif
6722 if (!context) return IE_INVALID_CONTEXT;
6723 zfree(context->long_message);
6725 if (user_id) zfree(*user_id);
6726 if (password) zfree(*password);
6728 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6731 #if HAVE_LIBCURL
6732 /* Build Activate request */
6733 request = xmlNewNode(NULL, BAD_CAST "Activate");
6734 if (!request) {
6735 isds_log_message(context, _("Could build Activate request"));
6736 return IE_ERROR;
6738 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6739 if(!isds_ns) {
6740 isds_log_message(context, _("Could not create ISDS name space"));
6741 xmlFreeNode(request);
6742 return IE_ERROR;
6744 xmlSetNs(request, isds_ns);
6746 INSERT_STRING(request, "dbAccessDataId", token);
6747 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6748 INSERT_STRING(request, "dbID", box_id);
6751 /* Send request and check response*/
6752 err = send_destroy_request_check_response(context,
6753 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6754 &response, NULL, NULL);
6755 if (err) goto leave;
6758 /* Extract data */
6759 xpath_ctx = xmlXPathNewContext(response);
6760 if (!xpath_ctx) {
6761 err = IE_ERROR;
6762 goto leave;
6764 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6765 err = IE_ERROR;
6766 goto leave;
6768 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6769 xpath_ctx);
6770 if (!result) {
6771 err = IE_ERROR;
6772 goto leave;
6774 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6775 isds_log_message(context, _("Missing ActivateResponse element"));
6776 err = IE_ISDS;
6777 goto leave;
6779 if (result->nodesetval->nodeNr > 1) {
6780 isds_log_message(context, _("Multiple ActivateResponse element"));
6781 err = IE_ISDS;
6782 goto leave;
6784 xpath_ctx->node = result->nodesetval->nodeTab[0];
6785 xmlXPathFreeObject(result); result = NULL;
6787 EXTRACT_STRING("isds:userId", *user_id);
6788 if (!*user_id)
6789 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6790 "but did not return `userId' element.\n"));
6792 EXTRACT_STRING("isds:password", *password);
6793 if (!*password)
6794 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6795 "but did not return `password' element.\n"));
6797 leave:
6798 xmlXPathFreeObject(result);
6799 xmlXPathFreeContext(xpath_ctx);
6800 xmlFreeDoc(response);
6801 xmlFreeNode(request);
6803 if (!err)
6804 isds_log(ILF_ISDS, ILL_DEBUG,
6805 _("Activate request processed by server successfully.\n"));
6806 #else /* not HAVE_LIBCURL */
6807 err = IE_NOTSUP;
6808 #endif
6810 return err;
6814 /* Reset credentials of user assigned to given box.
6815 * @context is session context
6816 * @box is box identification
6817 * @user identifies user to reset password
6818 * @fee_paid is true if fee has been paid, false otherwise
6819 * @approval is optional external approval of box manipulation
6820 * @credentials_delivery is NULL if new password should be delivered off-line
6821 * to the user. It is valid pointer if user should obtain new password on-line
6822 * on dedicated web server. Then input @credentials_delivery.email value is
6823 * user's e-mail address user must provide to dedicated web server together
6824 * with @credentials_delivery.token. The output reallocated token user needs
6825 * to use to authorize on the web server to view his new password. Output
6826 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6827 * ISDS changed up on this call. (No reason why server could change the name
6828 * is known now.)
6829 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6830 * NULL, if you don't care.*/
6831 isds_error isds_reset_password(struct isds_ctx *context,
6832 const struct isds_DbOwnerInfo *box,
6833 const struct isds_DbUserInfo *user,
6834 const _Bool fee_paid, const struct isds_approval *approval,
6835 struct isds_credentials_delivery *credentials_delivery,
6836 char **refnumber) {
6837 isds_error err = IE_SUCCESS;
6838 #if HAVE_LIBCURL
6839 xmlNsPtr isds_ns = NULL;
6840 xmlNodePtr request = NULL, node;
6841 xmlDocPtr response = NULL;
6842 #endif
6845 if (!context) return IE_INVALID_CONTEXT;
6846 zfree(context->long_message);
6848 if (credentials_delivery) {
6849 zfree(credentials_delivery->token);
6850 zfree(credentials_delivery->new_user_name);
6852 if (!box || !user) return IE_INVAL;
6855 #if HAVE_LIBCURL
6856 /* Build NewAccessData request */
6857 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6858 if (!request) {
6859 isds_log_message(context,
6860 _("Could build NewAccessData request"));
6861 return IE_ERROR;
6863 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6864 if(!isds_ns) {
6865 isds_log_message(context, _("Could not create ISDS name space"));
6866 xmlFreeNode(request);
6867 return IE_ERROR;
6869 xmlSetNs(request, isds_ns);
6871 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6872 err = insert_DbOwnerInfo(context, box, node);
6873 if (err) goto leave;
6875 INSERT_ELEMENT(node, request, "dbUserInfo");
6876 err = insert_DbUserInfo(context, user, node);
6877 if (err) goto leave;
6879 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6881 err = insert_credentials_delivery(context, credentials_delivery, request);
6882 if (err) goto leave;
6884 err = insert_GExtApproval(context, approval, request);
6885 if (err) goto leave;
6887 /* Send request and check response*/
6888 err = send_destroy_request_check_response(context,
6889 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6890 &response, (xmlChar **) refnumber, NULL);
6891 if (err) goto leave;
6894 /* Extract optional token */
6895 err = extract_credentials_delivery(context, credentials_delivery,
6896 response, "NewAccessData");
6898 leave:
6899 xmlFreeDoc(response);
6900 xmlFreeNode(request);
6902 if (!err)
6903 isds_log(ILF_ISDS, ILL_DEBUG,
6904 _("NewAccessData request processed by server "
6905 "successfully.\n"));
6906 #else /* not HAVE_LIBCURL */
6907 err = IE_NOTSUP;
6908 #endif
6910 return err;
6914 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6915 * code, destroy response and log success.
6916 * @context is ISDS session context.
6917 * @service_name is name of SERVICE_DB_MANIPULATION service
6918 * @box is box identification
6919 * @user identifies user to remove
6920 * @credentials_delivery is NULL if new user's password should be delivered
6921 * off-line to the user. It is valid pointer if user should obtain new
6922 * password on-line on dedicated web server. Then input
6923 * @credentials_delivery.email value is user's e-mail address user must
6924 * provide to dedicated web server together with @credentials_delivery.token.
6925 * The output reallocated token user needs to use to authorize on the web
6926 * server to view his new password. Output reallocated
6927 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6928 * assingned or changed up on this call.
6929 * @approval is optional external approval of box manipulation
6930 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6931 * NULL, if you don't care. */
6932 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6933 struct isds_ctx *context, const xmlChar *service_name,
6934 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6935 struct isds_credentials_delivery *credentials_delivery,
6936 const struct isds_approval *approval, xmlChar **refnumber) {
6937 isds_error err = IE_SUCCESS;
6938 #if HAVE_LIBCURL
6939 xmlNsPtr isds_ns = NULL;
6940 xmlNodePtr request = NULL, node;
6941 xmlDocPtr response = NULL;
6942 #endif
6945 if (!context) return IE_INVALID_CONTEXT;
6946 zfree(context->long_message);
6947 if (credentials_delivery) {
6948 zfree(credentials_delivery->token);
6949 zfree(credentials_delivery->new_user_name);
6951 if (!service_name || service_name[0] == '\0' || !box || !user)
6952 return IE_INVAL;
6955 #if HAVE_LIBCURL
6956 /* Build NewAccessData or similar request */
6957 request = xmlNewNode(NULL, service_name);
6958 if (!request) {
6959 char *service_name_locale = _isds_utf82locale((char *) service_name);
6960 isds_printf_message(context, _("Could not build %s request"),
6961 service_name_locale);
6962 free(service_name_locale);
6963 return IE_ERROR;
6965 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6966 if(!isds_ns) {
6967 isds_log_message(context, _("Could not create ISDS name space"));
6968 xmlFreeNode(request);
6969 return IE_ERROR;
6971 xmlSetNs(request, isds_ns);
6973 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6974 err = insert_DbOwnerInfo(context, box, node);
6975 if (err) goto leave;
6977 INSERT_ELEMENT(node, request, "dbUserInfo");
6978 err = insert_DbUserInfo(context, user, node);
6979 if (err) goto leave;
6981 err = insert_credentials_delivery(context, credentials_delivery, request);
6982 if (err) goto leave;
6984 err = insert_GExtApproval(context, approval, request);
6985 if (err) goto leave;
6988 /* Send request and check response*/
6989 err = send_destroy_request_check_response(context,
6990 SERVICE_DB_MANIPULATION, service_name, &request, &response,
6991 refnumber, NULL);
6993 xmlFreeNode(request);
6994 request = NULL;
6996 /* Pick up credentials_delivery if requested */
6997 err = extract_credentials_delivery(context, credentials_delivery, response,
6998 (char *)service_name);
7000 leave:
7001 xmlFreeDoc(response);
7002 if (request) xmlFreeNode(request);
7004 if (!err) {
7005 char *service_name_locale = _isds_utf82locale((char *) service_name);
7006 isds_log(ILF_ISDS, ILL_DEBUG,
7007 _("%s request processed by server successfully.\n"),
7008 service_name_locale);
7009 free(service_name_locale);
7011 #else /* not HAVE_LIBCURL */
7012 err = IE_NOTSUP;
7013 #endif
7015 return err;
7019 /* Assign new user to given box.
7020 * @context is session context
7021 * @box is box identification
7022 * @user defines new user to add
7023 * @credentials_delivery is NULL if new user's password should be delivered
7024 * off-line to the user. It is valid pointer if user should obtain new
7025 * password on-line on dedicated web server. Then input
7026 * @credentials_delivery.email value is user's e-mail address user must
7027 * provide to dedicated web server together with @credentials_delivery.token.
7028 * The output reallocated token user needs to use to authorize on the web
7029 * server to view his new password. Output reallocated
7030 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7031 * assingned up on this call.
7032 * @approval is optional external approval of box manipulation
7033 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7034 * NULL, if you don't care.*/
7035 isds_error isds_add_user(struct isds_ctx *context,
7036 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7037 struct isds_credentials_delivery *credentials_delivery,
7038 const struct isds_approval *approval, char **refnumber) {
7039 return build_send_manipulationboxuser_request_check_drop_response(context,
7040 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
7041 approval, (xmlChar **) refnumber);
7045 /* Remove user assigned to given box.
7046 * @context is session context
7047 * @box is box identification
7048 * @user identifies user to remove
7049 * @approval is optional external approval of box manipulation
7050 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7051 * NULL, if you don't care.*/
7052 isds_error isds_delete_user(struct isds_ctx *context,
7053 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7054 const struct isds_approval *approval, char **refnumber) {
7055 return build_send_manipulationboxuser_request_check_drop_response(context,
7056 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
7057 (xmlChar **) refnumber);
7061 /* Get list of boxes in ZIP archive.
7062 * @context is session context
7063 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7064 * System recognizes following values currently: ALL (all boxes), UPG
7065 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7066 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7067 * commercial messages). This argument is a string because specification
7068 * states new values can appear in the future. Not all list types are
7069 * available to all users.
7070 * @buffer is automatically reallocated memory to store the list of boxes. The
7071 * list is zipped CSV file.
7072 * @buffer_length is size of @buffer data in bytes.
7073 * In case of error @buffer will be freed and @buffer_length will be
7074 * undefined.*/
7075 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7076 const char *list_identifier, void **buffer, size_t *buffer_length) {
7077 isds_error err = IE_SUCCESS;
7078 #if HAVE_LIBCURL
7079 xmlNsPtr isds_ns = NULL;
7080 xmlNodePtr request = NULL, node;
7081 xmlDocPtr response = NULL;
7082 xmlXPathContextPtr xpath_ctx = NULL;
7083 xmlXPathObjectPtr result = NULL;
7084 char *string = NULL;
7085 #endif
7088 if (!context) return IE_INVALID_CONTEXT;
7089 zfree(context->long_message);
7090 if (buffer) zfree(*buffer);
7091 if (!buffer || !buffer_length) return IE_INVAL;
7094 #if HAVE_LIBCURL
7095 /* Check if connection is established
7096 * TODO: This check should be done downstairs. */
7097 if (!context->curl) return IE_CONNECTION_CLOSED;
7100 /* Build AuthenticateMessage request */
7101 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7102 if (!request) {
7103 isds_log_message(context,
7104 _("Could not build GetDataBoxList request"));
7105 return IE_ERROR;
7107 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7108 if(!isds_ns) {
7109 isds_log_message(context, _("Could not create ISDS name space"));
7110 xmlFreeNode(request);
7111 return IE_ERROR;
7113 xmlSetNs(request, isds_ns);
7114 INSERT_STRING(request, "dblType", list_identifier);
7116 /* Send request to server and process response */
7117 err = send_destroy_request_check_response(context,
7118 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7119 &response, NULL, NULL);
7120 if (err) goto leave;
7123 /* Extract Base-64 encoded ZIP file */
7124 xpath_ctx = xmlXPathNewContext(response);
7125 if (!xpath_ctx) {
7126 err = IE_ERROR;
7127 goto leave;
7129 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7130 err = IE_ERROR;
7131 goto leave;
7133 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7135 /* Decode non-empty archive */
7136 if (string && string[0] != '\0') {
7137 *buffer_length = _isds_b64decode(string, buffer);
7138 if (*buffer_length == (size_t) -1) {
7139 isds_printf_message(context,
7140 _("Error while Base64-decoding box list archive"));
7141 err = IE_ERROR;
7142 goto leave;
7147 leave:
7148 free(string);
7149 xmlXPathFreeObject(result);
7150 xmlXPathFreeContext(xpath_ctx);
7151 xmlFreeDoc(response);
7152 xmlFreeNode(request);
7154 if (!err) {
7155 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7156 "processed by server successfully.\n"));
7158 #else /* not HAVE_LIBCURL */
7159 err = IE_NOTSUP;
7160 #endif
7162 return err;
7166 /* Find boxes suiting given criteria.
7167 * @criteria is filter. You should fill in at least some members.
7168 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7169 * possibly empty. Input NULL or valid old structure.
7170 * @return:
7171 * IE_SUCCESS if search succeeded, @boxes contains useful data
7172 * IE_NOEXIST if no such box exists, @boxes will be NULL
7173 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7174 * contains still valid data
7175 * other code if something bad happens. @boxes will be NULL. */
7176 isds_error isds_FindDataBox(struct isds_ctx *context,
7177 const struct isds_DbOwnerInfo *criteria,
7178 struct isds_list **boxes) {
7179 isds_error err = IE_SUCCESS;
7180 #if HAVE_LIBCURL
7181 _Bool truncated = 0;
7182 xmlNsPtr isds_ns = NULL;
7183 xmlNodePtr request = NULL;
7184 xmlDocPtr response = NULL;
7185 xmlChar *code = NULL, *message = NULL;
7186 xmlNodePtr db_owner_info;
7187 xmlXPathContextPtr xpath_ctx = NULL;
7188 xmlXPathObjectPtr result = NULL;
7189 xmlChar *string = NULL;
7190 #endif
7193 if (!context) return IE_INVALID_CONTEXT;
7194 zfree(context->long_message);
7195 if (!boxes) return IE_INVAL;
7196 isds_list_free(boxes);
7198 if (!criteria) {
7199 return IE_INVAL;
7202 #if HAVE_LIBCURL
7203 /* Check if connection is established
7204 * TODO: This check should be done downstairs. */
7205 if (!context->curl) return IE_CONNECTION_CLOSED;
7208 /* Build FindDataBox request */
7209 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
7210 if (!request) {
7211 isds_log_message(context,
7212 _("Could build FindDataBox request"));
7213 return IE_ERROR;
7215 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7216 if(!isds_ns) {
7217 isds_log_message(context, _("Could not create ISDS name space"));
7218 xmlFreeNode(request);
7219 return IE_ERROR;
7221 xmlSetNs(request, isds_ns);
7222 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7223 if (!db_owner_info) {
7224 isds_log_message(context, _("Could not add dbOwnerInfo child to "
7225 "FindDataBox element"));
7226 xmlFreeNode(request);
7227 return IE_ERROR;
7230 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
7231 if (err) goto leave;
7234 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
7236 /* Sent request */
7237 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7239 /* Destroy request */
7240 xmlFreeNode(request); request = NULL;
7242 if (err) {
7243 isds_log(ILF_ISDS, ILL_DEBUG,
7244 _("Processing ISDS response on FindDataBox "
7245 "request failed\n"));
7246 goto leave;
7249 /* Check for response status */
7250 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7251 &code, &message, NULL);
7252 if (err) {
7253 isds_log(ILF_ISDS, ILL_DEBUG,
7254 _("ISDS response on FindDataBox request is missing status\n"));
7255 goto leave;
7258 /* Request processed, but nothing found */
7259 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7260 !xmlStrcmp(code, BAD_CAST "5001")) {
7261 char *code_locale = _isds_utf82locale((char*)code);
7262 char *message_locale = _isds_utf82locale((char*)message);
7263 isds_log(ILF_ISDS, ILL_DEBUG,
7264 _("Server did not found any box on FindDataBox request "
7265 "(code=%s, message=%s)\n"), code_locale, message_locale);
7266 isds_log_message(context, message_locale);
7267 free(code_locale);
7268 free(message_locale);
7269 err = IE_NOEXIST;
7270 goto leave;
7273 /* Warning, not a error */
7274 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7275 char *code_locale = _isds_utf82locale((char*)code);
7276 char *message_locale = _isds_utf82locale((char*)message);
7277 isds_log(ILF_ISDS, ILL_DEBUG,
7278 _("Server truncated response on FindDataBox request "
7279 "(code=%s, message=%s)\n"), code_locale, message_locale);
7280 isds_log_message(context, message_locale);
7281 free(code_locale);
7282 free(message_locale);
7283 truncated = 1;
7286 /* Other error */
7287 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7288 char *code_locale = _isds_utf82locale((char*)code);
7289 char *message_locale = _isds_utf82locale((char*)message);
7290 isds_log(ILF_ISDS, ILL_DEBUG,
7291 _("Server refused FindDataBox request "
7292 "(code=%s, message=%s)\n"), code_locale, message_locale);
7293 isds_log_message(context, message_locale);
7294 free(code_locale);
7295 free(message_locale);
7296 err = IE_ISDS;
7297 goto leave;
7300 xpath_ctx = xmlXPathNewContext(response);
7301 if (!xpath_ctx) {
7302 err = IE_ERROR;
7303 goto leave;
7305 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7306 err = IE_ERROR;
7307 goto leave;
7310 /* Extract boxes if they present */
7311 result = xmlXPathEvalExpression(BAD_CAST
7312 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7313 xpath_ctx);
7314 if (!result) {
7315 err = IE_ERROR;
7316 goto leave;
7318 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7319 struct isds_list *item, *prev_item = NULL;
7320 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7321 item = calloc(1, sizeof(*item));
7322 if (!item) {
7323 err = IE_NOMEM;
7324 goto leave;
7327 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7328 if (i == 0) *boxes = item;
7329 else prev_item->next = item;
7330 prev_item = item;
7332 xpath_ctx->node = result->nodesetval->nodeTab[i];
7333 err = extract_DbOwnerInfo(context,
7334 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7335 if (err) goto leave;
7339 leave:
7340 if (err) {
7341 isds_list_free(boxes);
7342 } else {
7343 if (truncated) err = IE_2BIG;
7346 free(string);
7347 xmlFreeNode(request);
7348 xmlXPathFreeObject(result);
7349 xmlXPathFreeContext(xpath_ctx);
7351 free(code);
7352 free(message);
7353 xmlFreeDoc(response);
7355 if (!err)
7356 isds_log(ILF_ISDS, ILL_DEBUG,
7357 _("FindDataBox request processed by server successfully.\n"));
7358 #else /* not HAVE_LIBCURL */
7359 err = IE_NOTSUP;
7360 #endif
7362 return err;
7366 #if HAVE_LIBCURL
7367 /* Convert a string with match markers into a plain string with list of
7368 * pointers to the matches
7369 * @string is an UTF-8 encoded non-constant string with match markers
7370 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7371 * The markers will be removed from the string.
7372 * @starts is a reallocated list of static pointers into the @string pointing
7373 * to places where match start markers occured.
7374 * @ends is a reallocated list of static pointers into the @string pointing
7375 * to places where match end markers occured.
7376 * @return IE_SUCCESS in case of no failure. */
7377 static isds_error interpret_matches(xmlChar *string,
7378 struct isds_list **starts, struct isds_list **ends) {
7379 isds_error err = IE_SUCCESS;
7380 xmlChar *pointer, *destination, *source;
7381 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7383 isds_list_free(starts);
7384 isds_list_free(ends);
7385 if (NULL == starts || NULL == ends) return IE_INVAL;
7386 if (NULL == string) return IE_SUCCESS;
7388 for (pointer = string; *pointer != '\0';) {
7389 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7390 /* Remove the start marker */
7391 for (source = pointer + 14, destination = pointer;
7392 *source != '\0'; source++, destination++) {
7393 *destination = *source;
7395 *destination = '\0';
7396 /* Append the pointer into the list */
7397 item = calloc(1, sizeof(*item));
7398 if (!item) {
7399 err = IE_NOMEM;
7400 goto leave;
7402 item->destructor = (void (*)(void **))NULL;
7403 item->data = pointer;
7404 if (NULL == prev_start) *starts = item;
7405 else prev_start->next = item;
7406 prev_start = item;
7407 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7408 /* Remove the end marker */
7409 for (source = pointer + 12, destination = pointer;
7410 *source != '\0'; source++, destination++) {
7411 *destination = *source;
7413 *destination = '\0';
7414 /* Append the pointer into the list */
7415 item = calloc(1, sizeof(*item));
7416 if (!item) {
7417 err = IE_NOMEM;
7418 goto leave;
7420 item->destructor = (void (*)(void **))NULL;
7421 item->data = pointer;
7422 if (NULL == prev_end) *ends = item;
7423 else prev_end->next = item;
7424 prev_end = item;
7425 } else {
7426 pointer++;
7430 leave:
7431 if (err) {
7432 isds_list_free(starts);
7433 isds_list_free(ends);
7435 return err;
7439 /* Convert isds:dbResult XML tree into structure
7440 * @context is ISDS context.
7441 * @fulltext_result is automatically reallocated found box structure.
7442 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7443 * @collect_matches is true to interpret match markers.
7444 * In case of error @result will be freed. */
7445 static isds_error extract_dbResult(struct isds_ctx *context,
7446 struct isds_fulltext_result **fulltext_result,
7447 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7448 isds_error err = IE_SUCCESS;
7449 xmlXPathObjectPtr result = NULL;
7450 char *string = NULL;
7452 if (NULL == context) return IE_INVALID_CONTEXT;
7453 if (NULL == fulltext_result) return IE_INVAL;
7454 isds_fulltext_result_free(fulltext_result);
7455 if (!xpath_ctx) return IE_INVAL;
7458 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7459 if (NULL == *fulltext_result) {
7460 err = IE_NOMEM;
7461 goto leave;
7464 /* Extract data */
7465 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7467 EXTRACT_STRING("isds:dbType", string);
7468 if (NULL == string) {
7469 err = IE_ISDS;
7470 isds_log_message(context, _("Empty isds:dbType element"));
7471 goto leave;
7473 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7474 if (err) {
7475 if (err == IE_ENUM) {
7476 err = IE_ISDS;
7477 char *string_locale = _isds_utf82locale(string);
7478 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7479 string_locale);
7480 free(string_locale);
7482 goto leave;
7484 zfree(string);
7486 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7487 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7489 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7490 if (err) goto leave;
7492 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7493 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7494 (*fulltext_result)->dbEffectiveOVM);
7496 EXTRACT_STRING("isds:dbSendOptions", string);
7497 if (NULL == string) {
7498 err = IE_ISDS;
7499 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7500 goto leave;
7502 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7503 (*fulltext_result)->active = 1;
7504 (*fulltext_result)->public_sending = 1;
7505 (*fulltext_result)->commercial_sending = 0;
7506 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7507 (*fulltext_result)->active = 1;
7508 (*fulltext_result)->public_sending = 1;
7509 (*fulltext_result)->commercial_sending = 1;
7510 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7511 (*fulltext_result)->active = 1;
7512 (*fulltext_result)->public_sending = 0;
7513 (*fulltext_result)->commercial_sending = 1;
7514 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7515 (*fulltext_result)->active = 1;
7516 (*fulltext_result)->public_sending = 0;
7517 (*fulltext_result)->commercial_sending = 0;
7518 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7519 (*fulltext_result)->active = 0;
7520 (*fulltext_result)->public_sending = 0;
7521 (*fulltext_result)->commercial_sending = 0;
7522 } else {
7523 err = IE_ISDS;
7524 char *string_locale = _isds_utf82locale(string);
7525 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7526 string_locale);
7527 free(string_locale);
7528 goto leave;
7530 zfree(string);
7532 /* Interpret match marks */
7533 if (collect_matches) {
7534 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7535 &((*fulltext_result)->name_match_start),
7536 &((*fulltext_result)->name_match_end));
7537 if (err) goto leave;
7538 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7539 &((*fulltext_result)->address_match_start),
7540 &((*fulltext_result)->address_match_end));
7541 if (err) goto leave;
7544 leave:
7545 if (err) isds_fulltext_result_free(fulltext_result);
7546 free(string);
7547 xmlXPathFreeObject(result);
7548 return err;
7550 #endif /* HAVE_LIBCURL */
7553 /* Find boxes matching a given full-text criteria.
7554 * @context is a session context
7555 * @query is a non-empty string which consists of words to search
7556 * @target selects box attributes to search for @query words. Pass NULL if you
7557 * don't care.
7558 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7559 * to search in all box types. Pass NULL to let server to use default value
7560 * which is DBTYPE_SYSTEM.
7561 * @page_size defines count of boxes to constitute a response page. It counts
7562 * from zero. Pass NULL to let server to use a default value (50 now).
7563 * @page_number defines ordinar number of the response page to return. It
7564 * counts from zero. Pass NULL to let server to use a default value (0 now).
7565 * @track_matches points to true for marking @query words found in the box
7566 * attributes. It points to false for not marking. Pass NULL to let the server
7567 * to use default value (false now).
7568 * @total_matching_boxes outputs reallocated number of all boxes matching the
7569 * query. Will be pointer to NULL if server did not provide the value.
7570 * Pass NULL if you don't care.
7571 * @current_page_beginning outputs reallocated ordinar number of the first box
7572 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7573 * server did not provide the value. Pass NULL if you don't care.
7574 * @current_page_size outputs reallocated count of boxes in the this @boxes
7575 * page. It will be pointer to NULL if the server did not provide the value.
7576 * Pass NULL if you don't care.
7577 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7578 * is the last one, false if more boxes match, NULL if the server did not
7579 * provude the value. Pass NULL if you don't care.
7580 * @boxes outputs reallocated list of isds_fulltext_result structures,
7581 * possibly empty.
7582 * @return:
7583 * IE_SUCCESS if search succeeded
7584 * IE_2BIG if @page_size is too large
7585 * other code if something bad happens; output arguments will be NULL. */
7586 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7587 const char *query,
7588 const isds_fulltext_target *target,
7589 const isds_DbType *box_type,
7590 const unsigned long int *page_size,
7591 const unsigned long int *page_number,
7592 const _Bool *track_matches,
7593 unsigned long int **total_matching_boxes,
7594 unsigned long int **current_page_beginning,
7595 unsigned long int **current_page_size,
7596 _Bool **last_page,
7597 struct isds_list **boxes) {
7598 isds_error err = IE_SUCCESS;
7599 #if HAVE_LIBCURL
7600 xmlNsPtr isds_ns = NULL;
7601 xmlNodePtr request = NULL;
7602 xmlDocPtr response = NULL;
7603 xmlNodePtr node;
7604 xmlXPathContextPtr xpath_ctx = NULL;
7605 xmlXPathObjectPtr result = NULL;
7606 const xmlChar *static_string = NULL;
7607 xmlChar *string = NULL;
7609 const xmlChar *codes[] = {
7610 BAD_CAST "1004",
7611 BAD_CAST "1152",
7612 BAD_CAST "1153",
7613 BAD_CAST "1154",
7614 BAD_CAST "1155",
7615 BAD_CAST "1156",
7616 BAD_CAST "9002",
7617 NULL
7619 const char *meanings[] = {
7620 N_("You are not allowed to perform the search"),
7621 N_("The query string is empty"),
7622 N_("Searched box ID is malformed"),
7623 N_("Searched organization ID is malformed"),
7624 N_("Invalid input"),
7625 N_("Requested page size is too large"),
7626 N_("Search engine internal error")
7628 const isds_error errors[] = {
7629 IE_ISDS,
7630 IE_INVAL,
7631 IE_INVAL,
7632 IE_INVAL,
7633 IE_INVAL,
7634 IE_2BIG,
7635 IE_ISDS
7637 struct code_map_isds_error map = {
7638 .codes = codes,
7639 .meanings = meanings,
7640 .errors = errors
7642 #endif
7645 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7646 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7647 if (NULL != current_page_size) zfree(*current_page_size);
7648 if (NULL != last_page) zfree(*last_page);
7649 isds_list_free(boxes);
7651 if (NULL == context) return IE_INVALID_CONTEXT;
7652 zfree(context->long_message);
7654 if (NULL == boxes) return IE_INVAL;
7656 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
7657 isds_log_message(context, _("Query string must be non-empty"));
7658 return IE_INVAL;
7661 #if HAVE_LIBCURL
7662 /* Check if connection is established
7663 * TODO: This check should be done downstairs. */
7664 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7666 /* Build FindDataBox request */
7667 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
7668 if (NULL == request) {
7669 isds_log_message(context,
7670 _("Could not build ISDSSearch2 request"));
7671 return IE_ERROR;
7673 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7674 if(NULL == isds_ns) {
7675 isds_log_message(context, _("Could not create ISDS name space"));
7676 xmlFreeNode(request);
7677 return IE_ERROR;
7679 xmlSetNs(request, isds_ns);
7681 INSERT_STRING(request, "searchText", query);
7683 if (NULL != target) {
7684 static_string = isds_fulltext_target2string(*(target));
7685 if (NULL == static_string) {
7686 isds_printf_message(context, _("Invalid target value: %d"),
7687 *(target));
7688 err = IE_ENUM;
7689 goto leave;
7692 INSERT_STRING(request, "searchType", static_string);
7693 static_string = NULL;
7695 if (NULL != box_type) {
7696 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7697 if (DBTYPE_SYSTEM == *box_type) {
7698 static_string = BAD_CAST "ALL";
7699 } else {
7700 static_string = isds_DbType2string(*(box_type));
7701 if (NULL == static_string) {
7702 isds_printf_message(context, _("Invalid box type value: %d"),
7703 *(box_type));
7704 err = IE_ENUM;
7705 goto leave;
7709 INSERT_STRING(request, "searchScope", static_string);
7710 static_string = NULL;
7712 INSERT_ULONGINT(request, "page", page_number, string);
7713 INSERT_ULONGINT(request, "pageSize", page_size, string);
7714 INSERT_BOOLEAN(request, "highlighting", track_matches);
7716 /* Send request and check response */
7717 err = send_destroy_request_check_response(context,
7718 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
7719 &request, &response, NULL, &map);
7720 if (err) goto leave;
7722 /* Parse response */
7723 xpath_ctx = xmlXPathNewContext(response);
7724 if (NULL == xpath_ctx) {
7725 err = IE_ERROR;
7726 goto leave;
7728 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7729 err = IE_ERROR;
7730 goto leave;
7732 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
7733 xpath_ctx);
7734 if (!result) {
7735 err = IE_ERROR;
7736 goto leave;
7738 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7739 isds_log_message(context, _("Missing ISDSSearch2 element"));
7740 err = IE_ISDS;
7741 goto leave;
7743 if (result->nodesetval->nodeNr > 1) {
7744 isds_log_message(context, _("Multiple ISDSSearch2 element"));
7745 err = IE_ISDS;
7746 goto leave;
7748 xpath_ctx->node = result->nodesetval->nodeTab[0];
7749 xmlXPathFreeObject(result); result = NULL;
7752 /* Extract counters */
7753 if (NULL != total_matching_boxes) {
7754 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
7756 if (NULL != current_page_size) {
7757 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
7759 if (NULL != current_page_beginning) {
7760 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
7762 if (NULL != last_page) {
7763 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
7765 xmlXPathFreeObject(result); result = NULL;
7767 /* Extract boxes if they present */
7768 result = xmlXPathEvalExpression(BAD_CAST
7769 "isds:dbResults/isds:dbResult", xpath_ctx);
7770 if (NULL == result) {
7771 err = IE_ERROR;
7772 goto leave;
7774 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7775 struct isds_list *item, *prev_item = NULL;
7776 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7777 item = calloc(1, sizeof(*item));
7778 if (!item) {
7779 err = IE_NOMEM;
7780 goto leave;
7783 item->destructor = (void (*)(void **))isds_fulltext_result_free;
7784 if (i == 0) *boxes = item;
7785 else prev_item->next = item;
7786 prev_item = item;
7788 xpath_ctx->node = result->nodesetval->nodeTab[i];
7789 err = extract_dbResult(context,
7790 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
7791 (NULL == track_matches) ? 0 : *track_matches);
7792 if (err) goto leave;
7796 leave:
7797 if (err) {
7798 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7799 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7800 if (NULL != current_page_size) zfree(*current_page_size);
7801 if (NULL != last_page) zfree(*last_page);
7802 isds_list_free(boxes);
7805 free(string);
7806 xmlFreeNode(request);
7807 xmlXPathFreeObject(result);
7808 xmlXPathFreeContext(xpath_ctx);
7809 xmlFreeDoc(response);
7811 if (!err)
7812 isds_log(ILF_ISDS, ILL_DEBUG,
7813 _("ISDSSearch2 request processed by server successfully.\n"));
7814 #else /* not HAVE_LIBCURL */
7815 err = IE_NOTSUP;
7816 #endif
7818 return err;
7822 /* Get status of a box.
7823 * @context is ISDS session context.
7824 * @box_id is UTF-8 encoded box identifier as zero terminated string
7825 * @box_status is return value of box status.
7826 * @return:
7827 * IE_SUCCESS if box has been found and its status retrieved
7828 * IE_NOEXIST if box is not known to ISDS server
7829 * or other appropriate error.
7830 * You can use isds_DbState to enumerate box status. However out of enum
7831 * range value can be returned too. This is feature because ISDS
7832 * specification leaves the set of values open.
7833 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7834 * the box has been deleted, but ISDS still lists its former existence. */
7835 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7836 long int *box_status) {
7837 isds_error err = IE_SUCCESS;
7838 #if HAVE_LIBCURL
7839 xmlNsPtr isds_ns = NULL;
7840 xmlNodePtr request = NULL, db_id;
7841 xmlDocPtr response = NULL;
7842 xmlXPathContextPtr xpath_ctx = NULL;
7843 xmlXPathObjectPtr result = NULL;
7844 xmlChar *string = NULL;
7846 const xmlChar *codes[] = {
7847 BAD_CAST "5001",
7848 BAD_CAST "1007",
7849 BAD_CAST "2011",
7850 NULL
7852 const char *meanings[] = {
7853 "The box does not exist",
7854 "Box ID is malformed",
7855 "Box ID malformed",
7857 const isds_error errors[] = {
7858 IE_NOEXIST,
7859 IE_INVAL,
7860 IE_INVAL,
7862 struct code_map_isds_error map = {
7863 .codes = codes,
7864 .meanings = meanings,
7865 .errors = errors
7867 #endif
7869 if (!context) return IE_INVALID_CONTEXT;
7870 zfree(context->long_message);
7871 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7873 #if HAVE_LIBCURL
7874 /* Check if connection is established
7875 * TODO: This check should be done downstairs. */
7876 if (!context->curl) return IE_CONNECTION_CLOSED;
7879 /* Build CheckDataBox request */
7880 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7881 if (!request) {
7882 isds_log_message(context,
7883 _("Could build CheckDataBox request"));
7884 return IE_ERROR;
7886 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7887 if(!isds_ns) {
7888 isds_log_message(context, _("Could not create ISDS name space"));
7889 xmlFreeNode(request);
7890 return IE_ERROR;
7892 xmlSetNs(request, isds_ns);
7893 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7894 if (!db_id) {
7895 isds_log_message(context, _("Could not add dbID child to "
7896 "CheckDataBox element"));
7897 xmlFreeNode(request);
7898 return IE_ERROR;
7902 /* Send request and check response*/
7903 err = send_destroy_request_check_response(context,
7904 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7905 &request, &response, NULL, &map);
7906 if (err) goto leave;
7909 /* Extract data */
7910 xpath_ctx = xmlXPathNewContext(response);
7911 if (!xpath_ctx) {
7912 err = IE_ERROR;
7913 goto leave;
7915 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7916 err = IE_ERROR;
7917 goto leave;
7919 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7920 xpath_ctx);
7921 if (!result) {
7922 err = IE_ERROR;
7923 goto leave;
7925 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7926 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7927 err = IE_ISDS;
7928 goto leave;
7930 if (result->nodesetval->nodeNr > 1) {
7931 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7932 err = IE_ISDS;
7933 goto leave;
7935 xpath_ctx->node = result->nodesetval->nodeTab[0];
7936 xmlXPathFreeObject(result); result = NULL;
7938 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7941 leave:
7942 free(string);
7943 xmlXPathFreeObject(result);
7944 xmlXPathFreeContext(xpath_ctx);
7946 xmlFreeDoc(response);
7948 if (!err)
7949 isds_log(ILF_ISDS, ILL_DEBUG,
7950 _("CheckDataBox request processed by server successfully.\n"));
7951 #else /* not HAVE_LIBCURL */
7952 err = IE_NOTSUP;
7953 #endif
7955 return err;
7959 /* Get list of permissions to send commercial messages.
7960 * @context is ISDS session context.
7961 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7962 * @permissions is a reallocated list of permissions (struct
7963 * isds_commercial_permission*) to send commercial messages from @box_id. The
7964 * order of permissions is significant as the server applies the permissions
7965 * and associated pre-paid credits in the order. Empty list means no
7966 * permission.
7967 * @return:
7968 * IE_SUCCESS if the list has been obtained correctly,
7969 * or other appropriate error. */
7970 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7971 const char *box_id, struct isds_list **permissions) {
7972 isds_error err = IE_SUCCESS;
7973 #if HAVE_LIBCURL
7974 xmlDocPtr response = NULL;
7975 xmlXPathContextPtr xpath_ctx = NULL;
7976 xmlXPathObjectPtr result = NULL;
7977 #endif
7979 if (!context) return IE_INVALID_CONTEXT;
7980 zfree(context->long_message);
7981 if (NULL == permissions) return IE_INVAL;
7982 isds_list_free(permissions);
7983 if (NULL == box_id) return IE_INVAL;
7985 #if HAVE_LIBCURL
7986 /* Check if connection is established */
7987 if (!context->curl) return IE_CONNECTION_CLOSED;
7989 /* Do request and check for success */
7990 err = build_send_dbid_request_check_response(context,
7991 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7992 BAD_CAST box_id, NULL, &response, NULL);
7993 if (!err) {
7994 isds_log(ILF_ISDS, ILL_DEBUG,
7995 _("PDZInfo request processed by server successfully.\n"));
7998 /* Extract data */
7999 /* Prepare structure */
8000 xpath_ctx = xmlXPathNewContext(response);
8001 if (!xpath_ctx) {
8002 err = IE_ERROR;
8003 goto leave;
8005 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8006 err = IE_ERROR;
8007 goto leave;
8010 /* Set context node */
8011 result = xmlXPathEvalExpression(BAD_CAST
8012 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8013 xpath_ctx);
8014 if (!result) {
8015 err = IE_ERROR;
8016 goto leave;
8018 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8019 struct isds_list *prev_item = NULL;
8021 /* Iterate over all permission records */
8022 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8023 struct isds_list *item;
8025 /* Prepare structure */
8026 item = calloc(1, sizeof(*item));
8027 if (!item) {
8028 err = IE_NOMEM;
8029 goto leave;
8031 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8032 if (i == 0) *permissions = item;
8033 else prev_item->next = item;
8034 prev_item = item;
8036 /* Extract it */
8037 xpath_ctx->node = result->nodesetval->nodeTab[i];
8038 err = extract_DbPDZRecord(context,
8039 (struct isds_commercial_permission **) (&item->data),
8040 xpath_ctx);
8041 if (err) goto leave;
8045 leave:
8046 if (err) {
8047 isds_list_free(permissions);
8050 xmlXPathFreeObject(result);
8051 xmlXPathFreeContext(xpath_ctx);
8052 xmlFreeDoc(response);
8054 #else /* not HAVE_LIBCURL */
8055 err = IE_NOTSUP;
8056 #endif
8058 return err;
8062 /* Get details about credit for sending pre-paid commercial messages.
8063 * @context is ISDS session context.
8064 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8065 * @from_date is first day of credit history to return in @history. Only
8066 * tm_year, tm_mon and tm_mday carry sane value.
8067 * @to_date is last day of credit history to return in @history. Only
8068 * tm_year, tm_mon and tm_mday carry sane value.
8069 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8070 * if you don't care. This and all other credit values are integers in
8071 * hundredths of Czech Crowns.
8072 * @email outputs notification e-mail address where notifications about credit
8073 * are sent. This is automatically reallocated string. Pass NULL if you don't
8074 * care. It can return NULL if no address is defined.
8075 * @history outputs auto-reallocated list of pointers to struct
8076 * isds_credit_event. Events in closed interval @from_time to @to_time are
8077 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8078 * are sorted by time.
8079 * @return:
8080 * IE_SUCCESS if the credit details have been obtained correctly,
8081 * or other appropriate error. Please note that server allows to retrieve
8082 * only limited history of events. */
8083 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8084 const char *box_id,
8085 const struct tm *from_date, const struct tm *to_date,
8086 long int *credit, char **email, struct isds_list **history) {
8087 isds_error err = IE_SUCCESS;
8088 #if HAVE_LIBCURL
8089 char *box_id_locale = NULL;
8090 xmlNodePtr request = NULL, node;
8091 xmlNsPtr isds_ns = NULL;
8092 xmlChar *string = NULL;
8094 xmlDocPtr response = NULL;
8095 xmlXPathContextPtr xpath_ctx = NULL;
8096 xmlXPathObjectPtr result = NULL;
8098 const xmlChar *codes[] = {
8099 BAD_CAST "1004",
8100 BAD_CAST "2011",
8101 BAD_CAST "1093",
8102 BAD_CAST "1137",
8103 BAD_CAST "1058",
8104 NULL
8106 const char *meanings[] = {
8107 "Insufficient priviledges for the box",
8108 "The box does not exist",
8109 "Date is too long (history is not available after 15 months)",
8110 "Interval is too long (limit is 3 months)",
8111 "Invalid date"
8113 const isds_error errors[] = {
8114 IE_ISDS,
8115 IE_NOEXIST,
8116 IE_DATE,
8117 IE_DATE,
8118 IE_DATE,
8120 struct code_map_isds_error map = {
8121 .codes = codes,
8122 .meanings = meanings,
8123 .errors = errors
8125 #endif
8127 if (!context) return IE_INVALID_CONTEXT;
8128 zfree(context->long_message);
8130 /* Free output argument */
8131 if (NULL != credit) *credit = 0;
8132 if (NULL != email) zfree(*email);
8133 isds_list_free(history);
8135 if (NULL == box_id) return IE_INVAL;
8137 #if HAVE_LIBCURL
8138 /* Check if connection is established */
8139 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8141 box_id_locale = _isds_utf82locale((char*)box_id);
8142 if (NULL == box_id_locale) {
8143 err = IE_NOMEM;
8144 goto leave;
8147 /* Build request */
8148 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8149 if (NULL == request) {
8150 isds_printf_message(context,
8151 _("Could not build DataBoxCreditInfo request for %s box"),
8152 box_id_locale);
8153 err = IE_ERROR;
8154 goto leave;
8156 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8157 if(!isds_ns) {
8158 isds_log_message(context, _("Could not create ISDS name space"));
8159 err = IE_ERROR;
8160 goto leave;
8162 xmlSetNs(request, isds_ns);
8164 /* Add mandatory XSD:tIdDbInput child */
8165 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8166 /* Add mandatory dates elements with optional values */
8167 if (from_date) {
8168 err = tm2datestring(from_date, &string);
8169 if (err) {
8170 isds_log_message(context,
8171 _("Could not convert `from_date' argument to ISO date "
8172 "string"));
8173 goto leave;
8175 INSERT_STRING(request, "ciFromDate", string);
8176 zfree(string);
8177 } else {
8178 INSERT_STRING(request, "ciFromDate", NULL);
8180 if (to_date) {
8181 err = tm2datestring(to_date, &string);
8182 if (err) {
8183 isds_log_message(context,
8184 _("Could not convert `to_date' argument to ISO date "
8185 "string"));
8186 goto leave;
8188 INSERT_STRING(request, "ciTodate", string);
8189 zfree(string);
8190 } else {
8191 INSERT_STRING(request, "ciTodate", NULL);
8194 /* Send request and check response*/
8195 err = send_destroy_request_check_response(context,
8196 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8197 &request, &response, NULL, &map);
8198 if (err) goto leave;
8201 /* Extract data */
8202 /* Set context to the root */
8203 xpath_ctx = xmlXPathNewContext(response);
8204 if (!xpath_ctx) {
8205 err = IE_ERROR;
8206 goto leave;
8208 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8209 err = IE_ERROR;
8210 goto leave;
8212 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8213 xpath_ctx);
8214 if (!result) {
8215 err = IE_ERROR;
8216 goto leave;
8218 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8219 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8220 err = IE_ISDS;
8221 goto leave;
8223 if (result->nodesetval->nodeNr > 1) {
8224 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8225 err = IE_ISDS;
8226 goto leave;
8228 xpath_ctx->node = result->nodesetval->nodeTab[0];
8229 xmlXPathFreeObject(result); result = NULL;
8231 /* Extract common data */
8232 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8233 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8235 /* Extract records */
8236 if (NULL == history) goto leave;
8237 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8238 xpath_ctx);
8239 if (!result) {
8240 err = IE_ERROR;
8241 goto leave;
8243 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8244 struct isds_list *prev_item = NULL;
8246 /* Iterate over all records */
8247 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8248 struct isds_list *item;
8250 /* Prepare structure */
8251 item = calloc(1, sizeof(*item));
8252 if (!item) {
8253 err = IE_NOMEM;
8254 goto leave;
8256 item->destructor = (void(*)(void**))isds_credit_event_free;
8257 if (i == 0) *history = item;
8258 else prev_item->next = item;
8259 prev_item = item;
8261 /* Extract it */
8262 xpath_ctx->node = result->nodesetval->nodeTab[i];
8263 err = extract_CiRecord(context,
8264 (struct isds_credit_event **) (&item->data),
8265 xpath_ctx);
8266 if (err) goto leave;
8270 leave:
8271 if (!err) {
8272 isds_log(ILF_ISDS, ILL_DEBUG,
8273 _("DataBoxCreditInfo request processed by server successfully.\n"));
8275 if (err) {
8276 isds_list_free(history);
8277 if (NULL != email) zfree(*email)
8280 free(box_id_locale);
8281 xmlXPathFreeObject(result);
8282 xmlXPathFreeContext(xpath_ctx);
8283 xmlFreeDoc(response);
8285 #else /* not HAVE_LIBCURL */
8286 err = IE_NOTSUP;
8287 #endif
8289 return err;
8293 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8294 * code, destroy response and log success.
8295 * @context is ISDS session context.
8296 * @service_name is name of SERVICE_DB_MANIPULATION service
8297 * @box_id is UTF-8 encoded box identifier as zero terminated string
8298 * @approval is optional external approval of box manipulation
8299 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8300 * NULL, if you don't care. */
8301 static isds_error build_send_manipulationdbid_request_check_drop_response(
8302 struct isds_ctx *context, const xmlChar *service_name,
8303 const xmlChar *box_id, const struct isds_approval *approval,
8304 xmlChar **refnumber) {
8305 isds_error err = IE_SUCCESS;
8306 #if HAVE_LIBCURL
8307 xmlDocPtr response = NULL;
8308 #endif
8310 if (!context) return IE_INVALID_CONTEXT;
8311 zfree(context->long_message);
8312 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8314 #if HAVE_LIBCURL
8315 /* Check if connection is established */
8316 if (!context->curl) return IE_CONNECTION_CLOSED;
8318 /* Do request and check for success */
8319 err = build_send_dbid_request_check_response(context,
8320 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8321 &response, refnumber);
8322 xmlFreeDoc(response);
8324 if (!err) {
8325 char *service_name_locale = _isds_utf82locale((char *) service_name);
8326 isds_log(ILF_ISDS, ILL_DEBUG,
8327 _("%s request processed by server successfully.\n"),
8328 service_name_locale);
8329 free(service_name_locale);
8331 #else /* not HAVE_LIBCURL */
8332 err = IE_NOTSUP;
8333 #endif
8335 return err;
8339 /* Switch box into state where box can receive commercial messages (off by
8340 * default)
8341 * @context is ISDS session context.
8342 * @box_id is UTF-8 encoded box identifier as zero terminated string
8343 * @allow is true for enable, false for disable commercial messages income
8344 * @approval is optional external approval of box manipulation
8345 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8346 * NULL, if you don't care. */
8347 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8348 const char *box_id, const _Bool allow,
8349 const struct isds_approval *approval, char **refnumber) {
8350 return build_send_manipulationdbid_request_check_drop_response(context,
8351 (allow) ? BAD_CAST "SetOpenAddressing" :
8352 BAD_CAST "ClearOpenAddressing",
8353 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8357 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8358 * message acceptance). This is just a box permission. Sender must apply
8359 * such role by sending each message.
8360 * @context is ISDS session context.
8361 * @box_id is UTF-8 encoded box identifier as zero terminated string
8362 * @allow is true for enable, false for disable OVM role permission
8363 * @approval is optional external approval of box manipulation
8364 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8365 * NULL, if you don't care. */
8366 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
8367 const char *box_id, const _Bool allow,
8368 const struct isds_approval *approval, char **refnumber) {
8369 return build_send_manipulationdbid_request_check_drop_response(context,
8370 (allow) ? BAD_CAST "SetEffectiveOVM" :
8371 BAD_CAST "ClearEffectiveOVM",
8372 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8376 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8377 * code, destroy response and log success.
8378 * @context is ISDS session context.
8379 * @service_name is name of SERVICE_DB_MANIPULATION service
8380 * @owner is structure describing box
8381 * @approval is optional external approval of box manipulation
8382 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8383 * NULL, if you don't care. */
8384 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8385 struct isds_ctx *context, const xmlChar *service_name,
8386 const struct isds_DbOwnerInfo *owner,
8387 const struct isds_approval *approval, xmlChar **refnumber) {
8388 isds_error err = IE_SUCCESS;
8389 #if HAVE_LIBCURL
8390 char *service_name_locale = NULL;
8391 xmlNodePtr request = NULL, db_owner_info;
8392 xmlNsPtr isds_ns = NULL;
8393 #endif
8396 if (!context) return IE_INVALID_CONTEXT;
8397 zfree(context->long_message);
8398 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8400 #if HAVE_LIBCURL
8401 service_name_locale = _isds_utf82locale((char*)service_name);
8402 if (!service_name_locale) {
8403 err = IE_NOMEM;
8404 goto leave;
8407 /* Build request */
8408 request = xmlNewNode(NULL, service_name);
8409 if (!request) {
8410 isds_printf_message(context,
8411 _("Could not build %s request"), service_name_locale);
8412 err = IE_ERROR;
8413 goto leave;
8415 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8416 if(!isds_ns) {
8417 isds_log_message(context, _("Could not create ISDS name space"));
8418 err = IE_ERROR;
8419 goto leave;
8421 xmlSetNs(request, isds_ns);
8424 /* Add XSD:tOwnerInfoInput child*/
8425 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8426 err = insert_DbOwnerInfo(context, owner, db_owner_info);
8427 if (err) goto leave;
8429 /* Add XSD:gExtApproval*/
8430 err = insert_GExtApproval(context, approval, request);
8431 if (err) goto leave;
8433 /* Send it to server and process response */
8434 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8435 service_name, &request, refnumber);
8437 leave:
8438 xmlFreeNode(request);
8439 free(service_name_locale);
8440 #else /* not HAVE_LIBCURL */
8441 err = IE_NOTSUP;
8442 #endif
8444 return err;
8448 /* Switch box accessibility state on request of box owner.
8449 * Despite the name, owner must do the request off-line. This function is
8450 * designed for such off-line meeting points (e.g. Czech POINT).
8451 * @context is ISDS session context.
8452 * @box identifies box to switch accessibility state.
8453 * @allow is true for making accessible, false to disallow access.
8454 * @approval is optional external approval of box manipulation
8455 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8456 * NULL, if you don't care. */
8457 isds_error isds_switch_box_accessibility_on_owner_request(
8458 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8459 const _Bool allow, const struct isds_approval *approval,
8460 char **refnumber) {
8461 return build_send_manipulationdbowner_request_check_drop_response(context,
8462 (allow) ? BAD_CAST "EnableOwnDataBox" :
8463 BAD_CAST "DisableOwnDataBox",
8464 box, approval, (xmlChar **) refnumber);
8468 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8469 * date.
8470 * @context is ISDS session context.
8471 * @box identifies box to switch accessibility state.
8472 * @since is date since accessibility has been denied. This can be past too.
8473 * Only tm_year, tm_mon and tm_mday carry sane value.
8474 * @approval is optional external approval of box manipulation
8475 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8476 * NULL, if you don't care. */
8477 isds_error isds_disable_box_accessibility_externaly(
8478 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8479 const struct tm *since, const struct isds_approval *approval,
8480 char **refnumber) {
8481 isds_error err = IE_SUCCESS;
8482 #if HAVE_LIBCURL
8483 char *service_name_locale = NULL;
8484 xmlNodePtr request = NULL, node;
8485 xmlNsPtr isds_ns = NULL;
8486 xmlChar *string = NULL;
8487 #endif
8490 if (!context) return IE_INVALID_CONTEXT;
8491 zfree(context->long_message);
8492 if (!box || !since) return IE_INVAL;
8494 #if HAVE_LIBCURL
8495 /* Build request */
8496 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
8497 if (!request) {
8498 isds_printf_message(context,
8499 _("Could not build %s request"), "DisableDataBoxExternally");
8500 err = IE_ERROR;
8501 goto leave;
8503 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8504 if(!isds_ns) {
8505 isds_log_message(context, _("Could not create ISDS name space"));
8506 err = IE_ERROR;
8507 goto leave;
8509 xmlSetNs(request, isds_ns);
8512 /* Add @box identification */
8513 INSERT_ELEMENT(node, request, "dbOwnerInfo");
8514 err = insert_DbOwnerInfo(context, box, node);
8515 if (err) goto leave;
8517 /* Add @since date */
8518 err = tm2datestring(since, &string);
8519 if(err) {
8520 isds_log_message(context,
8521 _("Could not convert `since' argument to ISO date string"));
8522 goto leave;
8524 INSERT_STRING(request, "dbOwnerDisableDate", string);
8525 zfree(string);
8527 /* Add @approval */
8528 err = insert_GExtApproval(context, approval, request);
8529 if (err) goto leave;
8531 /* Send it to server and process response */
8532 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8533 BAD_CAST "DisableDataBoxExternally", &request,
8534 (xmlChar **) refnumber);
8536 leave:
8537 free(string);
8538 xmlFreeNode(request);
8539 free(service_name_locale);
8540 #else /* not HAVE_LIBCURL */
8541 err = IE_NOTSUP;
8542 #endif
8544 return err;
8548 #if HAVE_LIBCURL
8549 /* Insert struct isds_message data (envelope (recipient data optional) and
8550 * documents into XML tree
8551 * @context is session context
8552 * @outgoing_message is libisds structure with message data
8553 * @create_message is XML CreateMessage or CreateMultipleMessage element
8554 * @process_recipient true for recipient data serialization, false for no
8555 * serialization */
8556 static isds_error insert_envelope_files(struct isds_ctx *context,
8557 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8558 const _Bool process_recipient) {
8560 isds_error err = IE_SUCCESS;
8561 xmlNodePtr envelope, dm_files, node;
8562 xmlChar *string = NULL;
8564 if (!context) return IE_INVALID_CONTEXT;
8565 if (!outgoing_message || !create_message) return IE_INVAL;
8568 /* Build envelope */
8569 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8570 if (!envelope) {
8571 isds_printf_message(context, _("Could not add dmEnvelope child to "
8572 "%s element"), create_message->name);
8573 return IE_ERROR;
8576 if (!outgoing_message->envelope) {
8577 isds_log_message(context, _("Outgoing message is missing envelope"));
8578 err = IE_INVAL;
8579 goto leave;
8582 /* Insert optional message type */
8583 err = insert_message_type(context, outgoing_message->envelope->dmType,
8584 envelope);
8585 if (err) goto leave;
8587 INSERT_STRING(envelope, "dmSenderOrgUnit",
8588 outgoing_message->envelope->dmSenderOrgUnit);
8589 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8590 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8592 if (process_recipient) {
8593 if (!outgoing_message->envelope->dbIDRecipient) {
8594 isds_log_message(context,
8595 _("Outgoing message is missing recipient box identifier"));
8596 err = IE_INVAL;
8597 goto leave;
8599 INSERT_STRING(envelope, "dbIDRecipient",
8600 outgoing_message->envelope->dbIDRecipient);
8602 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8603 outgoing_message->envelope->dmRecipientOrgUnit);
8604 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8605 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8606 INSERT_STRING(envelope, "dmToHands",
8607 outgoing_message->envelope->dmToHands);
8610 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8611 "dmAnnotation");
8612 INSERT_STRING(envelope, "dmAnnotation",
8613 outgoing_message->envelope->dmAnnotation);
8615 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8616 0, 50, "dmRecipientRefNumber");
8617 INSERT_STRING(envelope, "dmRecipientRefNumber",
8618 outgoing_message->envelope->dmRecipientRefNumber);
8620 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8621 0, 50, "dmSenderRefNumber");
8622 INSERT_STRING(envelope, "dmSenderRefNumber",
8623 outgoing_message->envelope->dmSenderRefNumber);
8625 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8626 0, 50, "dmRecipientIdent");
8627 INSERT_STRING(envelope, "dmRecipientIdent",
8628 outgoing_message->envelope->dmRecipientIdent);
8630 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8631 0, 50, "dmSenderIdent");
8632 INSERT_STRING(envelope, "dmSenderIdent",
8633 outgoing_message->envelope->dmSenderIdent);
8635 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8636 outgoing_message->envelope->dmLegalTitleLaw, string);
8637 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8638 outgoing_message->envelope->dmLegalTitleYear, string);
8639 INSERT_STRING(envelope, "dmLegalTitleSect",
8640 outgoing_message->envelope->dmLegalTitleSect);
8641 INSERT_STRING(envelope, "dmLegalTitlePar",
8642 outgoing_message->envelope->dmLegalTitlePar);
8643 INSERT_STRING(envelope, "dmLegalTitlePoint",
8644 outgoing_message->envelope->dmLegalTitlePoint);
8646 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8647 outgoing_message->envelope->dmPersonalDelivery);
8648 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8649 outgoing_message->envelope->dmAllowSubstDelivery);
8651 /* ???: Should we require value for dbEffectiveOVM sender?
8652 * ISDS has default as true */
8653 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8654 INSERT_BOOLEAN(envelope, "dmOVM",
8655 outgoing_message->envelope->dmPublishOwnID);
8658 /* Append dmFiles */
8659 if (!outgoing_message->documents) {
8660 isds_log_message(context,
8661 _("Outgoing message is missing list of documents"));
8662 err = IE_INVAL;
8663 goto leave;
8665 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8666 if (!dm_files) {
8667 isds_printf_message(context, _("Could not add dmFiles child to "
8668 "%s element"), create_message->name);
8669 err = IE_ERROR;
8670 goto leave;
8673 /* Check for document hierarchy */
8674 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8675 if (err) goto leave;
8677 /* Process each document */
8678 for (struct isds_list *item =
8679 (struct isds_list *) outgoing_message->documents;
8680 item; item = item->next) {
8681 if (!item->data) {
8682 isds_log_message(context,
8683 _("List of documents contains empty item"));
8684 err = IE_INVAL;
8685 goto leave;
8687 /* FIXME: Check for dmFileMetaType and for document references.
8688 * Only first document can be of MAIN type */
8689 err = insert_document(context, (struct isds_document*) item->data,
8690 dm_files);
8692 if (err) goto leave;
8695 leave:
8696 free(string);
8697 return err;
8699 #endif /* HAVE_LIBCURL */
8702 /* Send a message via ISDS to a recipient
8703 * @context is session context
8704 * @outgoing_message is message to send; Some members are mandatory (like
8705 * dbIDRecipient), some are optional and some are irrelevant (especially data
8706 * about sender). Included pointer to isds_list documents must contain at
8707 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8708 * members will be filled with valid data from ISDS. Exact list of write
8709 * members is subject to change. Currently dmID is changed.
8710 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8711 isds_error isds_send_message(struct isds_ctx *context,
8712 struct isds_message *outgoing_message) {
8714 isds_error err = IE_SUCCESS;
8715 #if HAVE_LIBCURL
8716 xmlNsPtr isds_ns = NULL;
8717 xmlNodePtr request = NULL;
8718 xmlDocPtr response = NULL;
8719 xmlChar *code = NULL, *message = NULL;
8720 xmlXPathContextPtr xpath_ctx = NULL;
8721 xmlXPathObjectPtr result = NULL;
8722 /*_Bool message_is_complete = 0;*/
8723 #endif
8725 if (!context) return IE_INVALID_CONTEXT;
8726 zfree(context->long_message);
8727 if (!outgoing_message) return IE_INVAL;
8729 #if HAVE_LIBCURL
8730 /* Check if connection is established
8731 * TODO: This check should be done downstairs. */
8732 if (!context->curl) return IE_CONNECTION_CLOSED;
8735 /* Build CreateMessage request */
8736 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8737 if (!request) {
8738 isds_log_message(context,
8739 _("Could not build CreateMessage request"));
8740 return IE_ERROR;
8742 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8743 if(!isds_ns) {
8744 isds_log_message(context, _("Could not create ISDS name space"));
8745 xmlFreeNode(request);
8746 return IE_ERROR;
8748 xmlSetNs(request, isds_ns);
8750 /* Append envelope and files */
8751 err = insert_envelope_files(context, outgoing_message, request, 1);
8752 if (err) goto leave;
8755 /* Signal we can serialize message since now */
8756 /*message_is_complete = 1;*/
8759 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8761 /* Sent request */
8762 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8764 /* Don't' destroy request, we want to provide it to application later */
8766 if (err) {
8767 isds_log(ILF_ISDS, ILL_DEBUG,
8768 _("Processing ISDS response on CreateMessage "
8769 "request failed\n"));
8770 goto leave;
8773 /* Check for response status */
8774 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8775 &code, &message, NULL);
8776 if (err) {
8777 isds_log(ILF_ISDS, ILL_DEBUG,
8778 _("ISDS response on CreateMessage request "
8779 "is missing status\n"));
8780 goto leave;
8783 /* Request processed, but refused by server or server failed */
8784 if (xmlStrcmp(code, BAD_CAST "0000")) {
8785 char *box_id_locale =
8786 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8787 char *code_locale = _isds_utf82locale((char*)code);
8788 char *message_locale = _isds_utf82locale((char*)message);
8789 isds_log(ILF_ISDS, ILL_DEBUG,
8790 _("Server did not accept message for %s on CreateMessage "
8791 "request (code=%s, message=%s)\n"),
8792 box_id_locale, code_locale, message_locale);
8793 isds_log_message(context, message_locale);
8794 free(box_id_locale);
8795 free(code_locale);
8796 free(message_locale);
8797 err = IE_ISDS;
8798 goto leave;
8802 /* Extract data */
8803 xpath_ctx = xmlXPathNewContext(response);
8804 if (!xpath_ctx) {
8805 err = IE_ERROR;
8806 goto leave;
8808 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8809 err = IE_ERROR;
8810 goto leave;
8812 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8813 xpath_ctx);
8814 if (!result) {
8815 err = IE_ERROR;
8816 goto leave;
8818 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8819 isds_log_message(context, _("Missing CreateMessageResponse element"));
8820 err = IE_ISDS;
8821 goto leave;
8823 if (result->nodesetval->nodeNr > 1) {
8824 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8825 err = IE_ISDS;
8826 goto leave;
8828 xpath_ctx->node = result->nodesetval->nodeTab[0];
8829 xmlXPathFreeObject(result); result = NULL;
8831 if (outgoing_message->envelope->dmID) {
8832 free(outgoing_message->envelope->dmID);
8833 outgoing_message->envelope->dmID = NULL;
8835 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8836 if (!outgoing_message->envelope->dmID) {
8837 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8838 "but did not return assigned message ID\n"));
8841 leave:
8842 /* TODO: Serialize message into structure member raw */
8843 /* XXX: Each web service transport message in different format.
8844 * Therefore it's not possible to save them directly.
8845 * To save them, one must figure out common format.
8846 * We can leave it on application, or we can implement the ESS format. */
8847 /*if (message_is_complete) {
8848 if (outgoing_message->envelope->dmID) {
8850 /* Add assigned message ID as first child*/
8851 /*xmlNodePtr dmid_text = xmlNewText(
8852 (xmlChar *) outgoing_message->envelope->dmID);
8853 if (!dmid_text) goto serialization_failed;
8855 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8856 BAD_CAST "dmID");
8857 if (!dmid_element) {
8858 xmlFreeNode(dmid_text);
8859 goto serialization_failed;
8862 xmlNodePtr dmid_element_with_text =
8863 xmlAddChild(dmid_element, dmid_text);
8864 if (!dmid_element_with_text) {
8865 xmlFreeNode(dmid_element);
8866 xmlFreeNode(dmid_text);
8867 goto serialization_failed;
8870 node = xmlAddPrevSibling(envelope->childern,
8871 dmid_element_with_text);
8872 if (!node) {
8873 xmlFreeNodeList(dmid_element_with_text);
8874 goto serialization_failed;
8878 /* Serialize message with ID into raw */
8879 /*buffer = serialize_element(envelope)*/
8880 /* }
8882 serialization_failed:
8886 /* Clean up */
8887 xmlXPathFreeObject(result);
8888 xmlXPathFreeContext(xpath_ctx);
8890 free(code);
8891 free(message);
8892 xmlFreeDoc(response);
8893 xmlFreeNode(request);
8895 if (!err)
8896 isds_log(ILF_ISDS, ILL_DEBUG,
8897 _("CreateMessage request processed by server "
8898 "successfully.\n"));
8899 #else /* not HAVE_LIBCURL */
8900 err = IE_NOTSUP;
8901 #endif
8903 return err;
8907 /* Send a message via ISDS to a multiple recipients
8908 * @context is session context
8909 * @outgoing_message is message to send; Some members are mandatory,
8910 * some are optional and some are irrelevant (especially data
8911 * about sender). Data about recipient will be substituted by ISDS from
8912 * @copies. Included pointer to isds_list documents must
8913 * contain at least one document of FILEMETATYPE_MAIN.
8914 * @copies is list of isds_message_copy structures addressing all desired
8915 * recipients. This is read-write structure, some members will be filled with
8916 * valid data from ISDS (message IDs, error codes, error descriptions).
8917 * @return
8918 * ISDS_SUCCESS if all messages have been sent
8919 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8920 * succeeded messages can be identified by copies->data->error),
8921 * or other error code if something other goes wrong. */
8922 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8923 const struct isds_message *outgoing_message,
8924 struct isds_list *copies) {
8926 isds_error err = IE_SUCCESS;
8927 #if HAVE_LIBCURL
8928 isds_error append_err;
8929 xmlNsPtr isds_ns = NULL;
8930 xmlNodePtr request = NULL, recipients, recipient, node;
8931 struct isds_list *item;
8932 struct isds_message_copy *copy;
8933 xmlDocPtr response = NULL;
8934 xmlChar *code = NULL, *message = NULL;
8935 xmlXPathContextPtr xpath_ctx = NULL;
8936 xmlXPathObjectPtr result = NULL;
8937 xmlChar *string = NULL;
8938 int i;
8939 #endif
8941 if (!context) return IE_INVALID_CONTEXT;
8942 zfree(context->long_message);
8943 if (!outgoing_message || !copies) return IE_INVAL;
8945 #if HAVE_LIBCURL
8946 /* Check if connection is established
8947 * TODO: This check should be done downstairs. */
8948 if (!context->curl) return IE_CONNECTION_CLOSED;
8951 /* Build CreateMultipleMessage request */
8952 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
8953 if (!request) {
8954 isds_log_message(context,
8955 _("Could not build CreateMultipleMessage request"));
8956 return IE_ERROR;
8958 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8959 if(!isds_ns) {
8960 isds_log_message(context, _("Could not create ISDS name space"));
8961 xmlFreeNode(request);
8962 return IE_ERROR;
8964 xmlSetNs(request, isds_ns);
8967 /* Build recipients */
8968 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8969 if (!recipients) {
8970 isds_log_message(context, _("Could not add dmRecipients child to "
8971 "CreateMultipleMessage element"));
8972 xmlFreeNode(request);
8973 return IE_ERROR;
8976 /* Insert each recipient */
8977 for (item = copies; item; item = item->next) {
8978 copy = (struct isds_message_copy *) item->data;
8979 if (!copy) {
8980 isds_log_message(context,
8981 _("`copies' list item contains empty data"));
8982 err = IE_INVAL;
8983 goto leave;
8986 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
8987 if (!recipient) {
8988 isds_log_message(context, _("Could not add dmRecipient child to "
8989 "dmRecipients element"));
8990 err = IE_ERROR;
8991 goto leave;
8994 if (!copy->dbIDRecipient) {
8995 isds_log_message(context,
8996 _("Message copy is missing recipient box identifier"));
8997 err = IE_INVAL;
8998 goto leave;
9000 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
9001 INSERT_STRING(recipient, "dmRecipientOrgUnit",
9002 copy->dmRecipientOrgUnit);
9003 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
9004 copy->dmRecipientOrgUnitNum, string);
9005 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
9008 /* Append envelope and files */
9009 err = insert_envelope_files(context, outgoing_message, request, 0);
9010 if (err) goto leave;
9013 isds_log(ILF_ISDS, ILL_DEBUG,
9014 _("Sending CreateMultipleMessage request to ISDS\n"));
9016 /* Sent request */
9017 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9018 if (err) {
9019 isds_log(ILF_ISDS, ILL_DEBUG,
9020 _("Processing ISDS response on CreateMultipleMessage "
9021 "request failed\n"));
9022 goto leave;
9025 /* Check for response status */
9026 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9027 &code, &message, NULL);
9028 if (err) {
9029 isds_log(ILF_ISDS, ILL_DEBUG,
9030 _("ISDS response on CreateMultipleMessage request "
9031 "is missing status\n"));
9032 goto leave;
9035 /* Request processed, but some copies failed */
9036 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9037 char *box_id_locale =
9038 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9039 char *code_locale = _isds_utf82locale((char*)code);
9040 char *message_locale = _isds_utf82locale((char*)message);
9041 isds_log(ILF_ISDS, ILL_DEBUG,
9042 _("Server did accept message for multiple recipients "
9043 "on CreateMultipleMessage request but delivery to "
9044 "some of them failed (code=%s, message=%s)\n"),
9045 box_id_locale, code_locale, message_locale);
9046 isds_log_message(context, message_locale);
9047 free(box_id_locale);
9048 free(code_locale);
9049 free(message_locale);
9050 err = IE_PARTIAL_SUCCESS;
9053 /* Request refused by server as whole */
9054 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9055 char *box_id_locale =
9056 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9057 char *code_locale = _isds_utf82locale((char*)code);
9058 char *message_locale = _isds_utf82locale((char*)message);
9059 isds_log(ILF_ISDS, ILL_DEBUG,
9060 _("Server did not accept message for multiple recipients "
9061 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9062 box_id_locale, code_locale, message_locale);
9063 isds_log_message(context, message_locale);
9064 free(box_id_locale);
9065 free(code_locale);
9066 free(message_locale);
9067 err = IE_ISDS;
9068 goto leave;
9072 /* Extract data */
9073 xpath_ctx = xmlXPathNewContext(response);
9074 if (!xpath_ctx) {
9075 err = IE_ERROR;
9076 goto leave;
9078 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9079 err = IE_ERROR;
9080 goto leave;
9082 result = xmlXPathEvalExpression(
9083 BAD_CAST "/isds:CreateMultipleMessageResponse"
9084 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9085 xpath_ctx);
9086 if (!result) {
9087 err = IE_ERROR;
9088 goto leave;
9090 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9091 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9092 err = IE_ISDS;
9093 goto leave;
9096 /* Extract message ID and delivery status for each copy */
9097 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9098 item = item->next, i++) {
9099 copy = (struct isds_message_copy *) item->data;
9100 xpath_ctx->node = result->nodesetval->nodeTab[i];
9102 append_err = append_TMStatus(context, copy, xpath_ctx);
9103 if (append_err) {
9104 err = append_err;
9105 goto leave;
9108 if (item || i < result->nodesetval->nodeNr) {
9109 isds_printf_message(context, _("ISDS returned unexpected number of "
9110 "message copy delivery states: %d"),
9111 result->nodesetval->nodeNr);
9112 err = IE_ISDS;
9113 goto leave;
9117 leave:
9118 /* Clean up */
9119 free(string);
9120 xmlXPathFreeObject(result);
9121 xmlXPathFreeContext(xpath_ctx);
9123 free(code);
9124 free(message);
9125 xmlFreeDoc(response);
9126 xmlFreeNode(request);
9128 if (!err)
9129 isds_log(ILF_ISDS, ILL_DEBUG,
9130 _("CreateMultipleMessageResponse request processed by server "
9131 "successfully.\n"));
9132 #else /* not HAVE_LIBCURL */
9133 err = IE_NOTSUP;
9134 #endif
9136 return err;
9140 /* Get list of messages. This is common core for getting sent or received
9141 * messages.
9142 * Any criterion argument can be NULL, if you don't care about it.
9143 * @context is session context. Must not be NULL.
9144 * @outgoing_direction is true if you want list of outgoing messages,
9145 * it's false if you want incoming messages.
9146 * @from_time is minimal time and date of message sending inclusive.
9147 * @to_time is maximal time and date of message sending inclusive
9148 * @organization_unit_number is number of sender/recipient respectively.
9149 * @status_filter is bit field of isds_message_status values. Use special
9150 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9151 * all values, you can use bit-wise arithmetic if you want.)
9152 * @offset is index of first message we are interested in. First message is 1.
9153 * Set to 0 (or 1) if you don't care.
9154 * @number is maximal length of list you want to get as input value, outputs
9155 * number of messages matching these criteria. Can be NULL if you don't care
9156 * (applies to output value either).
9157 * @messages is automatically reallocated list of isds_message's. Be ware that
9158 * it returns only brief overview (envelope and some other fields) about each
9159 * message, not the complete message. FIXME: Specify exact fields.
9160 * The list is sorted by delivery time in ascending order.
9161 * Use NULL if you don't care about don't need the data (useful if you want to
9162 * know only the @number). If you provide &NULL, list will be allocated on
9163 * heap, if you provide pointer to non-NULL, list will be freed automatically
9164 * at first. Also in case of error the list will be NULLed.
9165 * @return IE_SUCCESS or appropriate error code. */
9166 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9167 _Bool outgoing_direction,
9168 const struct timeval *from_time, const struct timeval *to_time,
9169 const long int *organization_unit_number,
9170 const unsigned int status_filter,
9171 const unsigned long int offset, unsigned long int *number,
9172 struct isds_list **messages) {
9174 isds_error err = IE_SUCCESS;
9175 #if HAVE_LIBCURL
9176 xmlNsPtr isds_ns = NULL;
9177 xmlNodePtr request = NULL, node;
9178 xmlDocPtr response = NULL;
9179 xmlChar *code = NULL, *message = NULL;
9180 xmlXPathContextPtr xpath_ctx = NULL;
9181 xmlXPathObjectPtr result = NULL;
9182 xmlChar *string = NULL;
9183 int count = 0;
9184 #endif
9186 if (!context) return IE_INVALID_CONTEXT;
9187 zfree(context->long_message);
9189 /* Free former message list if any */
9190 if (messages) isds_list_free(messages);
9192 #if HAVE_LIBCURL
9193 /* Check if connection is established
9194 * TODO: This check should be done downstairs. */
9195 if (!context->curl) return IE_CONNECTION_CLOSED;
9197 /* Build GetListOf*Messages request */
9198 request = xmlNewNode(NULL,
9199 (outgoing_direction) ?
9200 BAD_CAST "GetListOfSentMessages" :
9201 BAD_CAST "GetListOfReceivedMessages"
9203 if (!request) {
9204 isds_log_message(context,
9205 (outgoing_direction) ?
9206 _("Could not build GetListOfSentMessages request") :
9207 _("Could not build GetListOfReceivedMessages request")
9209 return IE_ERROR;
9211 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9212 if(!isds_ns) {
9213 isds_log_message(context, _("Could not create ISDS name space"));
9214 xmlFreeNode(request);
9215 return IE_ERROR;
9217 xmlSetNs(request, isds_ns);
9220 if (from_time) {
9221 err = timeval2timestring(from_time, &string);
9222 if (err) goto leave;
9224 INSERT_STRING(request, "dmFromTime", string);
9225 free(string); string = NULL;
9227 if (to_time) {
9228 err = timeval2timestring(to_time, &string);
9229 if (err) goto leave;
9231 INSERT_STRING(request, "dmToTime", string);
9232 free(string); string = NULL;
9234 if (outgoing_direction) {
9235 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9236 organization_unit_number, string);
9237 } else {
9238 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9239 organization_unit_number, string);
9242 if (status_filter > MESSAGESTATE_ANY) {
9243 isds_printf_message(context,
9244 _("Invalid message state filter value: %ld"), status_filter);
9245 err = IE_INVAL;
9246 goto leave;
9248 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9250 if (offset > 0 ) {
9251 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9252 } else {
9253 INSERT_STRING(request, "dmOffset", "1");
9256 /* number 0 means no limit */
9257 if (number && *number == 0) {
9258 INSERT_STRING(request, "dmLimit", NULL);
9259 } else {
9260 INSERT_ULONGINT(request, "dmLimit", number, string);
9264 isds_log(ILF_ISDS, ILL_DEBUG,
9265 (outgoing_direction) ?
9266 _("Sending GetListOfSentMessages request to ISDS\n") :
9267 _("Sending GetListOfReceivedMessages request to ISDS\n")
9270 /* Sent request */
9271 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9272 xmlFreeNode(request); request = NULL;
9274 if (err) {
9275 isds_log(ILF_ISDS, ILL_DEBUG,
9276 (outgoing_direction) ?
9277 _("Processing ISDS response on GetListOfSentMessages "
9278 "request failed\n") :
9279 _("Processing ISDS response on GetListOfReceivedMessages "
9280 "request failed\n")
9282 goto leave;
9285 /* Check for response status */
9286 err = isds_response_status(context, SERVICE_DM_INFO, response,
9287 &code, &message, NULL);
9288 if (err) {
9289 isds_log(ILF_ISDS, ILL_DEBUG,
9290 (outgoing_direction) ?
9291 _("ISDS response on GetListOfSentMessages request "
9292 "is missing status\n") :
9293 _("ISDS response on GetListOfReceivedMessages request "
9294 "is missing status\n")
9296 goto leave;
9299 /* Request processed, but nothing found */
9300 if (xmlStrcmp(code, BAD_CAST "0000")) {
9301 char *code_locale = _isds_utf82locale((char*)code);
9302 char *message_locale = _isds_utf82locale((char*)message);
9303 isds_log(ILF_ISDS, ILL_DEBUG,
9304 (outgoing_direction) ?
9305 _("Server refused GetListOfSentMessages request "
9306 "(code=%s, message=%s)\n") :
9307 _("Server refused GetListOfReceivedMessages request "
9308 "(code=%s, message=%s)\n"),
9309 code_locale, message_locale);
9310 isds_log_message(context, message_locale);
9311 free(code_locale);
9312 free(message_locale);
9313 err = IE_ISDS;
9314 goto leave;
9318 /* Extract data */
9319 xpath_ctx = xmlXPathNewContext(response);
9320 if (!xpath_ctx) {
9321 err = IE_ERROR;
9322 goto leave;
9324 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9325 err = IE_ERROR;
9326 goto leave;
9328 result = xmlXPathEvalExpression(
9329 (outgoing_direction) ?
9330 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9331 "isds:dmRecords/isds:dmRecord" :
9332 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9333 "isds:dmRecords/isds:dmRecord",
9334 xpath_ctx);
9335 if (!result) {
9336 err = IE_ERROR;
9337 goto leave;
9340 /* Fill output arguments in */
9341 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9342 struct isds_envelope *envelope;
9343 struct isds_list *item = NULL, *last_item = NULL;
9345 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9346 /* Create new message */
9347 item = calloc(1, sizeof(*item));
9348 if (!item) {
9349 err = IE_NOMEM;
9350 goto leave;
9352 item->destructor = (void(*)(void**)) &isds_message_free;
9353 item->data = calloc(1, sizeof(struct isds_message));
9354 if (!item->data) {
9355 isds_list_free(&item);
9356 err = IE_NOMEM;
9357 goto leave;
9360 /* Extract envelope data */
9361 xpath_ctx->node = result->nodesetval->nodeTab[count];
9362 envelope = NULL;
9363 err = extract_DmRecord(context, &envelope, xpath_ctx);
9364 if (err) {
9365 isds_list_free(&item);
9366 goto leave;
9369 /* Attach extracted envelope */
9370 ((struct isds_message *) item->data)->envelope = envelope;
9372 /* Append new message into the list */
9373 if (!*messages) {
9374 *messages = last_item = item;
9375 } else {
9376 last_item->next = item;
9377 last_item = item;
9381 if (number) *number = count;
9383 leave:
9384 if (err) {
9385 isds_list_free(messages);
9388 free(string);
9389 xmlXPathFreeObject(result);
9390 xmlXPathFreeContext(xpath_ctx);
9392 free(code);
9393 free(message);
9394 xmlFreeDoc(response);
9395 xmlFreeNode(request);
9397 if (!err)
9398 isds_log(ILF_ISDS, ILL_DEBUG,
9399 (outgoing_direction) ?
9400 _("GetListOfSentMessages request processed by server "
9401 "successfully.\n") :
9402 _("GetListOfReceivedMessages request processed by server "
9403 "successfully.\n")
9405 #else /* not HAVE_LIBCURL */
9406 err = IE_NOTSUP;
9407 #endif
9408 return err;
9412 /* Get list of outgoing (already sent) messages.
9413 * Any criterion argument can be NULL, if you don't care about it.
9414 * @context is session context. Must not be NULL.
9415 * @from_time is minimal time and date of message sending inclusive.
9416 * @to_time is maximal time and date of message sending inclusive
9417 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9418 * @status_filter is bit field of isds_message_status values. Use special
9419 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9420 * all values, you can use bit-wise arithmetic if you want.)
9421 * @offset is index of first message we are interested in. First message is 1.
9422 * Set to 0 (or 1) if you don't care.
9423 * @number is maximal length of list you want to get as input value, outputs
9424 * number of messages matching these criteria. Can be NULL if you don't care
9425 * (applies to output value either).
9426 * @messages is automatically reallocated list of isds_message's. Be ware that
9427 * it returns only brief overview (envelope and some other fields) about each
9428 * message, not the complete message. FIXME: Specify exact fields.
9429 * The list is sorted by delivery time in ascending order.
9430 * Use NULL if you don't care about the meta data (useful if you want to know
9431 * only the @number). If you provide &NULL, list will be allocated on heap,
9432 * if you provide pointer to non-NULL, list will be freed automatically at
9433 * first. Also in case of error the list will be NULLed.
9434 * @return IE_SUCCESS or appropriate error code. */
9435 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9436 const struct timeval *from_time, const struct timeval *to_time,
9437 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9438 const unsigned long int offset, unsigned long int *number,
9439 struct isds_list **messages) {
9441 return isds_get_list_of_messages(
9442 context, 1,
9443 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9444 offset, number,
9445 messages);
9449 /* Get list of incoming (addressed to you) messages.
9450 * Any criterion argument can be NULL, if you don't care about it.
9451 * @context is session context. Must not be NULL.
9452 * @from_time is minimal time and date of message sending inclusive.
9453 * @to_time is maximal time and date of message sending inclusive
9454 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9455 * @status_filter is bit field of isds_message_status values. Use special
9456 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9457 * all values, you can use bit-wise arithmetic if you want.)
9458 * @offset is index of first message we are interested in. First message is 1.
9459 * Set to 0 (or 1) if you don't care.
9460 * @number is maximal length of list you want to get as input value, outputs
9461 * number of messages matching these criteria. Can be NULL if you don't care
9462 * (applies to output value either).
9463 * @messages is automatically reallocated list of isds_message's. Be ware that
9464 * it returns only brief overview (envelope and some other fields) about each
9465 * message, not the complete message. FIXME: Specify exact fields.
9466 * Use NULL if you don't care about the meta data (useful if you want to know
9467 * only the @number). If you provide &NULL, list will be allocated on heap,
9468 * if you provide pointer to non-NULL, list will be freed automatically at
9469 * first. Also in case of error the list will be NULLed.
9470 * @return IE_SUCCESS or appropriate error code. */
9471 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9472 const struct timeval *from_time, const struct timeval *to_time,
9473 const long int *dmRecipientOrgUnitNum,
9474 const unsigned int status_filter,
9475 const unsigned long int offset, unsigned long int *number,
9476 struct isds_list **messages) {
9478 return isds_get_list_of_messages(
9479 context, 0,
9480 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9481 offset, number,
9482 messages);
9486 /* Get list of sent message state changes.
9487 * Any criterion argument can be NULL, if you don't care about it.
9488 * @context is session context. Must not be NULL.
9489 * @from_time is minimal time and date of status changes inclusive
9490 * @to_time is maximal time and date of status changes inclusive
9491 * @changed_states is automatically reallocated list of
9492 * isds_message_status_change's. If you provide &NULL, list will be allocated
9493 * on heap, if you provide pointer to non-NULL, list will be freed
9494 * automatically at first. Also in case of error the list will be NULLed.
9495 * XXX: The list item ordering is not specified.
9496 * XXX: Server provides only `recent' changes.
9497 * @return IE_SUCCESS or appropriate error code. */
9498 isds_error isds_get_list_of_sent_message_state_changes(
9499 struct isds_ctx *context,
9500 const struct timeval *from_time, const struct timeval *to_time,
9501 struct isds_list **changed_states) {
9503 isds_error err = IE_SUCCESS;
9504 #if HAVE_LIBCURL
9505 xmlNsPtr isds_ns = NULL;
9506 xmlNodePtr request = NULL, node;
9507 xmlDocPtr response = NULL;
9508 xmlXPathContextPtr xpath_ctx = NULL;
9509 xmlXPathObjectPtr result = NULL;
9510 xmlChar *string = NULL;
9511 int count = 0;
9512 #endif
9514 if (!context) return IE_INVALID_CONTEXT;
9515 zfree(context->long_message);
9517 /* Free former message list if any */
9518 isds_list_free(changed_states);
9520 #if HAVE_LIBCURL
9521 /* Check if connection is established
9522 * TODO: This check should be done downstairs. */
9523 if (!context->curl) return IE_CONNECTION_CLOSED;
9525 /* Build GetMessageStateChanges request */
9526 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
9527 if (!request) {
9528 isds_log_message(context,
9529 _("Could not build GetMessageStateChanges request"));
9530 return IE_ERROR;
9532 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9533 if(!isds_ns) {
9534 isds_log_message(context, _("Could not create ISDS name space"));
9535 xmlFreeNode(request);
9536 return IE_ERROR;
9538 xmlSetNs(request, isds_ns);
9541 if (from_time) {
9542 err = timeval2timestring(from_time, &string);
9543 if (err) goto leave;
9545 INSERT_STRING(request, "dmFromTime", string);
9546 zfree(string);
9548 if (to_time) {
9549 err = timeval2timestring(to_time, &string);
9550 if (err) goto leave;
9552 INSERT_STRING(request, "dmToTime", string);
9553 zfree(string);
9556 /* Sent request */
9557 err = send_destroy_request_check_response(context,
9558 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9559 &response, NULL, NULL);
9560 if (err) goto leave;
9563 /* Extract data */
9564 xpath_ctx = xmlXPathNewContext(response);
9565 if (!xpath_ctx) {
9566 err = IE_ERROR;
9567 goto leave;
9569 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9570 err = IE_ERROR;
9571 goto leave;
9573 result = xmlXPathEvalExpression(
9574 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9575 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9576 if (!result) {
9577 err = IE_ERROR;
9578 goto leave;
9581 /* Fill output arguments in */
9582 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9583 struct isds_list *item = NULL, *last_item = NULL;
9585 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9586 /* Create new status change */
9587 item = calloc(1, sizeof(*item));
9588 if (!item) {
9589 err = IE_NOMEM;
9590 goto leave;
9592 item->destructor =
9593 (void(*)(void**)) &isds_message_status_change_free;
9595 /* Extract message status change */
9596 xpath_ctx->node = result->nodesetval->nodeTab[count];
9597 err = extract_StateChangesRecord(context,
9598 (struct isds_message_status_change **) &item->data,
9599 xpath_ctx);
9600 if (err) {
9601 isds_list_free(&item);
9602 goto leave;
9605 /* Append new message status change into the list */
9606 if (!*changed_states) {
9607 *changed_states = last_item = item;
9608 } else {
9609 last_item->next = item;
9610 last_item = item;
9615 leave:
9616 if (err) {
9617 isds_list_free(changed_states);
9620 free(string);
9621 xmlXPathFreeObject(result);
9622 xmlXPathFreeContext(xpath_ctx);
9623 xmlFreeDoc(response);
9624 xmlFreeNode(request);
9626 if (!err)
9627 isds_log(ILF_ISDS, ILL_DEBUG,
9628 _("GetMessageStateChanges request processed by server "
9629 "successfully.\n"));
9630 #else /* not HAVE_LIBCURL */
9631 err = IE_NOTSUP;
9632 #endif
9633 return err;
9637 #if HAVE_LIBCURL
9638 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9639 * code
9640 * @context is session context
9641 * @service is ISDS WS service handler
9642 * @service_name is name of SERVICE_DM_OPERATIONS
9643 * @message_id is message ID to send as service argument to ISDS
9644 * @response is reallocated server SOAP body response as XML document
9645 * @raw_response is reallocated bit stream with response body. Use
9646 * NULL if you don't care
9647 * @raw_response_length is size of @raw_response in bytes
9648 * @code is reallocated ISDS status code
9649 * @status_message is reallocated ISDS status message
9650 * @return error coded from lower layer, context message will be set up
9651 * appropriately. */
9652 static isds_error build_send_check_message_request(struct isds_ctx *context,
9653 const isds_service service, const xmlChar *service_name,
9654 const char *message_id,
9655 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9656 xmlChar **code, xmlChar **status_message) {
9658 isds_error err = IE_SUCCESS;
9659 char *service_name_locale = NULL, *message_id_locale = NULL;
9660 xmlNodePtr request = NULL, node;
9661 xmlNsPtr isds_ns = NULL;
9663 if (!context) return IE_INVALID_CONTEXT;
9664 if (!service_name || !message_id) return IE_INVAL;
9665 if (!response || !code || !status_message) return IE_INVAL;
9666 if (!raw_response_length && raw_response) return IE_INVAL;
9668 /* Free output argument */
9669 xmlFreeDoc(*response); *response = NULL;
9670 if (raw_response) zfree(*raw_response);
9671 zfree(*code);
9672 zfree(*status_message);
9675 /* Check if connection is established
9676 * TODO: This check should be done downstairs. */
9677 if (!context->curl) return IE_CONNECTION_CLOSED;
9679 service_name_locale = _isds_utf82locale((char*)service_name);
9680 message_id_locale = _isds_utf82locale(message_id);
9681 if (!service_name_locale || !message_id_locale) {
9682 err = IE_NOMEM;
9683 goto leave;
9686 /* Build request */
9687 request = xmlNewNode(NULL, service_name);
9688 if (!request) {
9689 isds_printf_message(context,
9690 _("Could not build %s request for %s message ID"),
9691 service_name_locale, message_id_locale);
9692 err = IE_ERROR;
9693 goto leave;
9695 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9696 if(!isds_ns) {
9697 isds_log_message(context, _("Could not create ISDS name space"));
9698 err = IE_ERROR;
9699 goto leave;
9701 xmlSetNs(request, isds_ns);
9704 /* Add requested ID */
9705 err = validate_message_id_length(context, (xmlChar *) message_id);
9706 if (err) goto leave;
9707 INSERT_STRING(request, "dmID", message_id);
9710 isds_log(ILF_ISDS, ILL_DEBUG,
9711 _("Sending %s request for %s message ID to ISDS\n"),
9712 service_name_locale, message_id_locale);
9714 /* Send request */
9715 err = _isds(context, service, request, response,
9716 raw_response, raw_response_length);
9717 xmlFreeNode(request); request = NULL;
9719 if (err) {
9720 isds_log(ILF_ISDS, ILL_DEBUG,
9721 _("Processing ISDS response on %s request failed\n"),
9722 service_name_locale);
9723 goto leave;
9726 /* Check for response status */
9727 err = isds_response_status(context, service, *response,
9728 code, status_message, NULL);
9729 if (err) {
9730 isds_log(ILF_ISDS, ILL_DEBUG,
9731 _("ISDS response on %s request is missing status\n"),
9732 service_name_locale);
9733 goto leave;
9736 /* Request processed, but nothing found */
9737 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9738 char *code_locale = _isds_utf82locale((char*) *code);
9739 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9740 isds_log(ILF_ISDS, ILL_DEBUG,
9741 _("Server refused %s request for %s message ID "
9742 "(code=%s, message=%s)\n"),
9743 service_name_locale, message_id_locale,
9744 code_locale, status_message_locale);
9745 isds_log_message(context, status_message_locale);
9746 free(code_locale);
9747 free(status_message_locale);
9748 err = IE_ISDS;
9749 goto leave;
9752 leave:
9753 free(message_id_locale);
9754 free(service_name_locale);
9755 xmlFreeNode(request);
9756 return err;
9760 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9761 * signed data and free ISDS response.
9762 * @context is session context
9763 * @message_id is UTF-8 encoded message ID for logging purpose
9764 * @response is parsed XML document. It will be freed and NULLed in the middle
9765 * of function run to save memory. This is not guaranteed in case of error.
9766 * @request_name is name of ISDS request used to construct response root
9767 * element name and for logging purpose.
9768 * @raw is reallocated output buffer with DER encoded CMS data
9769 * @raw_length is size of @raw buffer in bytes
9770 * @returns standard error codes, in case of error, @raw will be freed and
9771 * NULLed, @response sometimes. */
9772 static isds_error find_extract_signed_data_free_response(
9773 struct isds_ctx *context, const xmlChar *message_id,
9774 xmlDocPtr *response, const xmlChar *request_name,
9775 void **raw, size_t *raw_length) {
9777 isds_error err = IE_SUCCESS;
9778 char *xpath_expression = NULL;
9779 xmlXPathContextPtr xpath_ctx = NULL;
9780 xmlXPathObjectPtr result = NULL;
9781 char *encoded_structure = NULL;
9783 if (!context) return IE_INVALID_CONTEXT;
9784 if (!raw) return IE_INVAL;
9785 zfree(*raw);
9786 if (!message_id || !response || !*response || !request_name || !raw_length)
9787 return IE_INVAL;
9789 /* Build XPath expression */
9790 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9791 "Response/isds:dmSignature");
9792 if (!xpath_expression) return IE_NOMEM;
9794 /* Extract data */
9795 xpath_ctx = xmlXPathNewContext(*response);
9796 if (!xpath_ctx) {
9797 err = IE_ERROR;
9798 goto leave;
9800 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9801 err = IE_ERROR;
9802 goto leave;
9804 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9805 if (!result) {
9806 err = IE_ERROR;
9807 goto leave;
9809 /* Empty response */
9810 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9811 char *message_id_locale = _isds_utf82locale((char*) message_id);
9812 isds_printf_message(context,
9813 _("Server did not return any signed data for message ID `%s' "
9814 "on %s request"),
9815 message_id_locale, request_name);
9816 free(message_id_locale);
9817 err = IE_ISDS;
9818 goto leave;
9820 /* More responses */
9821 if (result->nodesetval->nodeNr > 1) {
9822 char *message_id_locale = _isds_utf82locale((char*) message_id);
9823 isds_printf_message(context,
9824 _("Server did return more signed data for message ID `%s' "
9825 "on %s request"),
9826 message_id_locale, request_name);
9827 free(message_id_locale);
9828 err = IE_ISDS;
9829 goto leave;
9831 /* One response */
9832 xpath_ctx->node = result->nodesetval->nodeTab[0];
9834 /* Extract PKCS#7 structure */
9835 EXTRACT_STRING(".", encoded_structure);
9836 if (!encoded_structure) {
9837 isds_log_message(context, _("dmSignature element is empty"));
9840 /* Here we have delivery info as standalone CMS in encoded_structure.
9841 * We don't need any other data, free them: */
9842 xmlXPathFreeObject(result); result = NULL;
9843 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9844 xmlFreeDoc(*response); *response = NULL;
9847 /* Decode PKCS#7 to DER format */
9848 *raw_length = _isds_b64decode(encoded_structure, raw);
9849 if (*raw_length == (size_t) -1) {
9850 isds_log_message(context,
9851 _("Error while Base64-decoding PKCS#7 structure"));
9852 err = IE_ERROR;
9853 goto leave;
9856 leave:
9857 if (err) {
9858 zfree(*raw);
9859 raw_length = 0;
9862 free(encoded_structure);
9863 xmlXPathFreeObject(result);
9864 xmlXPathFreeContext(xpath_ctx);
9865 free(xpath_expression);
9867 return err;
9869 #endif /* HAVE_LIBCURL */
9872 /* Download incoming message envelope identified by ID.
9873 * @context is session context
9874 * @message_id is message identifier (you can get them from
9875 * isds_get_list_of_received_messages())
9876 * @message is automatically reallocated message retrieved from ISDS.
9877 * It will miss documents per se. Use isds_get_received_message(), if you are
9878 * interested in documents (content) too.
9879 * Returned hash and timestamp require documents to be verifiable. */
9880 isds_error isds_get_received_envelope(struct isds_ctx *context,
9881 const char *message_id, struct isds_message **message) {
9883 isds_error err = IE_SUCCESS;
9884 #if HAVE_LIBCURL
9885 xmlDocPtr response = NULL;
9886 xmlChar *code = NULL, *status_message = NULL;
9887 xmlXPathContextPtr xpath_ctx = NULL;
9888 xmlXPathObjectPtr result = NULL;
9889 #endif
9891 if (!context) return IE_INVALID_CONTEXT;
9892 zfree(context->long_message);
9894 /* Free former message if any */
9895 if (!message) return IE_INVAL;
9896 isds_message_free(message);
9898 #if HAVE_LIBCURL
9899 /* Do request and check for success */
9900 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9901 BAD_CAST "MessageEnvelopeDownload", message_id,
9902 &response, NULL, NULL, &code, &status_message);
9903 if (err) goto leave;
9905 /* Extract data */
9906 xpath_ctx = xmlXPathNewContext(response);
9907 if (!xpath_ctx) {
9908 err = IE_ERROR;
9909 goto leave;
9911 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9912 err = IE_ERROR;
9913 goto leave;
9915 result = xmlXPathEvalExpression(
9916 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9917 "isds:dmReturnedMessageEnvelope",
9918 xpath_ctx);
9919 if (!result) {
9920 err = IE_ERROR;
9921 goto leave;
9923 /* Empty response */
9924 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9925 char *message_id_locale = _isds_utf82locale((char*) message_id);
9926 isds_printf_message(context,
9927 _("Server did not return any envelope for ID `%s' "
9928 "on MessageEnvelopeDownload request"), message_id_locale);
9929 free(message_id_locale);
9930 err = IE_ISDS;
9931 goto leave;
9933 /* More envelops */
9934 if (result->nodesetval->nodeNr > 1) {
9935 char *message_id_locale = _isds_utf82locale((char*) message_id);
9936 isds_printf_message(context,
9937 _("Server did return more envelopes for ID `%s' "
9938 "on MessageEnvelopeDownload request"), message_id_locale);
9939 free(message_id_locale);
9940 err = IE_ISDS;
9941 goto leave;
9943 /* One message */
9944 xpath_ctx->node = result->nodesetval->nodeTab[0];
9946 /* Extract the envelope (= message without documents, hence 0) */
9947 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9948 if (err) goto leave;
9950 /* Save XML blob */
9951 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9952 &(*message)->raw_length);
9954 leave:
9955 if (err) {
9956 isds_message_free(message);
9959 xmlXPathFreeObject(result);
9960 xmlXPathFreeContext(xpath_ctx);
9962 free(code);
9963 free(status_message);
9964 if (!*message || !(*message)->xml) {
9965 xmlFreeDoc(response);
9968 if (!err)
9969 isds_log(ILF_ISDS, ILL_DEBUG,
9970 _("MessageEnvelopeDownload request processed by server "
9971 "successfully.\n")
9973 #else /* not HAVE_LIBCURL */
9974 err = IE_NOTSUP;
9975 #endif
9976 return err;
9980 /* Load delivery info of any format from buffer.
9981 * @context is session context
9982 * @raw_type advertises format of @buffer content. Only delivery info types
9983 * are accepted.
9984 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9985 * retrieve such data from message->raw after calling
9986 * isds_get_signed_delivery_info().
9987 * @length is length of buffer in bytes.
9988 * @message is automatically reallocated message parsed from @buffer.
9989 * @strategy selects how buffer will be attached into raw isds_message member.
9990 * */
9991 isds_error isds_load_delivery_info(struct isds_ctx *context,
9992 const isds_raw_type raw_type,
9993 const void *buffer, const size_t length,
9994 struct isds_message **message, const isds_buffer_strategy strategy) {
9996 isds_error err = IE_SUCCESS;
9997 message_ns_type message_ns;
9998 xmlDocPtr message_doc = NULL;
9999 xmlXPathContextPtr xpath_ctx = NULL;
10000 xmlXPathObjectPtr result = NULL;
10001 void *xml_stream = NULL;
10002 size_t xml_stream_length = 0;
10004 if (!context) return IE_INVALID_CONTEXT;
10005 zfree(context->long_message);
10006 if (!message) return IE_INVAL;
10007 isds_message_free(message);
10008 if (!buffer) return IE_INVAL;
10011 /* Select buffer format and extract XML from CMS*/
10012 switch (raw_type) {
10013 case RAWTYPE_DELIVERYINFO:
10014 message_ns = MESSAGE_NS_UNSIGNED;
10015 xml_stream = (void *) buffer;
10016 xml_stream_length = length;
10017 break;
10019 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
10020 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10021 xml_stream = (void *) buffer;
10022 xml_stream_length = length;
10023 break;
10025 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10026 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10027 err = _isds_extract_cms_data(context, buffer, length,
10028 &xml_stream, &xml_stream_length);
10029 if (err) goto leave;
10030 break;
10032 default:
10033 isds_log_message(context, _("Bad raw delivery representation type"));
10034 return IE_INVAL;
10035 break;
10038 isds_log(ILF_ISDS, ILL_DEBUG,
10039 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10040 xml_stream_length, xml_stream);
10042 /* Convert delivery info XML stream into XPath context */
10043 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10044 if (!message_doc) {
10045 err = IE_XML;
10046 goto leave;
10048 xpath_ctx = xmlXPathNewContext(message_doc);
10049 if (!xpath_ctx) {
10050 err = IE_ERROR;
10051 goto leave;
10053 /* XXX: Name spaces mangled for signed delivery info:
10054 * http://isds.czechpoint.cz/v20/delivery:
10056 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10057 * <q:dmDelivery>
10058 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10059 * <p:dmID>170272</p:dmID>
10060 * ...
10061 * </p:dmDm>
10062 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10063 * ...
10064 * </q:dmEvents>...</q:dmEvents>
10065 * </q:dmDelivery>
10066 * </q:GetDeliveryInfoResponse>
10067 * */
10068 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10069 err = IE_ERROR;
10070 goto leave;
10072 result = xmlXPathEvalExpression(
10073 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10074 xpath_ctx);
10075 if (!result) {
10076 err = IE_ERROR;
10077 goto leave;
10079 /* Empty delivery info */
10080 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10081 isds_printf_message(context,
10082 _("XML document is not sisds:dmDelivery document"));
10083 err = IE_ISDS;
10084 goto leave;
10086 /* More delivery info's */
10087 if (result->nodesetval->nodeNr > 1) {
10088 isds_printf_message(context,
10089 _("XML document has more sisds:dmDelivery elements"));
10090 err = IE_ISDS;
10091 goto leave;
10093 /* One delivery info */
10094 xpath_ctx->node = result->nodesetval->nodeTab[0];
10096 /* Extract the envelope (= message without documents, hence 0).
10097 * XXX: extract_TReturnedMessage() can obtain attachments size,
10098 * but delivery info carries none. It's coded as option elements,
10099 * so it should work. */
10100 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10101 if (err) goto leave;
10103 /* Extract events */
10104 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10105 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10106 if (err) { err = IE_ERROR; goto leave; }
10107 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10108 if (err) goto leave;
10110 /* Append raw CMS structure into message */
10111 (*message)->raw_type = raw_type;
10112 switch (strategy) {
10113 case BUFFER_DONT_STORE:
10114 break;
10115 case BUFFER_COPY:
10116 (*message)->raw = malloc(length);
10117 if (!(*message)->raw) {
10118 err = IE_NOMEM;
10119 goto leave;
10121 memcpy((*message)->raw, buffer, length);
10122 (*message)->raw_length = length;
10123 break;
10124 case BUFFER_MOVE:
10125 (*message)->raw = (void *) buffer;
10126 (*message)->raw_length = length;
10127 break;
10128 default:
10129 err = IE_ENUM;
10130 goto leave;
10133 leave:
10134 if (err) {
10135 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10136 isds_message_free(message);
10139 xmlXPathFreeObject(result);
10140 xmlXPathFreeContext(xpath_ctx);
10141 if (!*message || !(*message)->xml) {
10142 xmlFreeDoc(message_doc);
10144 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10146 if (!err)
10147 isds_log(ILF_ISDS, ILL_DEBUG,
10148 _("Delivery info loaded successfully.\n"));
10149 return err;
10153 /* Download signed delivery info-sheet of given message identified by ID.
10154 * @context is session context
10155 * @message_id is message identifier (you can get them from
10156 * isds_get_list_of_{sent,received}_messages())
10157 * @message is automatically reallocated message retrieved from ISDS.
10158 * It will miss documents per se. Use isds_get_signed_received_message(),
10159 * if you are interested in documents (content). OTOH, only this function
10160 * can get list events message has gone through. */
10161 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10162 const char *message_id, struct isds_message **message) {
10164 isds_error err = IE_SUCCESS;
10165 #if HAVE_LIBCURL
10166 xmlDocPtr response = NULL;
10167 xmlChar *code = NULL, *status_message = NULL;
10168 void *raw = NULL;
10169 size_t raw_length = 0;
10170 #endif
10172 if (!context) return IE_INVALID_CONTEXT;
10173 zfree(context->long_message);
10175 /* Free former message if any */
10176 if (!message) return IE_INVAL;
10177 isds_message_free(message);
10179 #if HAVE_LIBCURL
10180 /* Do request and check for success */
10181 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10182 BAD_CAST "GetSignedDeliveryInfo", message_id,
10183 &response, NULL, NULL, &code, &status_message);
10184 if (err) goto leave;
10186 /* Find signed delivery info, extract it into raw and maybe free
10187 * response */
10188 err = find_extract_signed_data_free_response(context,
10189 (xmlChar *)message_id, &response,
10190 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10191 if (err) goto leave;
10193 /* Parse delivery info */
10194 err = isds_load_delivery_info(context,
10195 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10196 message, BUFFER_MOVE);
10197 if (err) goto leave;
10199 raw = NULL;
10201 leave:
10202 if (err) {
10203 isds_message_free(message);
10206 free(raw);
10207 free(code);
10208 free(status_message);
10209 xmlFreeDoc(response);
10211 if (!err)
10212 isds_log(ILF_ISDS, ILL_DEBUG,
10213 _("GetSignedDeliveryInfo request processed by server "
10214 "successfully.\n")
10216 #else /* not HAVE_LIBCURL */
10217 err = IE_NOTSUP;
10218 #endif
10219 return err;
10223 /* Download delivery info-sheet of given message identified by ID.
10224 * @context is session context
10225 * @message_id is message identifier (you can get them from
10226 * isds_get_list_of_{sent,received}_messages())
10227 * @message is automatically reallocated message retrieved from ISDS.
10228 * It will miss documents per se. Use isds_get_received_message(), if you are
10229 * interested in documents (content). OTOH, only this function can get list
10230 * of events message has gone through. */
10231 isds_error isds_get_delivery_info(struct isds_ctx *context,
10232 const char *message_id, struct isds_message **message) {
10234 isds_error err = IE_SUCCESS;
10235 #if HAVE_LIBCURL
10236 xmlDocPtr response = NULL;
10237 xmlChar *code = NULL, *status_message = NULL;
10238 xmlNodePtr delivery_node = NULL;
10239 void *raw = NULL;
10240 size_t raw_length = 0;
10241 #endif
10243 if (!context) return IE_INVALID_CONTEXT;
10244 zfree(context->long_message);
10246 /* Free former message if any */
10247 if (!message) return IE_INVAL;
10248 isds_message_free(message);
10250 #if HAVE_LIBCURL
10251 /* Do request and check for success */
10252 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10253 BAD_CAST "GetDeliveryInfo", message_id,
10254 &response, NULL, NULL, &code, &status_message);
10255 if (err) goto leave;
10258 /* Serialize delivery info */
10259 delivery_node = xmlDocGetRootElement(response);
10260 if (!delivery_node) {
10261 char *message_id_locale = _isds_utf82locale((char*) message_id);
10262 isds_printf_message(context,
10263 _("Server did not return any delivery info for ID `%s' "
10264 "on GetDeliveryInfo request"), message_id_locale);
10265 free(message_id_locale);
10266 err = IE_ISDS;
10267 goto leave;
10269 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10270 if (err) goto leave;
10272 /* Parse delivery info */
10273 /* TODO: Here we parse the response second time. We could single delivery
10274 * parser from isds_load_delivery_info() to make things faster. */
10275 err = isds_load_delivery_info(context,
10276 RAWTYPE_DELIVERYINFO, raw, raw_length,
10277 message, BUFFER_MOVE);
10278 if (err) goto leave;
10280 raw = NULL;
10283 leave:
10284 if (err) {
10285 isds_message_free(message);
10288 free(raw);
10289 free(code);
10290 free(status_message);
10291 xmlFreeDoc(response);
10293 if (!err)
10294 isds_log(ILF_ISDS, ILL_DEBUG,
10295 _("GetDeliveryInfo request processed by server "
10296 "successfully.\n")
10298 #else /* not HAVE_LIBCURL */
10299 err = IE_NOTSUP;
10300 #endif
10301 return err;
10305 /* Download incoming message identified by ID.
10306 * @context is session context
10307 * @message_id is message identifier (you can get them from
10308 * isds_get_list_of_received_messages())
10309 * @message is automatically reallocated message retrieved from ISDS */
10310 isds_error isds_get_received_message(struct isds_ctx *context,
10311 const char *message_id, struct isds_message **message) {
10313 isds_error err = IE_SUCCESS;
10314 #if HAVE_LIBCURL
10315 xmlDocPtr response = NULL;
10316 void *xml_stream = NULL;
10317 size_t xml_stream_length;
10318 xmlChar *code = NULL, *status_message = NULL;
10319 xmlXPathContextPtr xpath_ctx = NULL;
10320 xmlXPathObjectPtr result = NULL;
10321 char *phys_path = NULL;
10322 size_t phys_start, phys_end;
10323 #endif
10325 if (!context) return IE_INVALID_CONTEXT;
10326 zfree(context->long_message);
10328 /* Free former message if any */
10329 if (NULL == message) return IE_INVAL;
10330 if (message) isds_message_free(message);
10332 #if HAVE_LIBCURL
10333 /* Do request and check for success */
10334 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10335 BAD_CAST "MessageDownload", message_id,
10336 &response, &xml_stream, &xml_stream_length,
10337 &code, &status_message);
10338 if (err) goto leave;
10340 /* Extract data */
10341 xpath_ctx = xmlXPathNewContext(response);
10342 if (!xpath_ctx) {
10343 err = IE_ERROR;
10344 goto leave;
10346 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10347 err = IE_ERROR;
10348 goto leave;
10350 result = xmlXPathEvalExpression(
10351 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10352 xpath_ctx);
10353 if (!result) {
10354 err = IE_ERROR;
10355 goto leave;
10357 /* Empty response */
10358 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10359 char *message_id_locale = _isds_utf82locale((char*) message_id);
10360 isds_printf_message(context,
10361 _("Server did not return any message for ID `%s' "
10362 "on MessageDownload request"), message_id_locale);
10363 free(message_id_locale);
10364 err = IE_ISDS;
10365 goto leave;
10367 /* More messages */
10368 if (result->nodesetval->nodeNr > 1) {
10369 char *message_id_locale = _isds_utf82locale((char*) message_id);
10370 isds_printf_message(context,
10371 _("Server did return more messages for ID `%s' "
10372 "on MessageDownload request"), message_id_locale);
10373 free(message_id_locale);
10374 err = IE_ISDS;
10375 goto leave;
10377 /* One message */
10378 xpath_ctx->node = result->nodesetval->nodeTab[0];
10380 /* Extract the message */
10381 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10382 if (err) goto leave;
10384 /* Locate raw XML blob */
10385 phys_path = strdup(
10386 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10387 PHYSXML_ELEMENT_SEPARATOR
10388 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10389 PHYSXML_ELEMENT_SEPARATOR
10390 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10392 if (!phys_path) {
10393 err = IE_NOMEM;
10394 goto leave;
10396 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10397 phys_path, &phys_start, &phys_end);
10398 zfree(phys_path);
10399 if (err) {
10400 isds_log_message(context,
10401 _("Substring with isds:MessageDownloadResponse element "
10402 "could not be located in raw SOAP message"));
10403 goto leave;
10405 /* Save XML blob */
10406 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10407 &(*message)->raw_length);*/
10408 /* TODO: Store name space declarations from ancestors */
10409 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10410 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10411 (*message)->raw_length = phys_end - phys_start + 1;
10412 (*message)->raw = malloc((*message)->raw_length);
10413 if (!(*message)->raw) {
10414 err = IE_NOMEM;
10415 goto leave;
10417 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10420 leave:
10421 if (err) {
10422 isds_message_free(message);
10425 free(phys_path);
10427 xmlXPathFreeObject(result);
10428 xmlXPathFreeContext(xpath_ctx);
10430 free(code);
10431 free(status_message);
10432 free(xml_stream);
10433 if (!*message || !(*message)->xml) {
10434 xmlFreeDoc(response);
10437 if (!err)
10438 isds_log(ILF_ISDS, ILL_DEBUG,
10439 _("MessageDownload request processed by server "
10440 "successfully.\n")
10442 #else /* not HAVE_LIBCURL */
10443 err = IE_NOTSUP;
10444 #endif
10445 return err;
10449 /* Load message of any type from buffer.
10450 * @context is session context
10451 * @raw_type defines content type of @buffer. Only message types are allowed.
10452 * @buffer is message raw representation. Format (CMS, plain signed,
10453 * message direction) is defined in @raw_type. You can retrieve such data
10454 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10455 * @length is length of buffer in bytes.
10456 * @message is automatically reallocated message parsed from @buffer.
10457 * @strategy selects how buffer will be attached into raw isds_message member.
10458 * */
10459 isds_error isds_load_message(struct isds_ctx *context,
10460 const isds_raw_type raw_type, const void *buffer, const size_t length,
10461 struct isds_message **message, const isds_buffer_strategy strategy) {
10463 isds_error err = IE_SUCCESS;
10464 void *xml_stream = NULL;
10465 size_t xml_stream_length = 0;
10466 message_ns_type message_ns;
10467 xmlDocPtr message_doc = NULL;
10468 xmlXPathContextPtr xpath_ctx = NULL;
10469 xmlXPathObjectPtr result = NULL;
10471 if (!context) return IE_INVALID_CONTEXT;
10472 zfree(context->long_message);
10473 if (!message) return IE_INVAL;
10474 isds_message_free(message);
10475 if (!buffer) return IE_INVAL;
10478 /* Select buffer format and extract XML from CMS*/
10479 switch (raw_type) {
10480 case RAWTYPE_INCOMING_MESSAGE:
10481 message_ns = MESSAGE_NS_UNSIGNED;
10482 xml_stream = (void *) buffer;
10483 xml_stream_length = length;
10484 break;
10486 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10487 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10488 xml_stream = (void *) buffer;
10489 xml_stream_length = length;
10490 break;
10492 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10493 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10494 err = _isds_extract_cms_data(context, buffer, length,
10495 &xml_stream, &xml_stream_length);
10496 if (err) goto leave;
10497 break;
10499 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10500 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10501 xml_stream = (void *) buffer;
10502 xml_stream_length = length;
10503 break;
10505 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10506 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10507 err = _isds_extract_cms_data(context, buffer, length,
10508 &xml_stream, &xml_stream_length);
10509 if (err) goto leave;
10510 break;
10512 default:
10513 isds_log_message(context, _("Bad raw message representation type"));
10514 return IE_INVAL;
10515 break;
10518 isds_log(ILF_ISDS, ILL_DEBUG,
10519 _("Loading message:\n%.*s\nEnd of message\n"),
10520 xml_stream_length, xml_stream);
10522 /* Convert messages XML stream into XPath context */
10523 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10524 if (!message_doc) {
10525 err = IE_XML;
10526 goto leave;
10528 xpath_ctx = xmlXPathNewContext(message_doc);
10529 if (!xpath_ctx) {
10530 err = IE_ERROR;
10531 goto leave;
10533 /* XXX: Standard name space for unsigned incoming direction:
10534 * http://isds.czechpoint.cz/v20/
10536 * XXX: Name spaces mangled for signed outgoing direction:
10537 * http://isds.czechpoint.cz/v20/SentMessage:
10539 * <q:MessageDownloadResponse
10540 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10541 * <q:dmReturnedMessage>
10542 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10543 * <p:dmID>151916</p:dmID>
10544 * ...
10545 * </p:dmDm>
10546 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10547 * ...
10548 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10549 * </q:dmReturnedMessage>
10550 * </q:MessageDownloadResponse>
10552 * XXX: Name spaces mangled for signed incoming direction:
10553 * http://isds.czechpoint.cz/v20/message:
10555 * <q:MessageDownloadResponse
10556 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10557 * <q:dmReturnedMessage>
10558 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10559 * <p:dmID>151916</p:dmID>
10560 * ...
10561 * </p:dmDm>
10562 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10563 * ...
10564 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10565 * </q:dmReturnedMessage>
10566 * </q:MessageDownloadResponse>
10568 * Stupidity of ISDS developers is unlimited */
10569 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10570 err = IE_ERROR;
10571 goto leave;
10573 result = xmlXPathEvalExpression(
10574 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10575 xpath_ctx);
10576 if (!result) {
10577 err = IE_ERROR;
10578 goto leave;
10580 /* Empty message */
10581 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10582 isds_printf_message(context,
10583 _("XML document does not contain "
10584 "sisds:dmReturnedMessage element"));
10585 err = IE_ISDS;
10586 goto leave;
10588 /* More messages */
10589 if (result->nodesetval->nodeNr > 1) {
10590 isds_printf_message(context,
10591 _("XML document has more sisds:dmReturnedMessage elements"));
10592 err = IE_ISDS;
10593 goto leave;
10595 /* One message */
10596 xpath_ctx->node = result->nodesetval->nodeTab[0];
10598 /* Extract the message */
10599 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10600 if (err) goto leave;
10602 /* Append raw buffer into message */
10603 (*message)->raw_type = raw_type;
10604 switch (strategy) {
10605 case BUFFER_DONT_STORE:
10606 break;
10607 case BUFFER_COPY:
10608 (*message)->raw = malloc(length);
10609 if (!(*message)->raw) {
10610 err = IE_NOMEM;
10611 goto leave;
10613 memcpy((*message)->raw, buffer, length);
10614 (*message)->raw_length = length;
10615 break;
10616 case BUFFER_MOVE:
10617 (*message)->raw = (void *) buffer;
10618 (*message)->raw_length = length;
10619 break;
10620 default:
10621 err = IE_ENUM;
10622 goto leave;
10626 leave:
10627 if (err) {
10628 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10629 isds_message_free(message);
10632 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10633 xmlXPathFreeObject(result);
10634 xmlXPathFreeContext(xpath_ctx);
10635 if (!*message || !(*message)->xml) {
10636 xmlFreeDoc(message_doc);
10639 if (!err)
10640 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10641 return err;
10645 /* Determine type of raw message or delivery info according some heuristics.
10646 * It does not validate the raw blob.
10647 * @context is session context
10648 * @raw_type returns content type of @buffer. Valid only if exit code of this
10649 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10650 * reallocated memory.
10651 * @buffer is message raw representation.
10652 * @length is length of buffer in bytes. */
10653 isds_error isds_guess_raw_type(struct isds_ctx *context,
10654 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10655 isds_error err;
10656 void *xml_stream = NULL;
10657 size_t xml_stream_length = 0;
10658 xmlDocPtr document = NULL;
10659 xmlNodePtr root = NULL;
10661 if (!context) return IE_INVALID_CONTEXT;
10662 zfree(context->long_message);
10663 if (length == 0 || !buffer) return IE_INVAL;
10664 if (!raw_type) return IE_INVAL;
10666 /* Try CMS */
10667 err = _isds_extract_cms_data(context, buffer, length,
10668 &xml_stream, &xml_stream_length);
10669 if (err) {
10670 xml_stream = (void *) buffer;
10671 xml_stream_length = (size_t) length;
10672 err = IE_SUCCESS;
10675 /* Try XML */
10676 document = xmlParseMemory(xml_stream, xml_stream_length);
10677 if (!document) {
10678 isds_printf_message(context,
10679 _("Could not parse data as XML document"));
10680 err = IE_NOTSUP;
10681 goto leave;
10684 /* Get root element */
10685 root = xmlDocGetRootElement(document);
10686 if (!root) {
10687 isds_printf_message(context,
10688 _("XML document is missing root element"));
10689 err = IE_XML;
10690 goto leave;
10693 if (!root->ns || !root->ns->href) {
10694 isds_printf_message(context,
10695 _("Root element does not belong to any name space"));
10696 err = IE_NOTSUP;
10697 goto leave;
10700 /* Test name space */
10701 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10702 if (xml_stream == buffer)
10703 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10704 else
10705 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10706 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10707 if (xml_stream == buffer)
10708 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10709 else
10710 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10711 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10712 if (xml_stream == buffer)
10713 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10714 else
10715 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10716 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10717 if (xml_stream != buffer) {
10718 isds_printf_message(context,
10719 _("Document in ISDS name space is encapsulated into CMS" ));
10720 err = IE_NOTSUP;
10721 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10722 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10723 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10724 *raw_type = RAWTYPE_DELIVERYINFO;
10725 else {
10726 isds_printf_message(context,
10727 _("Unknown root element in ISDS name space"));
10728 err = IE_NOTSUP;
10730 } else {
10731 isds_printf_message(context,
10732 _("Unknown name space"));
10733 err = IE_NOTSUP;
10736 leave:
10737 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10738 xmlFreeDoc(document);
10739 return err;
10743 /* Download signed incoming/outgoing message identified by ID.
10744 * @context is session context
10745 * @output is true for outgoing message, false for incoming message
10746 * @message_id is message identifier (you can get them from
10747 * isds_get_list_of_{sent,received}_messages())
10748 * @message is automatically reallocated message retrieved from ISDS. The raw
10749 * member will be filled with PKCS#7 structure in DER format. */
10750 static isds_error isds_get_signed_message(struct isds_ctx *context,
10751 const _Bool outgoing, const char *message_id,
10752 struct isds_message **message) {
10754 isds_error err = IE_SUCCESS;
10755 #if HAVE_LIBCURL
10756 xmlDocPtr response = NULL;
10757 xmlChar *code = NULL, *status_message = NULL;
10758 xmlXPathContextPtr xpath_ctx = NULL;
10759 xmlXPathObjectPtr result = NULL;
10760 char *encoded_structure = NULL;
10761 void *raw = NULL;
10762 size_t raw_length = 0;
10763 #endif
10765 if (!context) return IE_INVALID_CONTEXT;
10766 zfree(context->long_message);
10767 if (!message) return IE_INVAL;
10768 isds_message_free(message);
10770 #if HAVE_LIBCURL
10771 /* Do request and check for success */
10772 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10773 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10774 BAD_CAST "SignedMessageDownload",
10775 message_id, &response, NULL, NULL, &code, &status_message);
10776 if (err) goto leave;
10778 /* Find signed message, extract it into raw and maybe free
10779 * response */
10780 err = find_extract_signed_data_free_response(context,
10781 (xmlChar *)message_id, &response,
10782 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10783 BAD_CAST "SignedMessageDownload",
10784 &raw, &raw_length);
10785 if (err) goto leave;
10787 /* Parse message */
10788 err = isds_load_message(context,
10789 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10790 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10791 raw, raw_length, message, BUFFER_MOVE);
10792 if (err) goto leave;
10794 raw = NULL;
10796 leave:
10797 if (err) {
10798 isds_message_free(message);
10801 free(encoded_structure);
10802 xmlXPathFreeObject(result);
10803 xmlXPathFreeContext(xpath_ctx);
10804 free(raw);
10806 free(code);
10807 free(status_message);
10808 xmlFreeDoc(response);
10810 if (!err)
10811 isds_log(ILF_ISDS, ILL_DEBUG,
10812 (outgoing) ?
10813 _("SignedSentMessageDownload request processed by server "
10814 "successfully.\n") :
10815 _("SignedMessageDownload request processed by server "
10816 "successfully.\n")
10818 #else /* not HAVE_LIBCURL */
10819 err = IE_NOTSUP;
10820 #endif
10821 return err;
10825 /* Download signed incoming message identified by ID.
10826 * @context is session context
10827 * @message_id is message identifier (you can get them from
10828 * isds_get_list_of_received_messages())
10829 * @message is automatically reallocated message retrieved from ISDS. The raw
10830 * member will be filled with PKCS#7 structure in DER format. */
10831 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10832 const char *message_id, struct isds_message **message) {
10833 return isds_get_signed_message(context, 0, message_id, message);
10837 /* Download signed outgoing message identified by ID.
10838 * @context is session context
10839 * @message_id is message identifier (you can get them from
10840 * isds_get_list_of_sent_messages())
10841 * @message is automatically reallocated message retrieved from ISDS. The raw
10842 * member will be filled with PKCS#7 structure in DER format. */
10843 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10844 const char *message_id, struct isds_message **message) {
10845 return isds_get_signed_message(context, 1, message_id, message);
10849 /* Get type and name of user who sent a message identified by ID.
10850 * @context is session context
10851 * @message_id is message identifier
10852 * @sender_type is pointer to automatically allocated type of sender detected
10853 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10854 * library or to the server, NULL will be returned. Pass NULL if you don't
10855 * care about it.
10856 * @raw_sender_type is automatically reallocated UTF-8 string describing
10857 * sender type or NULL if not known to server. Pass NULL if you don't care.
10858 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10859 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10860 isds_error isds_get_message_sender(struct isds_ctx *context,
10861 const char *message_id, isds_sender_type **sender_type,
10862 char **raw_sender_type, char **sender_name) {
10863 isds_error err = IE_SUCCESS;
10864 #if HAVE_LIBCURL
10865 xmlDocPtr response = NULL;
10866 xmlChar *code = NULL, *status_message = NULL;
10867 xmlXPathContextPtr xpath_ctx = NULL;
10868 xmlXPathObjectPtr result = NULL;
10869 char *type_string = NULL;
10870 #endif
10872 if (!context) return IE_INVALID_CONTEXT;
10873 zfree(context->long_message);
10874 if (sender_type) zfree(*sender_type);
10875 if (raw_sender_type) zfree(*raw_sender_type);
10876 if (sender_name) zfree(*sender_name);
10877 if (!message_id) return IE_INVAL;
10879 #if HAVE_LIBCURL
10880 /* Do request and check for success */
10881 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10882 BAD_CAST "GetMessageAuthor",
10883 message_id, &response, NULL, NULL, &code, &status_message);
10884 if (err) goto leave;
10886 /* Extract data */
10887 xpath_ctx = xmlXPathNewContext(response);
10888 if (!xpath_ctx) {
10889 err = IE_ERROR;
10890 goto leave;
10892 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10893 err = IE_ERROR;
10894 goto leave;
10896 result = xmlXPathEvalExpression(
10897 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10898 if (!result) {
10899 err = IE_ERROR;
10900 goto leave;
10902 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10903 isds_log_message(context,
10904 _("Missing GetMessageAuthorResponse element"));
10905 err = IE_ISDS;
10906 goto leave;
10908 if (result->nodesetval->nodeNr > 1) {
10909 isds_log_message(context,
10910 _("Multiple GetMessageAuthorResponse element"));
10911 err = IE_ISDS;
10912 goto leave;
10914 xpath_ctx->node = result->nodesetval->nodeTab[0];
10915 xmlXPathFreeObject(result); result = NULL;
10917 /* Fill output arguments in */
10918 EXTRACT_STRING("isds:userType", type_string);
10919 if (NULL != type_string) {
10920 if (NULL != sender_type) {
10921 *sender_type = calloc(1, sizeof(**sender_type));
10922 if (NULL == *sender_type) {
10923 err = IE_NOMEM;
10924 goto leave;
10927 err = string2isds_sender_type((xmlChar *)type_string,
10928 *sender_type);
10929 if (err) {
10930 zfree(*sender_type);
10931 if (err == IE_ENUM) {
10932 err = IE_SUCCESS;
10933 char *type_string_locale = _isds_utf82locale(type_string);
10934 isds_log(ILF_ISDS, ILL_WARNING,
10935 _("Unknown isds:userType value: %s"),
10936 type_string_locale);
10937 free(type_string_locale);
10942 if (NULL != sender_name)
10943 EXTRACT_STRING("isds:authorName", *sender_name);
10945 leave:
10946 if (err) {
10947 if (NULL != sender_type) zfree(*sender_type);
10948 zfree(type_string);
10949 if (NULL != sender_name) zfree(*sender_name);
10951 if (NULL != raw_sender_type) *raw_sender_type = type_string;
10953 xmlXPathFreeObject(result);
10954 xmlXPathFreeContext(xpath_ctx);
10956 free(code);
10957 free(status_message);
10958 xmlFreeDoc(response);
10960 if (!err)
10961 isds_log(ILF_ISDS, ILL_DEBUG,
10962 _("GetMessageAuthor request processed by server "
10963 "successfully.\n"));
10964 #else /* not HAVE_LIBCURL */
10965 err = IE_NOTSUP;
10966 #endif
10967 return err;
10971 /* Retrieve hash of message identified by ID stored in ISDS.
10972 * @context is session context
10973 * @message_id is message identifier
10974 * @hash is automatically reallocated message hash downloaded from ISDS.
10975 * Message must exist in system and must not be deleted. */
10976 isds_error isds_download_message_hash(struct isds_ctx *context,
10977 const char *message_id, struct isds_hash **hash) {
10979 isds_error err = IE_SUCCESS;
10980 #if HAVE_LIBCURL
10981 xmlDocPtr response = NULL;
10982 xmlChar *code = NULL, *status_message = NULL;
10983 xmlXPathContextPtr xpath_ctx = NULL;
10984 xmlXPathObjectPtr result = NULL;
10985 #endif
10987 if (!context) return IE_INVALID_CONTEXT;
10988 zfree(context->long_message);
10990 isds_hash_free(hash);
10992 #if HAVE_LIBCURL
10993 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10994 BAD_CAST "VerifyMessage", message_id,
10995 &response, NULL, NULL, &code, &status_message);
10996 if (err) goto leave;
10999 /* Extract data */
11000 xpath_ctx = xmlXPathNewContext(response);
11001 if (!xpath_ctx) {
11002 err = IE_ERROR;
11003 goto leave;
11005 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11006 err = IE_ERROR;
11007 goto leave;
11009 result = xmlXPathEvalExpression(
11010 BAD_CAST "/isds:VerifyMessageResponse",
11011 xpath_ctx);
11012 if (!result) {
11013 err = IE_ERROR;
11014 goto leave;
11016 /* Empty response */
11017 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11018 char *message_id_locale = _isds_utf82locale((char*) message_id);
11019 isds_printf_message(context,
11020 _("Server did not return any response for ID `%s' "
11021 "on VerifyMessage request"), message_id_locale);
11022 free(message_id_locale);
11023 err = IE_ISDS;
11024 goto leave;
11026 /* More responses */
11027 if (result->nodesetval->nodeNr > 1) {
11028 char *message_id_locale = _isds_utf82locale((char*) message_id);
11029 isds_printf_message(context,
11030 _("Server did return more responses for ID `%s' "
11031 "on VerifyMessage request"), message_id_locale);
11032 free(message_id_locale);
11033 err = IE_ISDS;
11034 goto leave;
11036 /* One response */
11037 xpath_ctx->node = result->nodesetval->nodeTab[0];
11039 /* Extract the hash */
11040 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11042 leave:
11043 if (err) {
11044 isds_hash_free(hash);
11047 xmlXPathFreeObject(result);
11048 xmlXPathFreeContext(xpath_ctx);
11050 free(code);
11051 free(status_message);
11052 xmlFreeDoc(response);
11054 if (!err)
11055 isds_log(ILF_ISDS, ILL_DEBUG,
11056 _("VerifyMessage request processed by server "
11057 "successfully.\n")
11059 #else /* not HAVE_LIBCURL */
11060 err = IE_NOTSUP;
11061 #endif
11062 return err;
11066 /* Erase message specified by @message_id from long term storage. Other
11067 * message cannot be erased on user request.
11068 * @context is session context
11069 * @message_id is message identifier.
11070 * @incoming is true for incoming message, false for outgoing message.
11071 * @return
11072 * IE_SUCCESS if message has ben removed
11073 * IE_INVAL if message does not exist in long term storage or message
11074 * belongs to different box
11075 * TODO: IE_NOEPRM if user has no permission to erase a message */
11076 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11077 const char *message_id, _Bool incoming) {
11078 isds_error err = IE_SUCCESS;
11079 #if HAVE_LIBCURL
11080 xmlNodePtr request = NULL, node;
11081 xmlNsPtr isds_ns = NULL;
11082 xmlDocPtr response = NULL;
11083 xmlChar *code = NULL, *status_message = NULL;
11084 #endif
11086 if (!context) return IE_INVALID_CONTEXT;
11087 zfree(context->long_message);
11088 if (NULL == message_id) return IE_INVAL;
11090 /* Check if connection is established
11091 * TODO: This check should be done downstairs. */
11092 if (!context->curl) return IE_CONNECTION_CLOSED;
11094 #if HAVE_LIBCURL
11095 /* Build request */
11096 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11097 if (!request) {
11098 isds_log_message(context,
11099 _("Could build EraseMessage request"));
11100 return IE_ERROR;
11102 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11103 if(!isds_ns) {
11104 isds_log_message(context, _("Could not create ISDS name space"));
11105 xmlFreeNode(request);
11106 return IE_ERROR;
11108 xmlSetNs(request, isds_ns);
11110 err = validate_message_id_length(context, (xmlChar *) message_id);
11111 if (err) goto leave;
11112 INSERT_STRING(request, "dmID", message_id);
11114 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11117 /* Send request */
11118 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11119 "message ID %s to ISDS\n"), message_id);
11120 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11121 xmlFreeNode(request); request = NULL;
11123 if (err) {
11124 isds_log(ILF_ISDS, ILL_DEBUG,
11125 _("Processing ISDS response on EraseMessage request "
11126 "failed\n"));
11127 goto leave;
11130 /* Check for response status */
11131 err = isds_response_status(context, SERVICE_DM_INFO, response,
11132 &code, &status_message, NULL);
11133 if (err) {
11134 isds_log(ILF_ISDS, ILL_DEBUG,
11135 _("ISDS response on EraseMessage request is missing "
11136 "status\n"));
11137 goto leave;
11140 /* Check server status code */
11141 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11142 isds_log_message(context, _("Message to erase belongs to other box"));
11143 err = IE_INVAL;
11144 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11145 isds_log_message(context, _("Message to erase is not saved in "
11146 "long term storage or the direction does not match"));
11147 err = IE_INVAL;
11148 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11149 char *code_locale = _isds_utf82locale((char*) code);
11150 char *message_locale = _isds_utf82locale((char*) status_message);
11151 isds_log(ILF_ISDS, ILL_DEBUG,
11152 _("Server refused EraseMessage request "
11153 "(code=%s, message=%s)\n"),
11154 code_locale, message_locale);
11155 isds_log_message(context, message_locale);
11156 free(code_locale);
11157 free(message_locale);
11158 err = IE_ISDS;
11159 goto leave;
11162 leave:
11163 free(code);
11164 free(status_message);
11165 xmlFreeDoc(response);
11166 xmlFreeNode(request);
11168 if (!err)
11169 isds_log(ILF_ISDS, ILL_DEBUG,
11170 _("EraseMessage request processed by server "
11171 "successfully.\n")
11173 #else /* not HAVE_LIBCURL */
11174 err = IE_NOTSUP;
11175 #endif
11176 return err;
11180 /* Mark message as read. This is a transactional commit function to acknowledge
11181 * to ISDS the message has been downloaded and processed by client properly.
11182 * @context is session context
11183 * @message_id is message identifier. */
11184 isds_error isds_mark_message_read(struct isds_ctx *context,
11185 const char *message_id) {
11187 isds_error err = IE_SUCCESS;
11188 #if HAVE_LIBCURL
11189 xmlDocPtr response = NULL;
11190 xmlChar *code = NULL, *status_message = NULL;
11191 #endif
11193 if (!context) return IE_INVALID_CONTEXT;
11194 zfree(context->long_message);
11196 #if HAVE_LIBCURL
11197 /* Do request and check for success */
11198 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11199 BAD_CAST "MarkMessageAsDownloaded", message_id,
11200 &response, NULL, NULL, &code, &status_message);
11202 free(code);
11203 free(status_message);
11204 xmlFreeDoc(response);
11206 if (!err)
11207 isds_log(ILF_ISDS, ILL_DEBUG,
11208 _("MarkMessageAsDownloaded request processed by server "
11209 "successfully.\n")
11211 #else /* not HAVE_LIBCURL */
11212 err = IE_NOTSUP;
11213 #endif
11214 return err;
11218 /* Mark message as received by recipient. This is applicable only to
11219 * commercial message. Use envelope->dmType message member to distinguish
11220 * commercial message from government message. Government message is
11221 * received automatically (by law), commercial message on recipient request.
11222 * @context is session context
11223 * @message_id is message identifier. */
11224 isds_error isds_mark_message_received(struct isds_ctx *context,
11225 const char *message_id) {
11227 isds_error err = IE_SUCCESS;
11228 #if HAVE_LIBCURL
11229 xmlDocPtr response = NULL;
11230 xmlChar *code = NULL, *status_message = NULL;
11231 #endif
11233 if (!context) return IE_INVALID_CONTEXT;
11234 zfree(context->long_message);
11236 #if HAVE_LIBCURL
11237 /* Do request and check for success */
11238 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11239 BAD_CAST "ConfirmDelivery", message_id,
11240 &response, NULL, NULL, &code, &status_message);
11242 free(code);
11243 free(status_message);
11244 xmlFreeDoc(response);
11246 if (!err)
11247 isds_log(ILF_ISDS, ILL_DEBUG,
11248 _("ConfirmDelivery request processed by server "
11249 "successfully.\n")
11251 #else /* not HAVE_LIBCURL */
11252 err = IE_NOTSUP;
11253 #endif
11254 return err;
11258 /* Send document for authorized conversion into Czech POINT system.
11259 * This is public anonymous service, no log-in necessary. Special context is
11260 * used to reuse keep-a-live HTTPS connection.
11261 * @context is Czech POINT session context. DO NOT use context connected to
11262 * ISDS server. Use new context or context used by this function previously.
11263 * @document is document to convert. Only data, data_length, dmFileDescr and
11264 * is_xml members are significant. Be ware that not all document formats can be
11265 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11266 * @id is reallocated identifier assigned by Czech POINT system to
11267 * your document on submit. Use is to tell it to Czech POINT officer.
11268 * @date is reallocated document submit date (submitted documents
11269 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11270 * value. */
11271 isds_error czp_convert_document(struct isds_ctx *context,
11272 const struct isds_document *document,
11273 char **id, struct tm **date) {
11274 isds_error err = IE_SUCCESS;
11275 #if HAVE_LIBCURL
11276 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11277 xmlNodePtr request = NULL, node;
11278 xmlDocPtr response = NULL;
11280 xmlXPathContextPtr xpath_ctx = NULL;
11281 xmlXPathObjectPtr result = NULL;
11282 long int status = -1;
11283 long int *status_ptr = &status;
11284 char *string = NULL;
11285 #endif
11288 if (!context) return IE_INVALID_CONTEXT;
11289 zfree(context->long_message);
11290 if (!document || !id || !date) return IE_INVAL;
11292 if (document->is_xml) {
11293 isds_log_message(context,
11294 _("XML documents cannot be submitted to conversion"));
11295 return IE_NOTSUP;
11298 /* Free output arguments */
11299 zfree(*id);
11300 zfree(*date);
11302 #if HAVE_LIBCURL
11303 /* Store configuration */
11304 context->type = CTX_TYPE_CZP;
11305 free(context->url);
11306 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11307 if (!(context->url))
11308 return IE_NOMEM;
11310 /* Prepare CURL handle if not yet connected */
11311 if (!context->curl) {
11312 context->curl = curl_easy_init();
11313 if (!(context->curl))
11314 return IE_ERROR;
11317 /* Build conversion request */
11318 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11319 if (!request) {
11320 isds_log_message(context,
11321 _("Could not build Czech POINT conversion request"));
11322 return IE_ERROR;
11324 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11325 if(!deposit_ns) {
11326 isds_log_message(context,
11327 _("Could not create Czech POINT deposit name space"));
11328 xmlFreeNode(request);
11329 return IE_ERROR;
11331 xmlSetNs(request, deposit_ns);
11333 /* Insert children. They are in empty namespace! */
11334 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11335 if(!empty_ns) {
11336 isds_log_message(context, _("Could not create empty name space"));
11337 err = IE_ERROR;
11338 goto leave;
11340 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11341 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11342 document->dmFileDescr);
11344 /* Document encoded in Base64 */
11345 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11346 document->data, document->data_length);
11347 if (err) goto leave;
11349 isds_log(ILF_ISDS, ILL_DEBUG,
11350 _("Submitting document for conversion into Czech POINT deposit"));
11352 /* Send conversion request */
11353 err = _czp_czpdeposit(context, request, &response);
11354 xmlFreeNode(request); request = NULL;
11356 if (err) {
11357 czp_do_close_connection(context);
11358 goto leave;
11362 /* Extract response */
11363 xpath_ctx = xmlXPathNewContext(response);
11364 if (!xpath_ctx) {
11365 err = IE_ERROR;
11366 goto leave;
11368 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11369 err = IE_ERROR;
11370 goto leave;
11372 result = xmlXPathEvalExpression(
11373 BAD_CAST "/deposit:saveDocumentResponse/return",
11374 xpath_ctx);
11375 if (!result) {
11376 err = IE_ERROR;
11377 goto leave;
11379 /* Empty response */
11380 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11381 isds_printf_message(context,
11382 _("Missing `return' element in Czech POINT deposit response"));
11383 err = IE_ISDS;
11384 goto leave;
11386 /* More responses */
11387 if (result->nodesetval->nodeNr > 1) {
11388 isds_printf_message(context,
11389 _("Multiple `return' element in Czech POINT deposit response"));
11390 err = IE_ISDS;
11391 goto leave;
11393 /* One response */
11394 xpath_ctx->node = result->nodesetval->nodeTab[0];
11396 /* Get status */
11397 EXTRACT_LONGINT("status", status_ptr, 1);
11398 if (status) {
11399 EXTRACT_STRING("statusMsg", string);
11400 char *string_locale = _isds_utf82locale(string);
11401 isds_printf_message(context,
11402 _("Czech POINT deposit refused document for conversion "
11403 "(code=%ld, message=%s)"),
11404 status, string_locale);
11405 free(string_locale);
11406 err = IE_ISDS;
11407 goto leave;
11410 /* Get document ID */
11411 EXTRACT_STRING("documentID", *id);
11413 /* Get submit date */
11414 EXTRACT_STRING("dateInserted", string);
11415 if (string) {
11416 *date = calloc(1, sizeof(**date));
11417 if (!*date) {
11418 err = IE_NOMEM;
11419 goto leave;
11421 err = _isds_datestring2tm((xmlChar *)string, *date);
11422 if (err) {
11423 if (err == IE_NOTSUP) {
11424 err = IE_ISDS;
11425 char *string_locale = _isds_utf82locale(string);
11426 isds_printf_message(context,
11427 _("Invalid dateInserted value: %s"), string_locale);
11428 free(string_locale);
11430 goto leave;
11434 leave:
11435 free(string);
11436 xmlXPathFreeObject(result);
11437 xmlXPathFreeContext(xpath_ctx);
11439 xmlFreeDoc(response);
11440 xmlFreeNode(request);
11442 if (!err) {
11443 char *id_locale = _isds_utf82locale((char *) *id);
11444 isds_log(ILF_ISDS, ILL_DEBUG,
11445 _("Document %s has been submitted for conversion "
11446 "to server successfully\n"), id_locale);
11447 free(id_locale);
11449 #else /* not HAVE_LIBCURL */
11450 err = IE_NOTSUP;
11451 #endif
11452 return err;
11456 /* Close possibly opened connection to Czech POINT document deposit.
11457 * @context is Czech POINT session context. */
11458 isds_error czp_close_connection(struct isds_ctx *context) {
11459 if (!context) return IE_INVALID_CONTEXT;
11460 zfree(context->long_message);
11461 #if HAVE_LIBCURL
11462 return czp_do_close_connection(context);
11463 #else
11464 return IE_NOTSUP;
11465 #endif
11469 /* Send request for new box creation in testing ISDS instance.
11470 * It's not possible to request for a production box currently, as it
11471 * communicates via e-mail.
11472 * XXX: This function does not work either. Server complains about invalid
11473 * e-mail address.
11474 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11475 * this function
11476 * @context is special session context for box creation request. DO NOT use
11477 * standard context as it could reveal your password. Use fresh new context or
11478 * context previously used by this function.
11479 * @box is box description to create including single primary user (in case of
11480 * FO box type). It outputs box ID assigned by ISDS in dbID element.
11481 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11482 * box, or contact address of PFO box owner). The email member is mandatory as
11483 * it will be used to deliver credentials.
11484 * @former_names is former name of box owner. Pass NULL if you don't care.
11485 * @approval is optional external approval of box manipulation
11486 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11487 * NULL, if you don't care.*/
11488 isds_error isds_request_new_testing_box(struct isds_ctx *context,
11489 struct isds_DbOwnerInfo *box, const struct isds_list *users,
11490 const char *former_names, const struct isds_approval *approval,
11491 char **refnumber) {
11492 isds_error err = IE_SUCCESS;
11493 #if HAVE_LIBCURL
11494 xmlNodePtr request = NULL;
11495 xmlDocPtr response = NULL;
11496 xmlXPathContextPtr xpath_ctx = NULL;
11497 xmlXPathObjectPtr result = NULL;
11498 #endif
11501 if (!context) return IE_INVALID_CONTEXT;
11502 zfree(context->long_message);
11503 if (!box) return IE_INVAL;
11505 #if HAVE_LIBCURL
11506 if (!box->email || box->email[0] == '\0') {
11507 isds_log_message(context, _("E-mail field is mandatory"));
11508 return IE_INVAL;
11511 /* Scratch box ID */
11512 zfree(box->dbID);
11514 /* Store configuration */
11515 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
11516 free(context->url);
11517 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
11518 if (!(context->url))
11519 return IE_NOMEM;
11521 /* Prepare CURL handle if not yet connected */
11522 if (!context->curl) {
11523 context->curl = curl_easy_init();
11524 if (!(context->curl))
11525 return IE_ERROR;
11528 /* Build CreateDataBox request */
11529 err = build_CreateDBInput_request(context,
11530 &request, BAD_CAST "CreateDataBox",
11531 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
11532 if (err) goto leave;
11534 /* Send it to server and process response */
11535 err = send_destroy_request_check_response(context,
11536 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
11537 &response, (xmlChar **) refnumber, NULL);
11538 if (err) goto leave;
11540 /* Extract box ID */
11541 xpath_ctx = xmlXPathNewContext(response);
11542 if (!xpath_ctx) {
11543 err = IE_ERROR;
11544 goto leave;
11546 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11547 err = IE_ERROR;
11548 goto leave;
11550 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
11552 leave:
11553 xmlXPathFreeObject(result);
11554 xmlXPathFreeContext(xpath_ctx);
11555 xmlFreeDoc(response);
11556 xmlFreeNode(request);
11558 if (!err) {
11559 isds_log(ILF_ISDS, ILL_DEBUG,
11560 _("CreateDataBox request processed by server successfully.\n"));
11562 #else /* not HAVE_LIBCURL */
11563 err = IE_NOTSUP;
11564 #endif
11566 return err;
11570 /* Submit CMS signed message to ISDS to verify its originality. This is
11571 * stronger form of isds_verify_message_hash() because ISDS does more checks
11572 * than simple one (potentialy old weak) hash comparison.
11573 * @context is session context
11574 * @message is memory with raw CMS signed message bit stream
11575 * @length is @message size in bytes
11576 * @return
11577 * IE_SUCCESS if message originates in ISDS
11578 * IE_NOTEQUAL if message is unknown to ISDS
11579 * other code for other errors */
11580 isds_error isds_authenticate_message(struct isds_ctx *context,
11581 const void *message, size_t length) {
11582 isds_error err = IE_SUCCESS;
11583 #if HAVE_LIBCURL
11584 xmlNsPtr isds_ns = NULL;
11585 xmlNodePtr request = NULL;
11586 xmlDocPtr response = NULL;
11587 xmlXPathContextPtr xpath_ctx = NULL;
11588 xmlXPathObjectPtr result = NULL;
11589 _Bool *authentic = NULL;
11590 #endif
11592 if (!context) return IE_INVALID_CONTEXT;
11593 zfree(context->long_message);
11594 if (!message || length == 0) return IE_INVAL;
11596 #if HAVE_LIBCURL
11597 /* Check if connection is established
11598 * TODO: This check should be done downstairs. */
11599 if (!context->curl) return IE_CONNECTION_CLOSED;
11602 /* Build AuthenticateMessage request */
11603 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11604 if (!request) {
11605 isds_log_message(context,
11606 _("Could not build AuthenticateMessage request"));
11607 return IE_ERROR;
11609 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11610 if(!isds_ns) {
11611 isds_log_message(context, _("Could not create ISDS name space"));
11612 xmlFreeNode(request);
11613 return IE_ERROR;
11615 xmlSetNs(request, isds_ns);
11617 /* Insert Base64 encoded message */
11618 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11619 message, length);
11620 if (err) goto leave;
11622 /* Send request to server and process response */
11623 err = send_destroy_request_check_response(context,
11624 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11625 &response, NULL, NULL);
11626 if (err) goto leave;
11629 /* ISDS has decided */
11630 xpath_ctx = xmlXPathNewContext(response);
11631 if (!xpath_ctx) {
11632 err = IE_ERROR;
11633 goto leave;
11635 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11636 err = IE_ERROR;
11637 goto leave;
11640 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11642 if (!authentic) {
11643 isds_log_message(context,
11644 _("Server did not return any response on "
11645 "AuthenticateMessage request"));
11646 err = IE_ISDS;
11647 goto leave;
11649 if (*authentic) {
11650 isds_log(ILF_ISDS, ILL_DEBUG,
11651 _("ISDS authenticated the message successfully\n"));
11652 } else {
11653 isds_log_message(context, _("ISDS does not know the message"));
11654 err = IE_NOTEQUAL;
11658 leave:
11659 free(authentic);
11660 xmlXPathFreeObject(result);
11661 xmlXPathFreeContext(xpath_ctx);
11663 xmlFreeDoc(response);
11664 xmlFreeNode(request);
11665 #else /* not HAVE_LIBCURL */
11666 err = IE_NOTSUP;
11667 #endif
11669 return err;
11673 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11674 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11675 * be re-signed.
11676 * @context is session context
11677 * @input_data is memory with raw CMS signed message or delivery info bit
11678 * stream to re-sign
11679 * @input_length is @input_data size in bytes
11680 * @output_data is pointer to auto-allocated memory where to store re-signed
11681 * input data blob. Caller must free it.
11682 * @output_data is pointer where to store @output_data size in bytes
11683 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11684 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11685 * @return
11686 * IE_SUCCESS if CMS blob has been re-signed successfully
11687 * other code for other errors */
11688 isds_error isds_resign_message(struct isds_ctx *context,
11689 const void *input_data, size_t input_length,
11690 void **output_data, size_t *output_length, struct tm **valid_to) {
11691 isds_error err = IE_SUCCESS;
11692 #if HAVE_LIBCURL
11693 xmlNsPtr isds_ns = NULL;
11694 xmlNodePtr request = NULL;
11695 xmlDocPtr response = NULL;
11696 xmlXPathContextPtr xpath_ctx = NULL;
11697 xmlXPathObjectPtr result = NULL;
11698 char *string = NULL;
11699 const xmlChar *codes[] = {
11700 BAD_CAST "2200",
11701 BAD_CAST "2201",
11702 BAD_CAST "2204",
11703 BAD_CAST "2207",
11704 NULL
11706 const char *meanings[] = {
11707 "Message is bad",
11708 "Message is not original",
11709 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11710 "Time stamp could not been generated in time"
11712 const isds_error errors[] = {
11713 IE_INVAL,
11714 IE_NOTUNIQ,
11715 IE_INVAL,
11716 IE_ISDS,
11718 struct code_map_isds_error map = {
11719 .codes = codes,
11720 .meanings = meanings,
11721 .errors = errors
11723 #endif
11725 if (NULL != output_data) *output_data = NULL;
11726 if (NULL != output_length) *output_length = 0;
11727 if (NULL != valid_to) *valid_to = NULL;
11729 if (NULL == context) return IE_INVALID_CONTEXT;
11730 zfree(context->long_message);
11731 if (NULL == input_data || 0 == input_length) {
11732 isds_log_message(context, _("Empty CMS blob on input"));
11733 return IE_INVAL;
11735 if (NULL == output_data || NULL == output_length) {
11736 isds_log_message(context,
11737 _("NULL pointer provided for output CMS blob"));
11738 return IE_INVAL;
11741 #if HAVE_LIBCURL
11742 /* Check if connection is established
11743 * TODO: This check should be done downstairs. */
11744 if (!context->curl) return IE_CONNECTION_CLOSED;
11747 /* Build Re-signISDSDocument request */
11748 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11749 if (!request) {
11750 isds_log_message(context,
11751 _("Could not build Re-signISDSDocument request"));
11752 return IE_ERROR;
11754 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11755 if(!isds_ns) {
11756 isds_log_message(context, _("Could not create ISDS name space"));
11757 xmlFreeNode(request);
11758 return IE_ERROR;
11760 xmlSetNs(request, isds_ns);
11762 /* Insert Base64 encoded CMS blob */
11763 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11764 input_data, input_length);
11765 if (err) goto leave;
11767 /* Send request to server and process response */
11768 err = send_destroy_request_check_response(context,
11769 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11770 &response, NULL, &map);
11771 if (err) goto leave;
11774 /* Extract re-signed data */
11775 xpath_ctx = xmlXPathNewContext(response);
11776 if (!xpath_ctx) {
11777 err = IE_ERROR;
11778 goto leave;
11780 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11781 err = IE_ERROR;
11782 goto leave;
11784 result = xmlXPathEvalExpression(
11785 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11786 if (!result) {
11787 err = IE_ERROR;
11788 goto leave;
11790 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11791 isds_log_message(context,
11792 _("Missing Re-signISDSDocumentResponse element"));
11793 err = IE_ISDS;
11794 goto leave;
11796 if (result->nodesetval->nodeNr > 1) {
11797 isds_log_message(context,
11798 _("Multiple Re-signISDSDocumentResponse element"));
11799 err = IE_ISDS;
11800 goto leave;
11802 xpath_ctx->node = result->nodesetval->nodeTab[0];
11803 xmlXPathFreeObject(result); result = NULL;
11805 EXTRACT_STRING("isds:dmResultDoc", string);
11806 /* Decode non-empty data */
11807 if (NULL != string && string[0] != '\0') {
11808 *output_length = _isds_b64decode(string, output_data);
11809 if (*output_length == (size_t) -1) {
11810 isds_log_message(context,
11811 _("Error while Base64-decoding re-signed data"));
11812 err = IE_ERROR;
11813 goto leave;
11815 } else {
11816 isds_log_message(context, _("Server did not send re-signed data"));
11817 err = IE_ISDS;
11818 goto leave;
11820 zfree(string);
11822 if (NULL != valid_to) {
11823 /* Get time stamp expiration date */
11824 EXTRACT_STRING("isds:dmValidTo", string);
11825 if (NULL != string) {
11826 *valid_to = calloc(1, sizeof(**valid_to));
11827 if (!*valid_to) {
11828 err = IE_NOMEM;
11829 goto leave;
11831 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11832 if (err) {
11833 if (err == IE_NOTSUP) {
11834 err = IE_ISDS;
11835 char *string_locale = _isds_utf82locale(string);
11836 isds_printf_message(context,
11837 _("Invalid dmValidTo value: %s"), string_locale);
11838 free(string_locale);
11840 goto leave;
11845 leave:
11846 free(string);
11848 xmlXPathFreeObject(result);
11849 xmlXPathFreeContext(xpath_ctx);
11851 xmlFreeDoc(response);
11852 xmlFreeNode(request);
11853 #else /* not HAVE_LIBCURL */
11854 err = IE_NOTSUP;
11855 #endif
11857 return err;
11860 #undef INSERT_ELEMENT
11861 #undef CHECK_FOR_STRING_LENGTH
11862 #undef INSERT_STRING_ATTRIBUTE
11863 #undef INSERT_ULONGINTNOPTR
11864 #undef INSERT_ULONGINT
11865 #undef INSERT_LONGINT
11866 #undef INSERT_BOOLEAN
11867 #undef INSERT_SCALAR_BOOLEAN
11868 #undef INSERT_STRING
11869 #undef INSERT_STRING_WITH_NS
11870 #undef EXTRACT_STRING_ATTRIBUTE
11871 #undef EXTRACT_ULONGINT
11872 #undef EXTRACT_LONGINT
11873 #undef EXTRACT_BOOLEAN
11874 #undef EXTRACT_STRING
11877 /* Compute hash of message from raw representation and store it into envelope.
11878 * Original hash structure will be destroyed in envelope.
11879 * @context is session context
11880 * @message is message carrying raw XML message blob
11881 * @algorithm is desired hash algorithm to use */
11882 isds_error isds_compute_message_hash(struct isds_ctx *context,
11883 struct isds_message *message, const isds_hash_algorithm algorithm) {
11884 isds_error err = IE_SUCCESS;
11885 const char *nsuri;
11886 void *xml_stream = NULL;
11887 size_t xml_stream_length;
11888 size_t phys_start, phys_end;
11889 char *phys_path = NULL;
11890 struct isds_hash *new_hash = NULL;
11893 if (!context) return IE_INVALID_CONTEXT;
11894 zfree(context->long_message);
11895 if (!message) return IE_INVAL;
11897 if (!message->raw) {
11898 isds_log_message(context,
11899 _("Message does not carry raw representation"));
11900 return IE_INVAL;
11903 switch (message->raw_type) {
11904 case RAWTYPE_INCOMING_MESSAGE:
11905 nsuri = ISDS_NS;
11906 xml_stream = message->raw;
11907 xml_stream_length = message->raw_length;
11908 break;
11910 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11911 nsuri = SISDS_INCOMING_NS;
11912 xml_stream = message->raw;
11913 xml_stream_length = message->raw_length;
11914 break;
11916 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11917 nsuri = SISDS_INCOMING_NS;
11918 err = _isds_extract_cms_data(context,
11919 message->raw, message->raw_length,
11920 &xml_stream, &xml_stream_length);
11921 if (err) goto leave;
11922 break;
11924 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11925 nsuri = SISDS_OUTGOING_NS;
11926 xml_stream = message->raw;
11927 xml_stream_length = message->raw_length;
11928 break;
11930 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11931 nsuri = SISDS_OUTGOING_NS;
11932 err = _isds_extract_cms_data(context,
11933 message->raw, message->raw_length,
11934 &xml_stream, &xml_stream_length);
11935 if (err) goto leave;
11936 break;
11938 default:
11939 isds_log_message(context, _("Bad raw representation type"));
11940 return IE_INVAL;
11941 break;
11945 /* XXX: Hash is computed from original string representing isds:dmDm
11946 * subtree. That means no encoding, white space, xmlns attributes changes.
11947 * In other words, input for hash can be invalid XML stream. */
11948 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
11949 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11950 PHYSXML_ELEMENT_SEPARATOR,
11951 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
11952 PHYSXML_ELEMENT_SEPARATOR
11953 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
11954 err = IE_NOMEM;
11955 goto leave;
11957 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11958 phys_path, &phys_start, &phys_end);
11959 zfree(phys_path);
11960 if (err) {
11961 isds_log_message(context,
11962 _("Substring with isds:dmDM element could not be located "
11963 "in raw message"));
11964 goto leave;
11968 /* Compute hash */
11969 new_hash = calloc(1, sizeof(*new_hash));
11970 if (!new_hash) {
11971 err = IE_NOMEM;
11972 goto leave;
11974 new_hash->algorithm = algorithm;
11975 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
11976 new_hash);
11977 if (err) {
11978 isds_log_message(context, _("Could not compute message hash"));
11979 goto leave;
11982 /* Save computed hash */
11983 if (!message->envelope) {
11984 message->envelope = calloc(1, sizeof(*message->envelope));
11985 if (!message->envelope) {
11986 err = IE_NOMEM;
11987 goto leave;
11990 isds_hash_free(&message->envelope->hash);
11991 message->envelope->hash = new_hash;
11993 leave:
11994 if (err) {
11995 isds_hash_free(&new_hash);
11998 free(phys_path);
11999 if (xml_stream != message->raw) free(xml_stream);
12000 return err;
12004 /* Compare two hashes.
12005 * @h1 is first hash
12006 * @h2 is another hash
12007 * @return
12008 * IE_SUCCESS if hashes equal
12009 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12010 * IE_ENUM if not comparable, but both structures defined
12011 * IE_INVAL if some of the structures are undefined (NULL)
12012 * IE_ERROR if internal error occurs */
12013 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
12014 if (h1 == NULL || h2 == NULL) return IE_INVAL;
12015 if (h1->algorithm != h2->algorithm) return IE_ENUM;
12016 if (h1->length != h2->length) return IE_ERROR;
12017 if (h1->length > 0 && !h1->value) return IE_ERROR;
12018 if (h2->length > 0 && !h2->value) return IE_ERROR;
12020 for (size_t i = 0; i < h1->length; i++) {
12021 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12022 return IE_NOTEQUAL;
12024 return IE_SUCCESS;
12028 /* Check message has gone through ISDS by comparing message hash stored in
12029 * ISDS and locally computed hash. You must provide message with valid raw
12030 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12031 * This is convenient wrapper for isds_download_message_hash(),
12032 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12033 * @context is session context
12034 * @message is message with valid raw and envelope member; envelope->hash
12035 * member will be changed during function run. Use envelope on heap only.
12036 * @return
12037 * IE_SUCCESS if message originates in ISDS
12038 * IE_NOTEQUAL if message is unknown to ISDS
12039 * other code for other errors */
12040 isds_error isds_verify_message_hash(struct isds_ctx *context,
12041 struct isds_message *message) {
12042 isds_error err = IE_SUCCESS;
12043 struct isds_hash *downloaded_hash = NULL;
12045 if (!context) return IE_INVALID_CONTEXT;
12046 zfree(context->long_message);
12047 if (!message) return IE_INVAL;
12049 if (!message->envelope) {
12050 isds_log_message(context,
12051 _("Given message structure is missing envelope"));
12052 return IE_INVAL;
12054 if (!message->raw) {
12055 isds_log_message(context,
12056 _("Given message structure is missing raw representation"));
12057 return IE_INVAL;
12060 err = isds_download_message_hash(context, message->envelope->dmID,
12061 &downloaded_hash);
12062 if (err) goto leave;
12064 err = isds_compute_message_hash(context, message,
12065 downloaded_hash->algorithm);
12066 if (err) goto leave;
12068 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12070 leave:
12071 isds_hash_free(&downloaded_hash);
12072 return err;
12076 /* Search for document by document ID in list of documents. IDs are compared
12077 * as UTF-8 string.
12078 * @documents is list of isds_documents
12079 * @id is document identifier
12080 * @return first matching document or NULL. */
12081 const struct isds_document *isds_find_document_by_id(
12082 const struct isds_list *documents, const char *id) {
12083 const struct isds_list *item;
12084 const struct isds_document *document;
12086 for (item = documents; item; item = item->next) {
12087 document = (struct isds_document *) item->data;
12088 if (!document) continue;
12090 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12091 return document;
12094 return NULL;
12098 /* Normalize @mime_type to be proper MIME type.
12099 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12100 * guess regular MIME type (e.g. "application/pdf").
12101 * @mime_type is UTF-8 encoded MIME type to fix
12102 * @return original @mime_type if no better interpretation exists, or
12103 * constant static UTF-8 encoded string with proper MIME type. */
12104 const char *isds_normalize_mime_type(const char *mime_type) {
12105 if (!mime_type) return NULL;
12107 for (size_t offset = 0;
12108 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12109 offset += 2) {
12110 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12111 extension_map_mime[offset]))
12112 return (const char *) extension_map_mime[offset + 1];
12115 return mime_type;
12119 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12120 struct isds_message **message);
12121 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12122 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12123 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12124 struct isds_address **address);
12126 int isds_message_free(struct isds_message **message);
12127 int isds_address_free(struct isds_address **address);
12131 /* Makes known all relevant namespaces to given XPath context
12132 * @xpath_ctx is XPath context
12133 * @message_ns selects proper message name space. Unsigned and signed
12134 * messages and delivery info's differ in prefix and URI. */
12135 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12136 const message_ns_type message_ns) {
12137 const xmlChar *message_namespace = NULL;
12139 if (!xpath_ctx) return IE_ERROR;
12141 switch(message_ns) {
12142 case MESSAGE_NS_1:
12143 message_namespace = BAD_CAST ISDS1_NS; break;
12144 case MESSAGE_NS_UNSIGNED:
12145 message_namespace = BAD_CAST ISDS_NS; break;
12146 case MESSAGE_NS_SIGNED_INCOMING:
12147 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12148 case MESSAGE_NS_SIGNED_OUTGOING:
12149 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12150 case MESSAGE_NS_SIGNED_DELIVERY:
12151 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12152 default:
12153 return IE_ENUM;
12156 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12157 return IE_ERROR;
12158 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12159 return IE_ERROR;
12160 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12161 return IE_ERROR;
12162 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12163 return IE_ERROR;
12164 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12165 return IE_ERROR;
12166 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12167 return IE_ERROR;
12168 return IE_SUCCESS;