Started using OpenSSL as cryptographic back-end.
[libisds.git] / src / isds.c
blob71f29212259f2cc99bcfe43297ed9f02e99bbc80
1 #include "isds_priv.h"
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include <stdint.h>
8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
9 #include "utils.h"
10 #if HAVE_LIBCURL
11 #include "soap.h"
12 #endif
13 #include "validator.h"
14 #include "crypto.h"
15 #include "physxml.h"
16 #include "system.h"
18 /* Global variables.
19 * Allocated in isds_init() and deallocated in isds_cleanup(). */
20 unsigned int log_facilities;
21 isds_log_level log_level;
22 isds_log_callback log_callback;
23 void *log_callback_data;
24 const char *version_gpgme = "n/a";
25 const char *version_gcrypt = "n/a";
26 const char *version_expat = "n/a";
28 /* Locators */
29 /* Base URL of production ISDS instance */
30 const char isds_locator[] = "https://ws1.mojedatovaschranka.cz/";
31 const char isds_cert_locator[] = "https://ws1c.mojedatovaschranka.cz/";
32 const char isds_otp_locator[] = "https://www.mojedatovaschranka.cz/";
34 /* Base URL of production ISDS instance */
35 const char isds_testing_locator[] = "https://ws1.czebox.cz/";
36 const char isds_cert_testing_locator[] = "https://ws1c.czebox.cz/";
37 const char isds_otp_testing_locator[] = "https://www.czebox.cz/";
39 /* Extension to MIME type map */
40 static const xmlChar *extension_map_mime[] = {
41 BAD_CAST "cer", BAD_CAST "application/x-x509-ca-cert",
42 BAD_CAST "crt", BAD_CAST "application/x-x509-ca-cert",
43 BAD_CAST "der", BAD_CAST "application/x-x509-ca-cert",
44 BAD_CAST "doc", BAD_CAST "application/msword",
45 BAD_CAST "docx", BAD_CAST "application/vnd.openxmlformats-officedocument."
46 "wordprocessingml.document",
47 BAD_CAST "dbf", BAD_CAST "application/octet-stream",
48 BAD_CAST "prj", BAD_CAST "application/octet-stream",
49 BAD_CAST "qix", BAD_CAST "application/octet-stream",
50 BAD_CAST "sbn", BAD_CAST "application/octet-stream",
51 BAD_CAST "sbx", BAD_CAST "application/octet-stream",
52 BAD_CAST "shp", BAD_CAST "application/octet-stream",
53 BAD_CAST "shx", BAD_CAST "application/octet-stream",
54 BAD_CAST "dgn", BAD_CAST "application/octet-stream",
55 BAD_CAST "dwg", BAD_CAST "image/vnd.dwg",
56 BAD_CAST "edi", BAD_CAST "application/edifact",
57 BAD_CAST "fo", BAD_CAST "application/vnd.software602.filler.form+xml",
58 BAD_CAST "gfs", BAD_CAST "application/xml",
59 BAD_CAST "gml", BAD_CAST "application/xml",
60 BAD_CAST "gif", BAD_CAST "image/gif",
61 BAD_CAST "htm", BAD_CAST "text/html",
62 BAD_CAST "html", BAD_CAST "text/html",
63 BAD_CAST "isdoc", BAD_CAST "text/isdoc",
64 BAD_CAST "isdocx", BAD_CAST "text/isdocx",
65 BAD_CAST "jfif", BAD_CAST "image/jpeg",
66 BAD_CAST "jpg", BAD_CAST "image/jpeg",
67 BAD_CAST "jpeg", BAD_CAST "image/jpeg",
68 BAD_CAST "mpeg", BAD_CAST "video/mpeg",
69 BAD_CAST "mpeg1", BAD_CAST "video/mpeg",
70 BAD_CAST "mpeg2", BAD_CAST "video/mpeg",
71 BAD_CAST "mpg", BAD_CAST "video/mpeg",
72 BAD_CAST "mp2", BAD_CAST "audio/mpeg",
73 BAD_CAST "mp3", BAD_CAST "audio/mpeg",
74 BAD_CAST "odp", BAD_CAST "application/vnd.oasis.opendocument.presentation",
75 BAD_CAST "ods", BAD_CAST "application/vnd.oasis.opendocument.spreadsheet",
76 BAD_CAST "odt", BAD_CAST "application/vnd.oasis.opendocument.text",
77 BAD_CAST "pdf", BAD_CAST "application/pdf",
78 BAD_CAST "p7b", BAD_CAST "application/pkcs7-certificates",
79 BAD_CAST "p7c", BAD_CAST "application/pkcs7-mime",
80 BAD_CAST "p7m", BAD_CAST "application/pkcs7-mime",
81 BAD_CAST "p7f", BAD_CAST "application/pkcs7-signature",
82 BAD_CAST "p7s", BAD_CAST "application/pkcs7-signature",
83 BAD_CAST "pk7", BAD_CAST "application/pkcs7-mime",
84 BAD_CAST "png", BAD_CAST "image/png",
85 BAD_CAST "ppt", BAD_CAST "application/vnd.ms-powerpoint",
86 BAD_CAST "pptx", BAD_CAST "application/vnd.openxmlformats-officedocument."
87 "presentationml.presentation",
88 BAD_CAST "rtf", BAD_CAST "application/rtf",
89 BAD_CAST "tif", BAD_CAST "image/tiff",
90 BAD_CAST "tiff", BAD_CAST "image/tiff",
91 BAD_CAST "tsr", BAD_CAST "application/timestamp-reply",
92 BAD_CAST "tst", BAD_CAST "application/timestamp-reply",
93 BAD_CAST "txt", BAD_CAST "text/plain",
94 BAD_CAST "wav", BAD_CAST "audio/wav",
95 BAD_CAST "xls", BAD_CAST "application/vnd.ms-excel",
96 BAD_CAST "xlsx", BAD_CAST "application/vnd.openxmlformats-officedocument."
97 "spreadsheetml.sheet",
98 BAD_CAST "xml", BAD_CAST "application/xml",
99 BAD_CAST "xsd", BAD_CAST "application/xml",
100 BAD_CAST "zfo", BAD_CAST "application/vnd.software602.filler.form-xml-zip"
103 /* Structure type to hold conversion table from status code to isds_error and
104 * long message */
105 struct code_map_isds_error {
106 const xmlChar **codes; /* NULL terminated array of status codes */
107 const char **meanings; /* Mapping to non-localized long messages */
108 const isds_error *errors; /* Mapping to isds_error code */
111 /* Deallocate structure isds_pki_credentials and NULL it.
112 * Pass-phrase is discarded.
113 * @pki credentials to to free */
114 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
115 if(!pki || !*pki) return;
117 free((*pki)->engine);
118 free((*pki)->certificate);
119 free((*pki)->key);
121 if ((*pki)->passphrase) {
122 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
123 free((*pki)->passphrase);
126 zfree((*pki));
130 /* Free isds_list with all member data.
131 * @list list to free, on return will be NULL */
132 void isds_list_free(struct isds_list **list) {
133 struct isds_list *item, *next_item;
135 if (!list || !*list) return;
137 for(item = *list; item; item = next_item) {
138 if (item->destructor) (item->destructor)(&(item->data));
139 next_item = item->next;
140 free(item);
143 *list = NULL;
147 /* Deallocate structure isds_hash and NULL it.
148 * @hash hash to to free */
149 void isds_hash_free(struct isds_hash **hash) {
150 if(!hash || !*hash) return;
151 free((*hash)->value);
152 zfree((*hash));
156 /* Deallocate structure isds_PersonName recursively and NULL it */
157 void isds_PersonName_free(struct isds_PersonName **person_name) {
158 if (!person_name || !*person_name) return;
160 free((*person_name)->pnFirstName);
161 free((*person_name)->pnMiddleName);
162 free((*person_name)->pnLastName);
163 free((*person_name)->pnLastNameAtBirth);
165 free(*person_name);
166 *person_name = NULL;
170 /* Deallocate structure isds_BirthInfo recursively and NULL it */
171 void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
172 if (!birth_info || !*birth_info) return;
174 free((*birth_info)->biDate);
175 free((*birth_info)->biCity);
176 free((*birth_info)->biCounty);
177 free((*birth_info)->biState);
179 free(*birth_info);
180 *birth_info = NULL;
184 /* Deallocate structure isds_Address recursively and NULL it */
185 void isds_Address_free(struct isds_Address **address) {
186 if (!address || !*address) return;
188 free((*address)->adCity);
189 free((*address)->adStreet);
190 free((*address)->adNumberInStreet);
191 free((*address)->adNumberInMunicipality);
192 free((*address)->adZipCode);
193 free((*address)->adState);
195 free(*address);
196 *address = NULL;
200 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
201 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
202 if (!db_owner_info || !*db_owner_info) return;
204 free((*db_owner_info)->dbID);
205 free((*db_owner_info)->dbType);
206 free((*db_owner_info)->ic);
207 isds_PersonName_free(&((*db_owner_info)->personName));
208 free((*db_owner_info)->firmName);
209 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
210 isds_Address_free(&((*db_owner_info)->address));
211 free((*db_owner_info)->nationality);
212 free((*db_owner_info)->email);
213 free((*db_owner_info)->telNumber);
214 free((*db_owner_info)->identifier);
215 free((*db_owner_info)->registryCode);
216 free((*db_owner_info)->dbState);
217 free((*db_owner_info)->dbEffectiveOVM);
218 free((*db_owner_info)->dbOpenAddressing);
220 free(*db_owner_info);
221 *db_owner_info = NULL;
224 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
225 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
226 if (!db_user_info || !*db_user_info) return;
228 free((*db_user_info)->userID);
229 free((*db_user_info)->userType);
230 free((*db_user_info)->userPrivils);
231 isds_PersonName_free(&((*db_user_info)->personName));
232 isds_Address_free(&((*db_user_info)->address));
233 free((*db_user_info)->biDate);
234 free((*db_user_info)->ic);
235 free((*db_user_info)->firmName);
236 free((*db_user_info)->caStreet);
237 free((*db_user_info)->caCity);
238 free((*db_user_info)->caZipCode);
239 free((*db_user_info)->caState);
241 zfree(*db_user_info);
245 /* Deallocate struct isds_event recursively and NULL it */
246 void isds_event_free(struct isds_event **event) {
247 if (!event || !*event) return;
249 free((*event)->time);
250 free((*event)->type);
251 free((*event)->description);
252 zfree(*event);
256 /* Deallocate struct isds_envelope recursively and NULL it */
257 void isds_envelope_free(struct isds_envelope **envelope) {
258 if (!envelope || !*envelope) return;
260 free((*envelope)->dmID);
261 free((*envelope)->dbIDSender);
262 free((*envelope)->dmSender);
263 free((*envelope)->dmSenderAddress);
264 free((*envelope)->dmSenderType);
265 free((*envelope)->dmRecipient);
266 free((*envelope)->dmRecipientAddress);
267 free((*envelope)->dmAmbiguousRecipient);
268 free((*envelope)->dmType);
270 free((*envelope)->dmOrdinal);
271 free((*envelope)->dmMessageStatus);
272 free((*envelope)->dmDeliveryTime);
273 free((*envelope)->dmAcceptanceTime);
274 isds_hash_free(&(*envelope)->hash);
275 free((*envelope)->timestamp);
276 isds_list_free(&(*envelope)->events);
278 free((*envelope)->dmSenderOrgUnit);
279 free((*envelope)->dmSenderOrgUnitNum);
280 free((*envelope)->dbIDRecipient);
281 free((*envelope)->dmRecipientOrgUnit);
282 free((*envelope)->dmRecipientOrgUnitNum);
283 free((*envelope)->dmToHands);
284 free((*envelope)->dmAnnotation);
285 free((*envelope)->dmRecipientRefNumber);
286 free((*envelope)->dmSenderRefNumber);
287 free((*envelope)->dmRecipientIdent);
288 free((*envelope)->dmSenderIdent);
290 free((*envelope)->dmLegalTitleLaw);
291 free((*envelope)->dmLegalTitleYear);
292 free((*envelope)->dmLegalTitleSect);
293 free((*envelope)->dmLegalTitlePar);
294 free((*envelope)->dmLegalTitlePoint);
296 free((*envelope)->dmPersonalDelivery);
297 free((*envelope)->dmAllowSubstDelivery);
299 free((*envelope)->dmOVM);
300 free((*envelope)->dmPublishOwnID);
302 free(*envelope);
303 *envelope = NULL;
307 /* Deallocate struct isds_message recursively and NULL it */
308 void isds_message_free(struct isds_message **message) {
309 if (!message || !*message) return;
311 free((*message)->raw);
312 isds_envelope_free(&((*message)->envelope));
313 isds_list_free(&((*message)->documents));
314 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
316 free(*message);
317 *message = NULL;
321 /* Deallocate struct isds_document recursively and NULL it */
322 void isds_document_free(struct isds_document **document) {
323 if (!document || !*document) return;
325 if (!(*document)->is_xml) {
326 free((*document)->data);
328 free((*document)->dmMimeType);
329 free((*document)->dmFileGuid);
330 free((*document)->dmUpFileGuid);
331 free((*document)->dmFileDescr);
332 free((*document)->dmFormat);
334 free(*document);
335 *document = NULL;
339 /* Deallocate struct isds_message_copy recursively and NULL it */
340 void isds_message_copy_free(struct isds_message_copy **copy) {
341 if (!copy || !*copy) return;
343 free((*copy)->dbIDRecipient);
344 free((*copy)->dmRecipientOrgUnit);
345 free((*copy)->dmRecipientOrgUnitNum);
346 free((*copy)->dmToHands);
348 free((*copy)->dmStatus);
349 free((*copy)->dmID);
351 zfree(*copy);
355 /* Deallocate struct isds_message_status_change recursively and NULL it */
356 void isds_message_status_change_free(
357 struct isds_message_status_change **message_status_change) {
358 if (!message_status_change || !*message_status_change) return;
360 free((*message_status_change)->dmID);
361 free((*message_status_change)->time);
362 free((*message_status_change)->dmMessageStatus);
364 zfree(*message_status_change);
368 /* Deallocate struct isds_approval recursively and NULL it */
369 void isds_approval_free(struct isds_approval **approval) {
370 if (!approval || !*approval) return;
372 free((*approval)->refference);
374 zfree(*approval);
378 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
379 * The email string is deallocated too. */
380 void isds_credentials_delivery_free(
381 struct isds_credentials_delivery **credentials_delivery) {
382 if (!credentials_delivery || !*credentials_delivery) return;
384 free((*credentials_delivery)->email);
385 free((*credentials_delivery)->token);
386 free((*credentials_delivery)->new_user_name);
388 zfree(*credentials_delivery);
392 /* Deallocate struct isds_commercial_permission recursively and NULL it */
393 void isds_commercial_permission_free(
394 struct isds_commercial_permission **permission) {
395 if (NULL == permission || NULL == *permission) return;
397 free((*permission)->recipient);
398 free((*permission)->payer);
399 free((*permission)->expiration);
400 free((*permission)->count);
401 free((*permission)->reply_identifier);
403 zfree(*permission);
407 /* Deallocate struct isds_credit_event recursively and NULL it */
408 void isds_credit_event_free(struct isds_credit_event **event) {
409 if (NULL == event || NULL == *event) return;
411 free((*event)->time);
412 switch ((*event)->type) {
413 case ISDS_CREDIT_CHARGED:
414 free((*event)->details.charged.transaction);
415 break;
416 case ISDS_CREDIT_DISCHARGED:
417 free((*event)->details.discharged.transaction);
418 break;
419 case ISDS_CREDIT_MESSAGE_SENT:
420 free((*event)->details.message_sent.recipient);
421 free((*event)->details.message_sent.message_id);
422 break;
423 case ISDS_CREDIT_STORAGE_SET:
424 free((*event)->details.storage_set.new_valid_from);
425 free((*event)->details.storage_set.new_valid_to);
426 free((*event)->details.storage_set.old_capacity);
427 free((*event)->details.storage_set.old_valid_from);
428 free((*event)->details.storage_set.old_valid_to);
429 free((*event)->details.storage_set.initiator);
430 break;
431 case ISDS_CREDIT_EXPIRED:
432 break;
435 zfree(*event);
439 /* *DUP_OR_ERROR macros needs error label */
440 #define STRDUP_OR_ERROR(new, template) { \
441 if (!template) { \
442 (new) = NULL; \
443 } else { \
444 (new) = strdup(template); \
445 if (!new) goto error; \
449 #define FLATDUP_OR_ERROR(new, template) { \
450 if (!template) { \
451 (new) = NULL; \
452 } else { \
453 (new) = malloc(sizeof(*(new))); \
454 if (!new) goto error; \
455 memcpy((new), (template), sizeof(*(template))); \
459 /* Copy structure isds_pki_credentials recursively. */
460 struct isds_pki_credentials *isds_pki_credentials_duplicate(
461 const struct isds_pki_credentials *template) {
462 struct isds_pki_credentials *new = NULL;
464 if(!template) return NULL;
466 new = calloc(1, sizeof(*new));
467 if (!new) return NULL;
469 STRDUP_OR_ERROR(new->engine, template->engine);
470 new->certificate_format = template->certificate_format;
471 STRDUP_OR_ERROR(new->certificate, template->certificate);
472 new->key_format = template->key_format;
473 STRDUP_OR_ERROR(new->key, template->key);
474 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
476 return new;
478 error:
479 isds_pki_credentials_free(&new);
480 return NULL;
484 /* Copy structure isds_PersonName recursively */
485 struct isds_PersonName *isds_PersonName_duplicate(
486 const struct isds_PersonName *src) {
487 struct isds_PersonName *new = NULL;
489 if (!src) return NULL;
491 new = calloc(1, sizeof(*new));
492 if (!new) return NULL;
494 STRDUP_OR_ERROR(new->pnFirstName, src->pnFirstName);
495 STRDUP_OR_ERROR(new->pnMiddleName, src->pnMiddleName);
496 STRDUP_OR_ERROR(new->pnLastName, src->pnLastName);
497 STRDUP_OR_ERROR(new->pnLastNameAtBirth, src->pnLastNameAtBirth);
499 return new;
501 error:
502 isds_PersonName_free(&new);
503 return NULL;
507 /* Copy structure isds_BirthInfo recursively */
508 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
509 const struct isds_BirthInfo *template) {
510 struct isds_BirthInfo *new = NULL;
512 if (!template) return NULL;
514 new = calloc(1, sizeof(*new));
515 if (!new) return NULL;
517 FLATDUP_OR_ERROR(new->biDate, template->biDate);
518 STRDUP_OR_ERROR(new->biCity, template->biCity);
519 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
520 STRDUP_OR_ERROR(new->biState, template->biState);
522 return new;
524 error:
525 isds_BirthInfo_free(&new);
526 return NULL;
530 /* Copy structure isds_Address recursively */
531 struct isds_Address *isds_Address_duplicate(
532 const struct isds_Address *src) {
533 struct isds_Address *new = NULL;
535 if (!src) return NULL;
537 new = calloc(1, sizeof(*new));
538 if (!new) return NULL;
540 STRDUP_OR_ERROR(new->adCity, src->adCity);
541 STRDUP_OR_ERROR(new->adStreet, src->adStreet);
542 STRDUP_OR_ERROR(new->adNumberInStreet, src->adNumberInStreet);
543 STRDUP_OR_ERROR(new->adNumberInMunicipality,
544 src->adNumberInMunicipality);
545 STRDUP_OR_ERROR(new->adZipCode, src->adZipCode);
546 STRDUP_OR_ERROR(new->adState, src->adState);
548 return new;
550 error:
551 isds_Address_free(&new);
552 return NULL;
556 /* Copy structure isds_DbOwnerInfo recursively */
557 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
558 const struct isds_DbOwnerInfo *src) {
559 struct isds_DbOwnerInfo *new = NULL;
560 if (!src) return NULL;
562 new = calloc(1, sizeof(*new));
563 if (!new) return NULL;
565 STRDUP_OR_ERROR(new->dbID, src->dbID);
566 FLATDUP_OR_ERROR(new->dbType, src->dbType);
567 STRDUP_OR_ERROR(new->ic, src->ic);
569 if (src->personName) {
570 if (!(new->personName =
571 isds_PersonName_duplicate(src->personName)))
572 goto error;
575 STRDUP_OR_ERROR(new->firmName, src->firmName);
577 if (src->birthInfo) {
578 if (!(new->birthInfo =
579 isds_BirthInfo_duplicate(src->birthInfo)))
580 goto error;
583 if (src->address) {
584 if (!(new->address = isds_Address_duplicate(src->address)))
585 goto error;
588 STRDUP_OR_ERROR(new->nationality, src->nationality);
589 STRDUP_OR_ERROR(new->email, src->email);
590 STRDUP_OR_ERROR(new->telNumber, src->telNumber);
591 STRDUP_OR_ERROR(new->identifier, src->identifier);
592 STRDUP_OR_ERROR(new->registryCode, src->registryCode);
593 FLATDUP_OR_ERROR(new->dbState, src->dbState);
594 FLATDUP_OR_ERROR(new->dbEffectiveOVM, src->dbEffectiveOVM);
595 FLATDUP_OR_ERROR(new->dbOpenAddressing, src->dbOpenAddressing);
597 return new;
599 error:
600 isds_DbOwnerInfo_free(&new);
601 return NULL;
605 /* Copy structure isds_DbUserInfo recursively */
606 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
607 const struct isds_DbUserInfo *src) {
608 struct isds_DbUserInfo *new = NULL;
609 if (!src) return NULL;
611 new = calloc(1, sizeof(*new));
612 if (!new) return NULL;
614 STRDUP_OR_ERROR(new->userID, src->userID);
615 FLATDUP_OR_ERROR(new->userType, src->userType);
616 FLATDUP_OR_ERROR(new->userPrivils, src->userPrivils);
618 if (src->personName) {
619 if (!(new->personName =
620 isds_PersonName_duplicate(src->personName)))
621 goto error;
624 if (src->address) {
625 if (!(new->address = isds_Address_duplicate(src->address)))
626 goto error;
629 FLATDUP_OR_ERROR(new->biDate, src->biDate);
630 STRDUP_OR_ERROR(new->ic, src->ic);
631 STRDUP_OR_ERROR(new->firmName, src->firmName);
632 STRDUP_OR_ERROR(new->caStreet, src->caStreet);
633 STRDUP_OR_ERROR(new->caCity, src->caCity);
634 STRDUP_OR_ERROR(new->caZipCode, src->caZipCode);
635 STRDUP_OR_ERROR(new->caState, src->caState);
637 return new;
639 error:
640 isds_DbUserInfo_free(&new);
641 return NULL;
644 #undef FLATDUP_OR_ERROR
645 #undef STRDUP_OR_ERROR
648 /* Logs libxml2 errors. Should be registered to libxml2 library.
649 * @ctx is unused currently
650 * @msg is printf-like formated message from libxml2 (UTF-8?)
651 * @... are variadic arguments for @msg */
652 static void log_xml(void *ctx, const char *msg, ...) {
653 va_list ap;
654 char *text = NULL;
656 if (!msg) return;
658 va_start(ap, msg);
659 isds_vasprintf(&text, msg, ap);
660 va_end(ap);
662 if (text)
663 isds_log(ILF_XML, ILL_ERR, "%s", text);
664 free(text);
668 /* Initialize ISDS library.
669 * Global function, must be called before other functions.
670 * If it fails you can not use ISDS library and must call isds_cleanup() to
671 * free partially initialized global variables. */
672 isds_error isds_init(void) {
673 /* NULL global variables */
674 log_facilities = ILF_ALL;
675 log_level = ILL_WARNING;
676 log_callback = NULL;
677 log_callback_data = NULL;
679 #if ENABLE_NLS
680 /* Initialize gettext */
681 bindtextdomain(PACKAGE, LOCALEDIR);
682 #endif
684 #if HAVE_LIBCURL
685 /* Initialize CURL */
686 if (curl_global_init(CURL_GLOBAL_ALL)) {
687 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
688 return IE_ERROR;
690 #endif /* HAVE_LIBCURL */
692 /* Initialise cryptographic back-ends. */
693 if (IE_SUCCESS != _isds_init_crypto()) {
694 isds_log(ILF_ISDS, ILL_CRIT,
695 _("initialisation of cryptographic back-end failed\n"));
696 return IE_ERROR;
699 /* This can _exit() current program. Find not so assertive check. */
700 LIBXML_TEST_VERSION;
701 xmlSetGenericErrorFunc(NULL, log_xml);
703 /* Check expat */
704 if (_isds_init_expat(&version_expat)) {
705 isds_log(ILF_ISDS, ILL_CRIT,
706 _("expat library initialization failed\n"));
707 return IE_ERROR;
710 /* Allocate global variables */
713 return IE_SUCCESS;
717 /* Deinitialize ISDS library.
718 * Global function, must be called as last library function. */
719 isds_error isds_cleanup(void) {
720 /* XML */
721 xmlCleanupParser();
723 #if HAVE_LIBCURL
724 /* Curl */
725 curl_global_cleanup();
726 #endif
728 return IE_SUCCESS;
732 /* Return version string of this library. Version of dependencies can be
733 * embedded. Do no try to parse it. You must free it. */
734 char *isds_version(void) {
735 char *buffer = NULL;
737 isds_asprintf(&buffer,
738 #if HAVE_LIBCURL
739 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
740 #else
741 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
742 #endif
743 PACKAGE_VERSION,
744 #if HAVE_LIBCURL
745 curl_version(),
746 #endif
747 version_gpgme, version_gcrypt,
748 version_expat, xmlParserVersion);
749 return buffer;
753 /* Return text description of ISDS error */
754 const char *isds_strerror(const isds_error error) {
755 switch (error) {
756 case IE_SUCCESS:
757 return(_("Success")); break;
758 case IE_ERROR:
759 return(_("Unspecified error")); break;
760 case IE_NOTSUP:
761 return(_("Not supported")); break;
762 case IE_INVAL:
763 return(_("Invalid value")); break;
764 case IE_INVALID_CONTEXT:
765 return(_("Invalid context")); break;
766 case IE_NOT_LOGGED_IN:
767 return(_("Not logged in")); break;
768 case IE_CONNECTION_CLOSED:
769 return(_("Connection closed")); break;
770 case IE_TIMED_OUT:
771 return(_("Timed out")); break;
772 case IE_NOEXIST:
773 return(_("Not exist")); break;
774 case IE_NOMEM:
775 return(_("Out of memory")); break;
776 case IE_NETWORK:
777 return(_("Network problem")); break;
778 case IE_HTTP:
779 return(_("HTTP problem")); break;
780 case IE_SOAP:
781 return(_("SOAP problem")); break;
782 case IE_XML:
783 return(_("XML problem")); break;
784 case IE_ISDS:
785 return(_("ISDS server problem")); break;
786 case IE_ENUM:
787 return(_("Invalid enum value")); break;
788 case IE_DATE:
789 return(_("Invalid date value")); break;
790 case IE_2BIG:
791 return(_("Too big")); break;
792 case IE_2SMALL:
793 return(_("Too small")); break;
794 case IE_NOTUNIQ:
795 return(_("Value not unique")); break;
796 case IE_NOTEQUAL:
797 return(_("Values not equal")); break;
798 case IE_PARTIAL_SUCCESS:
799 return(_("Some suboperations failed")); break;
800 case IE_ABORTED:
801 return(_("Operation aborted")); break;
802 case IE_SECURITY:
803 return(_("Security problem")); break;
804 default:
805 return(_("Unknown error"));
810 /* Create ISDS context.
811 * Each context can be used for different sessions to (possibly) different
812 * ISDS server with different credentials. */
813 struct isds_ctx *isds_ctx_create(void) {
814 struct isds_ctx *context;
815 context = malloc(sizeof(*context));
816 if (context) memset(context, 0, sizeof(*context));
817 return context;
820 #if HAVE_LIBCURL
821 /* Close possibly opened connection to Czech POINT document deposit without
822 * resetting long_message buffer.
823 * XXX: Do not use czp_close_connection() if you do not want to destroy log
824 * message.
825 * @context is Czech POINT session context. */
826 static isds_error czp_do_close_connection(struct isds_ctx *context) {
827 if (!context) return IE_INVALID_CONTEXT;
828 _isds_close_connection(context);
829 return IE_SUCCESS;
833 /* Discard credentials.
834 * @context is ISDS context
835 * @discard_saved_username is true for removing saved username, false for
836 * keeping it.
837 * Only that. It does not cause log out, connection close or similar. */
838 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context,
839 _Bool discard_saved_username) {
840 if(!context) return IE_INVALID_CONTEXT;
842 if (context->username) {
843 memset(context->username, 0, strlen(context->username));
844 zfree(context->username);
846 if (context->password) {
847 memset(context->password, 0, strlen(context->password));
848 zfree(context->password);
850 isds_pki_credentials_free(&context->pki_credentials);
851 if (discard_saved_username && context->saved_username) {
852 memset(context->saved_username, 0, strlen(context->saved_username));
853 zfree(context->saved_username);
856 return IE_SUCCESS;
858 #endif /* HAVE_LIBCURL */
861 /* Destroy ISDS context and free memory.
862 * @context will be NULLed on success. */
863 isds_error isds_ctx_free(struct isds_ctx **context) {
864 if (!context || !*context) {
865 return IE_INVALID_CONTEXT;
868 #if HAVE_LIBCURL
869 /* Discard credentials and close connection */
870 switch ((*context)->type) {
871 case CTX_TYPE_NONE: break;
872 case CTX_TYPE_ISDS: isds_logout(*context); break;
873 case CTX_TYPE_CZP:
874 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
875 czp_do_close_connection(*context); break;
878 /* For sure */
879 _isds_discard_credentials(*context, 1);
881 /* Free other structures */
882 free((*context)->tls_verify_server);
883 free((*context)->tls_ca_file);
884 free((*context)->tls_ca_dir);
885 free((*context)->tls_crl_file);
886 #endif /* HAVE_LIBCURL */
887 free((*context)->long_message);
889 free(*context);
890 *context = NULL;
891 return IE_SUCCESS;
895 /* Return long message text produced by library function, e.g. detailed error
896 * message. Returned pointer is only valid until new library function is
897 * called for the same context. Could be NULL, especially if NULL context is
898 * supplied. Return string is locale encoded. */
899 char *isds_long_message(const struct isds_ctx *context) {
900 if (!context) return NULL;
901 return context->long_message;
905 /* Stores message into context' long_message buffer.
906 * Application can pick the message up using isds_long_message().
907 * NULL @message truncates the buffer but does not deallocate it.
908 * @message is coded in locale encoding */
909 _hidden isds_error isds_log_message(struct isds_ctx *context,
910 const char *message) {
911 char *buffer;
912 size_t length;
914 if (!context) return IE_INVALID_CONTEXT;
916 /* FIXME: Check for integer overflow */
917 length = 1 + ((message) ? strlen(message) : 0);
918 buffer = realloc(context->long_message, length);
919 if (!buffer) return IE_NOMEM;
921 if (message)
922 strcpy(buffer, message);
923 else
924 *buffer = '\0';
926 context->long_message = buffer;
927 return IE_SUCCESS;
931 /* Appends message into context' long_message buffer.
932 * Application can pick the message up using isds_long_message().
933 * NULL message has void effect. */
934 _hidden isds_error isds_append_message(struct isds_ctx *context,
935 const char *message) {
936 char *buffer;
937 size_t old_length, length;
939 if (!context) return IE_INVALID_CONTEXT;
940 if (!message) return IE_SUCCESS;
941 if (!context->long_message)
942 return isds_log_message(context, message);
944 old_length = strlen(context->long_message);
945 /* FIXME: Check for integer overflow */
946 length = 1 + old_length + strlen(message);
947 buffer = realloc(context->long_message, length);
948 if (!buffer) return IE_NOMEM;
950 strcpy(buffer + old_length, message);
952 context->long_message = buffer;
953 return IE_SUCCESS;
957 /* Stores formatted message into context' long_message buffer.
958 * Application can pick the message up using isds_long_message(). */
959 _hidden isds_error isds_printf_message(struct isds_ctx *context,
960 const char *format, ...) {
961 va_list ap;
962 int length;
964 if (!context) return IE_INVALID_CONTEXT;
965 va_start(ap, format);
966 length = isds_vasprintf(&(context->long_message), format, ap);
967 va_end(ap);
969 return (length < 0) ? IE_ERROR: IE_SUCCESS;
973 /* Set logging up.
974 * @facilities is bit mask of isds_log_facility values,
975 * @level is verbosity level. */
976 void isds_set_logging(const unsigned int facilities,
977 const isds_log_level level) {
978 log_facilities = facilities;
979 log_level = level;
983 /* Register callback function libisds calls when new global log message is
984 * produced by library. Library logs to stderr by default.
985 * @callback is function provided by application libisds will call. See type
986 * definition for @callback argument explanation. Pass NULL to revert logging to
987 * default behaviour.
988 * @data is application specific data @callback gets as last argument */
989 void isds_set_log_callback(isds_log_callback callback, void *data) {
990 log_callback = callback;
991 log_callback_data = data;
995 /* Log @message in class @facility with log @level into global log. @message
996 * is printf(3) formatting string, variadic arguments may be necessary.
997 * For debugging purposes. */
998 _hidden isds_error isds_log(const isds_log_facility facility,
999 const isds_log_level level, const char *message, ...) {
1000 va_list ap;
1001 char *buffer = NULL;
1002 int length;
1004 if (level > log_level) return IE_SUCCESS;
1005 if (!(log_facilities & facility)) return IE_SUCCESS;
1006 if (!message) return IE_INVAL;
1008 if (log_callback) {
1009 /* Pass message to application supplied callback function */
1010 va_start(ap, message);
1011 length = isds_vasprintf(&buffer, message, ap);
1012 va_end(ap);
1014 if (length == -1) {
1015 return IE_ERROR;
1017 if (length > 0) {
1018 log_callback(facility, level, buffer, length, log_callback_data);
1020 free(buffer);
1021 } else {
1022 /* Default: Log it to stderr */
1023 va_start(ap, message);
1024 vfprintf(stderr, message, ap);
1025 va_end(ap);
1026 /* Line buffered printf is default.
1027 * fflush(stderr);*/
1030 return IE_SUCCESS;
1034 /* Set timeout in milliseconds for each network job like connecting to server
1035 * or sending message. Use 0 to disable timeout limits. */
1036 isds_error isds_set_timeout(struct isds_ctx *context,
1037 const unsigned int timeout) {
1038 if (!context) return IE_INVALID_CONTEXT;
1039 zfree(context->long_message);
1041 #if HAVE_LIBCURL
1042 context->timeout = timeout;
1044 if (context->curl) {
1045 CURLcode curl_err;
1047 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
1048 if (!curl_err)
1049 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1050 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
1051 context->timeout);
1052 #else
1053 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
1054 context->timeout / 1000);
1055 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1056 if (curl_err) return IE_ERROR;
1059 return IE_SUCCESS;
1060 #else /* not HAVE_LIBCURL */
1061 return IE_NOTSUP;
1062 #endif
1066 /* Register callback function libisds calls periodically during HTTP data
1067 * transfer.
1068 * @context is session context
1069 * @callback is function provided by application libisds will call. See type
1070 * definition for @callback argument explanation.
1071 * @data is application specific data @callback gets as last argument */
1072 isds_error isds_set_progress_callback(struct isds_ctx *context,
1073 isds_progress_callback callback, void *data) {
1074 if (!context) return IE_INVALID_CONTEXT;
1075 zfree(context->long_message);
1077 #if HAVE_LIBCURL
1078 context->progress_callback = callback;
1079 context->progress_callback_data = data;
1081 return IE_SUCCESS;
1082 #else /* not HAVE_LIBCURL */
1083 return IE_NOTSUP;
1084 #endif
1088 /* Change context settings.
1089 * @context is context which setting will be applied to
1090 * @option is name of option. It determines the type of last argument. See
1091 * isds_option definition for more info.
1092 * @... is value of new setting. Type is determined by @option
1093 * */
1094 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1095 ...) {
1096 isds_error err = IE_SUCCESS;
1097 va_list ap;
1098 #if HAVE_LIBCURL
1099 char *pointer, *string;
1100 #endif
1102 if (!context) return IE_INVALID_CONTEXT;
1103 zfree(context->long_message);
1105 va_start(ap, option);
1107 #define REPLACE_VA_BOOLEAN(destination) { \
1108 if (!(destination)) { \
1109 (destination) = malloc(sizeof(*(destination))); \
1110 if (!(destination)) { \
1111 err = IE_NOMEM; goto leave; \
1114 *(destination) = (_Bool) !!va_arg(ap, int); \
1117 #define REPLACE_VA_STRING(destination) { \
1118 string = va_arg(ap, char *); \
1119 if (string) { \
1120 pointer = realloc((destination), 1 + strlen(string)); \
1121 if (!pointer) { err = IE_NOMEM; goto leave; } \
1122 strcpy(pointer, string); \
1123 (destination) = pointer; \
1124 } else { \
1125 free(destination); \
1126 (destination) = NULL; \
1130 switch (option) {
1131 case IOPT_TLS_VERIFY_SERVER:
1132 #if HAVE_LIBCURL
1133 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1134 #else
1135 err = IE_NOTSUP; goto leave;
1136 #endif
1137 break;
1138 case IOPT_TLS_CA_FILE:
1139 #if HAVE_LIBCURL
1140 REPLACE_VA_STRING(context->tls_ca_file);
1141 #else
1142 err = IE_NOTSUP; goto leave;
1143 #endif
1144 break;
1145 case IOPT_TLS_CA_DIRECTORY:
1146 #if HAVE_LIBCURL
1147 REPLACE_VA_STRING(context->tls_ca_dir);
1148 #else
1149 err = IE_NOTSUP; goto leave;
1150 #endif
1151 break;
1152 case IOPT_TLS_CRL_FILE:
1153 #if HAVE_LIBCURL
1154 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1155 REPLACE_VA_STRING(context->tls_crl_file);
1156 #else
1157 isds_log_message(context,
1158 _("Curl library does not support CRL definition"));
1159 err = IE_NOTSUP;
1160 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1161 #else
1162 err = IE_NOTSUP; goto leave;
1163 #endif /* not HAVE_LIBCURL */
1164 break;
1165 case IOPT_NORMALIZE_MIME_TYPE:
1166 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1167 break;
1169 default:
1170 err = IE_ENUM; goto leave;
1173 #undef REPLACE_VA_STRING
1174 #undef REPLACE_VA_BOOLEAN
1176 leave:
1177 va_end(ap);
1178 return err;
1182 #if HAVE_LIBCURL
1183 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1184 * Destination for NULL argument will not be touched.
1185 * Destination pointers must be freed before calling this function.
1186 * If @username is @context->saved_username, the saved_username will not be
1187 * replaced. The saved_username is clobbered only if context has set otp
1188 * member.
1189 * Return IE_SUCCESS on success. */
1190 static isds_error _isds_store_credentials(struct isds_ctx *context,
1191 const char *username, const char *password,
1192 const struct isds_pki_credentials *pki_credentials) {
1193 if (NULL == context) return IE_INVALID_CONTEXT;
1195 /* FIXME: mlock password
1196 * (I have a library) */
1198 if (username) {
1199 context->username = strdup(username);
1200 if (context->otp && context->saved_username != username)
1201 context->saved_username = strdup(username);
1203 if (password) {
1204 if (NULL == context->otp_credentials)
1205 context->password = strdup(password);
1206 else
1207 context->password = _isds_astrcat(password,
1208 context->otp_credentials->otp_code);
1210 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1212 if ((NULL != username && NULL == context->username) ||
1213 (NULL != password && NULL == context->password) ||
1214 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1215 (context->otp && NULL != context->username &&
1216 NULL == context->saved_username)) {
1217 return IE_NOMEM;
1220 return IE_SUCCESS;
1222 #endif
1225 /* Connect and log into ISDS server.
1226 * All required arguments will be copied, you do not have to keep them after
1227 * that.
1228 * ISDS supports six different authentication methods. Exact method is
1229 * selected on @username, @password, @pki_credentials, and @otp arguments:
1230 * - If @pki_credentials == NULL, @username and @password must be supplied
1231 * and then
1232 * - If @otp == NULL, simple authentication by username and password will
1233 * be proceeded.
1234 * - If @otp != NULL, authentication by username and password and OTP
1235 * will be used.
1236 * - If @pki_credentials != NULL, then
1237 * - If @username == NULL, only certificate will be used
1238 * - If @username != NULL, then
1239 * - If @password == NULL, then certificate will be used and
1240 * @username shifts meaning to box ID. This is used for hosted
1241 * services.
1242 * - Otherwise all three arguments will be used.
1243 * Please note, that different cases require different certificate type
1244 * (system qualified one or commercial non qualified one). This library
1245 * does not check such political issues. Please see ISDS Specification
1246 * for more details.
1247 * @url is base address of ISDS web service. Pass extern isds_locator
1248 * variable to use production ISDS instance without client certificate
1249 * authentication (or extern isds_cert_locator with client certificate
1250 * authentication or extern isds_otp_locators with OTP authentication).
1251 * Passing NULL has the same effect, autoselection between isds_locator,
1252 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1253 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1254 * isds_otp_testing_locator) variable to select testing instance.
1255 * @username is user name of ISDS user or box ID
1256 * @password is user's secret password
1257 * @pki_credentials defines public key cryptographic material to use in client
1258 * authentication.
1259 * @otp selects one-time password authentication method to use, defines OTP
1260 * code (if known) and returns fine grade resolution of OTP procedure.
1261 * @return:
1262 * IE_SUCCESS if authentication succeeds
1263 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1264 * requested, fine grade reason will be set into @otp->resolution. Error
1265 * message from server can be obtained by isds_long_message() call.
1266 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1267 * server has sent OTP code through side channel. Application is expected to
1268 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1269 * this call to complete second phase of TOTP authentication;
1270 * or other appropriate error. */
1271 isds_error isds_login(struct isds_ctx *context, const char *url,
1272 const char *username, const char *password,
1273 const struct isds_pki_credentials *pki_credentials,
1274 struct isds_otp *otp) {
1275 #if HAVE_LIBCURL
1276 isds_error err = IE_NOT_LOGGED_IN;
1277 isds_error soap_err;
1278 xmlNsPtr isds_ns = NULL;
1279 xmlNodePtr request = NULL;
1280 xmlNodePtr response = NULL;
1281 #endif /* HAVE_LIBCURL */
1283 if (!context) return IE_INVALID_CONTEXT;
1284 zfree(context->long_message);
1286 #if HAVE_LIBCURL
1287 /* Close connection if already logged in */
1288 if (context->curl) {
1289 _isds_close_connection(context);
1292 /* Store configuration */
1293 context->type = CTX_TYPE_ISDS;
1294 zfree(context->url);
1296 /* Mangle base URI according to requested authentication method */
1297 if (NULL == pki_credentials) {
1298 isds_log(ILF_SEC, ILL_INFO,
1299 _("Selected authentication method: no certificate, "
1300 "username and password\n"));
1301 if (!username || !password) {
1302 isds_log_message(context,
1303 _("Both username and password must be supplied"));
1304 return IE_INVAL;
1306 context->otp_credentials = otp;
1307 context->otp = (NULL != context->otp_credentials);
1309 if (!context->otp) {
1310 /* Default locator is official system (without certificate or
1311 * OTP) */
1312 context->url = strdup((NULL != url) ? url : isds_locator);
1313 } else {
1314 const char *authenticator_uri = NULL;
1315 if (!url) url = isds_otp_locator;
1316 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1317 switch (context->otp_credentials->method) {
1318 case OTP_HMAC:
1319 isds_log(ILF_SEC, ILL_INFO,
1320 _("Selected authentication method: "
1321 "HMAC-based one-time password\n"));
1322 authenticator_uri =
1323 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1324 break;
1325 case OTP_TIME:
1326 isds_log(ILF_SEC, ILL_INFO,
1327 _("Selected authentication method: "
1328 "Time-based one-time password\n"));
1329 if (context->otp_credentials->otp_code == NULL) {
1330 isds_log(ILF_SEC, ILL_INFO,
1331 _("OTP code has not been provided by "
1332 "application, requesting server for "
1333 "new one.\n"));
1334 authenticator_uri =
1335 "%1$sas/processLogin?type=totp&sendSms=true&"
1336 "uri=%1$sapps/";
1337 } else {
1338 isds_log(ILF_SEC, ILL_INFO,
1339 _("OTP code has been provided by "
1340 "application, not requesting server "
1341 "for new one.\n"));
1342 authenticator_uri =
1343 "%1$sas/processLogin?type=totp&"
1344 "uri=%1$sapps/";
1346 break;
1347 default:
1348 isds_log_message(context,
1349 _("Unknown one-time password authentication "
1350 "method requested by application"));
1351 return IE_ENUM;
1353 if (-1 == isds_asprintf(&context->url, authenticator_uri, url))
1354 return IE_NOMEM;
1356 } else {
1357 /* Default locator is official system (with client certificate) */
1358 context->otp = 0;
1359 context->otp_credentials = NULL;
1360 if (!url) url = isds_cert_locator;
1362 if (!username) {
1363 isds_log(ILF_SEC, ILL_INFO,
1364 _("Selected authentication method: system certificate, "
1365 "no username and no password\n"));
1366 password = NULL;
1367 context->url = _isds_astrcat(url, "cert/");
1368 } else {
1369 if (!password) {
1370 isds_log(ILF_SEC, ILL_INFO,
1371 _("Selected authentication method: system certificate, "
1372 "box ID and no password\n"));
1373 context->url = _isds_astrcat(url, "hspis/");
1374 } else {
1375 isds_log(ILF_SEC, ILL_INFO,
1376 _("Selected authentication method: commercial "
1377 "certificate, username and password\n"));
1378 context->url = _isds_astrcat(url, "certds/");
1382 if (!(context->url))
1383 return IE_NOMEM;
1385 /* Prepare CURL handle */
1386 context->curl = curl_easy_init();
1387 if (!(context->curl))
1388 return IE_ERROR;
1390 /* Build log-in request */
1391 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1392 if (!request) {
1393 isds_log_message(context, _("Could not build ISDS log-in request"));
1394 return IE_ERROR;
1396 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1397 if(!isds_ns) {
1398 isds_log_message(context, _("Could not create ISDS name space"));
1399 xmlFreeNode(request);
1400 return IE_ERROR;
1402 xmlSetNs(request, isds_ns);
1404 /* Store credentials */
1405 _isds_discard_credentials(context, 1);
1406 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1407 _isds_discard_credentials(context, 1);
1408 xmlFreeNode(request);
1409 return IE_NOMEM;
1412 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1413 username, url);
1415 /* Send log-in request */
1416 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1418 if (context->otp) {
1419 /* Revert context URL from OTP authentication service URL to OTP web
1420 * service base URL for subsequent calls. Potenial isds_login() retry
1421 * will re-set context URL again. */
1422 zfree(context->url);
1423 context->url = _isds_astrcat(url, "apps/");
1424 if (context->url == NULL) {
1425 soap_err = IE_NOMEM;
1427 /* Detach pointer to OTP credentials from context */
1428 context->otp_credentials = NULL;
1431 /* Remove credentials */
1432 _isds_discard_credentials(context, 0);
1434 /* Destroy log-in request */
1435 xmlFreeNode(request);
1437 if (soap_err) {
1438 xmlFreeNodeList(response);
1439 _isds_close_connection(context);
1440 return soap_err;
1443 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1444 * authentication succeeded if soap_err == IE_SUCCESS */
1445 err = IE_SUCCESS;
1447 xmlFreeNodeList(response);
1449 if (!err)
1450 isds_log(ILF_ISDS, ILL_DEBUG,
1451 _("User %s has been logged into server %s successfully\n"),
1452 username, url);
1453 return err;
1454 #else /* not HAVE_LIBCURL */
1455 return IE_NOTSUP;
1456 #endif
1460 /* Log out from ISDS server discards credentials and connection configuration. */
1461 isds_error isds_logout(struct isds_ctx *context) {
1462 if (!context) return IE_INVALID_CONTEXT;
1463 zfree(context->long_message);
1465 #if HAVE_LIBCURL
1466 if (context->curl) {
1467 if (context->otp) {
1468 isds_error err = _isds_invalidate_otp_cookie(context);
1469 if (err) return err;
1472 /* Close connection */
1473 _isds_close_connection(context);
1475 /* Discard credentials for sure. They should not survive isds_login(),
1476 * even successful .*/
1477 _isds_discard_credentials(context, 1);
1478 zfree(context->url);
1480 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1481 } else {
1482 _isds_discard_credentials(context, 1);
1484 return IE_SUCCESS;
1485 #else /* not HAVE_LIBCURL */
1486 return IE_NOTSUP;
1487 #endif
1491 /* Verify connection to ISDS is alive and server is responding.
1492 * Send dummy request to ISDS and expect dummy response. */
1493 isds_error isds_ping(struct isds_ctx *context) {
1494 #if HAVE_LIBCURL
1495 isds_error soap_err;
1496 xmlNsPtr isds_ns = NULL;
1497 xmlNodePtr request = NULL;
1498 xmlNodePtr response = NULL;
1499 #endif /* HAVE_LIBCURL */
1501 if (!context) return IE_INVALID_CONTEXT;
1502 zfree(context->long_message);
1504 #if HAVE_LIBCURL
1505 /* Check if connection is established */
1506 if (!context->curl) return IE_CONNECTION_CLOSED;
1509 /* Build dummy request */
1510 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1511 if (!request) {
1512 isds_log_message(context, _("Could build ISDS dummy request"));
1513 return IE_ERROR;
1515 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1516 if(!isds_ns) {
1517 isds_log_message(context, _("Could not create ISDS name space"));
1518 xmlFreeNode(request);
1519 return IE_ERROR;
1521 xmlSetNs(request, isds_ns);
1523 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1525 /* Sent dummy request */
1526 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1528 /* Destroy log-in request */
1529 xmlFreeNode(request);
1531 if (soap_err) {
1532 isds_log(ILF_ISDS, ILL_DEBUG,
1533 _("ISDS server could not be contacted\n"));
1534 xmlFreeNodeList(response);
1535 return soap_err;
1538 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1539 * authentication succeeded if soap_err == IE_SUCCESS */
1540 /* TODO: ISDS documentation does not specify response body.
1541 * However real server sends back DummyOperationResponse */
1544 xmlFreeNodeList(response);
1546 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1548 return IE_SUCCESS;
1549 #else /* not HAVE_LIBCURL */
1550 return IE_NOTSUP;
1551 #endif
1555 /* Send bogus request to ISDS.
1556 * Just for test purposes */
1557 isds_error isds_bogus_request(struct isds_ctx *context) {
1558 #if HAVE_LIBCURL
1559 isds_error err;
1560 xmlNsPtr isds_ns = NULL;
1561 xmlNodePtr request = NULL;
1562 xmlDocPtr response = NULL;
1563 xmlChar *code = NULL, *message = NULL;
1564 #endif
1566 if (!context) return IE_INVALID_CONTEXT;
1567 zfree(context->long_message);
1569 #if HAVE_LIBCURL
1570 /* Check if connection is established */
1571 if (!context->curl) {
1572 /* Testing printf message */
1573 isds_printf_message(context, "%s", _("I said connection closed"));
1574 return IE_CONNECTION_CLOSED;
1578 /* Build dummy request */
1579 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1580 if (!request) {
1581 isds_log_message(context, _("Could build ISDS bogus request"));
1582 return IE_ERROR;
1584 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1585 if(!isds_ns) {
1586 isds_log_message(context, _("Could not create ISDS name space"));
1587 xmlFreeNode(request);
1588 return IE_ERROR;
1590 xmlSetNs(request, isds_ns);
1592 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1594 /* Sent bogus request */
1595 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1597 /* Destroy request */
1598 xmlFreeNode(request);
1600 if (err) {
1601 isds_log(ILF_ISDS, ILL_DEBUG,
1602 _("Processing ISDS response on bogus request failed\n"));
1603 xmlFreeDoc(response);
1604 return err;
1607 /* Check for response status */
1608 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1609 &code, &message, NULL);
1610 if (err) {
1611 isds_log(ILF_ISDS, ILL_DEBUG,
1612 _("ISDS response on bogus request is missing status\n"));
1613 free(code);
1614 free(message);
1615 xmlFreeDoc(response);
1616 return err;
1618 if (xmlStrcmp(code, BAD_CAST "0000")) {
1619 char *code_locale = _isds_utf82locale((char*)code);
1620 char *message_locale = _isds_utf82locale((char*)message);
1621 isds_log(ILF_ISDS, ILL_DEBUG,
1622 _("Server refused bogus request (code=%s, message=%s)\n"),
1623 code_locale, message_locale);
1624 /* XXX: Literal error messages from ISDS are Czech messages
1625 * (English sometimes) in UTF-8. It's hard to catch them for
1626 * translation. Successfully gettextized would return in locale
1627 * encoding, unsuccessfully translated would pass in UTF-8. */
1628 isds_log_message(context, message_locale);
1629 free(code_locale);
1630 free(message_locale);
1631 free(code);
1632 free(message);
1633 xmlFreeDoc(response);
1634 return IE_ISDS;
1638 free(code);
1639 free(message);
1640 xmlFreeDoc(response);
1642 isds_log(ILF_ISDS, ILL_DEBUG,
1643 _("Bogus message accepted by server. This should not happen.\n"));
1645 return IE_SUCCESS;
1646 #else /* not HAVE_LIBCURL */
1647 return IE_NOTSUP;
1648 #endif
1652 #if HAVE_LIBCURL
1653 /* Serialize XML subtree to buffer preserving XML indentation.
1654 * @context is session context
1655 * @subtree is XML element to be serialized (with children)
1656 * @buffer is automatically reallocated buffer where serialize to
1657 * @length is size of serialized stream in bytes
1658 * @return standard error code, free @buffer in case of error */
1659 static isds_error serialize_subtree(struct isds_ctx *context,
1660 xmlNodePtr subtree, void **buffer, size_t *length) {
1661 isds_error err = IE_SUCCESS;
1662 xmlBufferPtr xml_buffer = NULL;
1663 xmlSaveCtxtPtr save_ctx = NULL;
1664 xmlDocPtr subtree_doc = NULL;
1665 xmlNodePtr subtree_copy;
1666 xmlNsPtr isds_ns;
1667 void *new_buffer;
1669 if (!context) return IE_INVALID_CONTEXT;
1670 if (!buffer) return IE_INVAL;
1671 zfree(*buffer);
1672 if (!subtree || !length) return IE_INVAL;
1674 /* Make temporary XML document with @subtree root element */
1675 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1676 * It can result in not well-formed on invalid XML tree (e.g. name space
1677 * prefix definition can miss. */
1678 /*FIXME */
1680 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1681 if (!subtree_doc) {
1682 isds_log_message(context, _("Could not build temporary document"));
1683 err = IE_ERROR;
1684 goto leave;
1687 /* XXX: Copy subtree and attach the copy to document.
1688 * One node can not bee attached into more document at the same time.
1689 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1690 * automatically.
1691 * XXX: Check xmlSaveTree() too. */
1692 subtree_copy = xmlCopyNodeList(subtree);
1693 if (!subtree_copy) {
1694 isds_log_message(context, _("Could not copy subtree"));
1695 err = IE_ERROR;
1696 goto leave;
1698 xmlDocSetRootElement(subtree_doc, subtree_copy);
1700 /* Only this way we get namespace definition as @xmlns:isds,
1701 * otherwise we get namespace prefix without definition */
1702 /* FIXME: Don't overwrite original default namespace */
1703 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1704 if(!isds_ns) {
1705 isds_log_message(context, _("Could not create ISDS name space"));
1706 err = IE_ERROR;
1707 goto leave;
1709 xmlSetNs(subtree_copy, isds_ns);
1712 /* Serialize the document into buffer */
1713 xml_buffer = xmlBufferCreate();
1714 if (!xml_buffer) {
1715 isds_log_message(context, _("Could not create xmlBuffer"));
1716 err = IE_ERROR;
1717 goto leave;
1719 /* Last argument 0 means to not format the XML tree */
1720 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1721 if (!save_ctx) {
1722 isds_log_message(context, _("Could not create XML serializer"));
1723 err = IE_ERROR;
1724 goto leave;
1726 /* XXX: According LibXML documentation, this function does not return
1727 * meaningful value yet */
1728 xmlSaveDoc(save_ctx, subtree_doc);
1729 if (-1 == xmlSaveFlush(save_ctx)) {
1730 isds_log_message(context,
1731 _("Could not serialize XML subtree"));
1732 err = IE_ERROR;
1733 goto leave;
1735 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1736 * even after xmlSaveFlush(). Thus close it here */
1737 xmlSaveClose(save_ctx); save_ctx = NULL;
1740 /* Store and detach buffer from xml_buffer */
1741 *buffer = xml_buffer->content;
1742 *length = xml_buffer->use;
1743 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1745 /* Shrink buffer */
1746 new_buffer = realloc(*buffer, *length);
1747 if (new_buffer) *buffer = new_buffer;
1749 leave:
1750 if (err) {
1751 zfree(*buffer);
1752 *length = 0;
1755 xmlSaveClose(save_ctx);
1756 xmlBufferFree(xml_buffer);
1757 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1758 return err;
1760 #endif /* HAVE_LIBCURL */
1763 #if 0
1764 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1765 * @context is session context
1766 * @document is original document where @nodeset points to
1767 * @nodeset is XPath node set to dump (recursively)
1768 * @buffer is automatically reallocated buffer where serialize to
1769 * @length is size of serialized stream in bytes
1770 * @return standard error code, free @buffer in case of error */
1771 static isds_error dump_nodeset(struct isds_ctx *context,
1772 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1773 void **buffer, size_t *length) {
1774 isds_error err = IE_SUCCESS;
1775 xmlBufferPtr xml_buffer = NULL;
1776 void *new_buffer;
1778 if (!context) return IE_INVALID_CONTEXT;
1779 if (!buffer) return IE_INVAL;
1780 zfree(*buffer);
1781 if (!document || !nodeset || !length) return IE_INVAL;
1782 *length = 0;
1784 /* Empty node set results into NULL buffer */
1785 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1786 goto leave;
1789 /* Resulting the document into buffer */
1790 xml_buffer = xmlBufferCreate();
1791 if (!xml_buffer) {
1792 isds_log_message(context, _("Could not create xmlBuffer"));
1793 err = IE_ERROR;
1794 goto leave;
1797 /* Iterate over all nodes */
1798 for (int i = 0; i < nodeset->nodeNr; i++) {
1799 /* Serialize node.
1800 * XXX: xmlNodeDump() appends to xml_buffer. */
1801 if (-1 ==
1802 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1803 isds_log_message(context, _("Could not dump XML node"));
1804 err = IE_ERROR;
1805 goto leave;
1809 /* Store and detach buffer from xml_buffer */
1810 *buffer = xml_buffer->content;
1811 *length = xml_buffer->use;
1812 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1814 /* Shrink buffer */
1815 new_buffer = realloc(*buffer, *length);
1816 if (new_buffer) *buffer = new_buffer;
1819 leave:
1820 if (err) {
1821 zfree(*buffer);
1822 *length = 0;
1825 xmlBufferFree(xml_buffer);
1826 return err;
1828 #endif
1830 #if 0
1831 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1832 * @context is session context
1833 * @document is original document where @nodeset points to
1834 * @nodeset is XPath node set to dump (recursively)
1835 * @buffer is automatically reallocated buffer where serialize to
1836 * @length is size of serialized stream in bytes
1837 * @return standard error code, free @buffer in case of error */
1838 static isds_error dump_nodeset(struct isds_ctx *context,
1839 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1840 void **buffer, size_t *length) {
1841 isds_error err = IE_SUCCESS;
1842 xmlBufferPtr xml_buffer = NULL;
1843 xmlSaveCtxtPtr save_ctx = NULL;
1844 void *new_buffer;
1846 if (!context) return IE_INVALID_CONTEXT;
1847 if (!buffer) return IE_INVAL;
1848 zfree(*buffer);
1849 if (!document || !nodeset || !length) return IE_INVAL;
1850 *length = 0;
1852 /* Empty node set results into NULL buffer */
1853 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1854 goto leave;
1857 /* Resulting the document into buffer */
1858 xml_buffer = xmlBufferCreate();
1859 if (!xml_buffer) {
1860 isds_log_message(context, _("Could not create xmlBuffer"));
1861 err = IE_ERROR;
1862 goto leave;
1864 if (xmlSubstituteEntitiesDefault(1)) {
1865 isds_log_message(context, _("Could not disable attribute escaping"));
1866 err = IE_ERROR;
1867 goto leave;
1869 /* Last argument means:
1870 * 0 to not format the XML tree
1871 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1872 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1873 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1874 if (!save_ctx) {
1875 isds_log_message(context, _("Could not create XML serializer"));
1876 err = IE_ERROR;
1877 goto leave;
1879 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1880 isds_log_message(context, _("Could not disable attribute escaping"));
1881 err = IE_ERROR;
1882 goto leave;
1886 /* Iterate over all nodes */
1887 for (int i = 0; i < nodeset->nodeNr; i++) {
1888 /* Serialize node.
1889 * XXX: xmlNodeDump() appends to xml_buffer. */
1890 /*if (-1 ==
1891 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1893 /* XXX: According LibXML documentation, this function does not return
1894 * meaningful value yet */
1895 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1896 if (-1 == xmlSaveFlush(save_ctx)) {
1897 isds_log_message(context,
1898 _("Could not serialize XML subtree"));
1899 err = IE_ERROR;
1900 goto leave;
1904 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1905 * even after xmlSaveFlush(). Thus close it here */
1906 xmlSaveClose(save_ctx); save_ctx = NULL;
1908 /* Store and detach buffer from xml_buffer */
1909 *buffer = xml_buffer->content;
1910 *length = xml_buffer->use;
1911 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1913 /* Shrink buffer */
1914 new_buffer = realloc(*buffer, *length);
1915 if (new_buffer) *buffer = new_buffer;
1917 leave:
1918 if (err) {
1919 zfree(*buffer);
1920 *length = 0;
1923 xmlSaveClose(save_ctx);
1924 xmlBufferFree(xml_buffer);
1925 return err;
1927 #endif
1930 #if HAVE_LIBCURL
1931 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1932 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1933 if (!string || !type) return IE_INVAL;
1935 if (!xmlStrcmp(string, BAD_CAST "FO"))
1936 *type = DBTYPE_FO;
1937 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1938 *type = DBTYPE_PFO;
1939 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1940 *type = DBTYPE_PFO_ADVOK;
1941 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1942 *type = DBTYPE_PFO_DANPOR;
1943 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1944 *type = DBTYPE_PFO_INSSPR;
1945 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1946 *type = DBTYPE_PO;
1947 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1948 *type = DBTYPE_PO_ZAK;
1949 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1950 *type = DBTYPE_PO_REQ;
1951 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1952 *type = DBTYPE_OVM;
1953 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1954 *type = DBTYPE_OVM_NOTAR;
1955 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1956 *type = DBTYPE_OVM_EXEKUT;
1957 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1958 *type = DBTYPE_OVM_REQ;
1959 else
1960 return IE_ENUM;
1961 return IE_SUCCESS;
1965 /* Convert ISDS dbType enum @type to UTF-8 string.
1966 * @Return pointer to static string, or NULL if unknown enum value */
1967 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1968 switch(type) {
1969 /* DBTYPE_SYSTEM is invalid value from point of view of public
1970 * SOAP interface. */
1971 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1972 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1973 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1974 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1975 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1976 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1977 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1978 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1979 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1980 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1981 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1982 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1983 default: return NULL; break;
1988 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
1989 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1990 if (!string || !type) return IE_INVAL;
1992 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1993 *type = USERTYPE_PRIMARY;
1994 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1995 *type = USERTYPE_ENTRUSTED;
1996 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1997 *type = USERTYPE_ADMINISTRATOR;
1998 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1999 *type = USERTYPE_OFFICIAL;
2000 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2001 *type = USERTYPE_OFFICIAL_CERT;
2002 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2003 *type = USERTYPE_LIQUIDATOR;
2004 else
2005 return IE_ENUM;
2006 return IE_SUCCESS;
2010 /* Convert ISDS userType enum @type to UTF-8 string.
2011 * @Return pointer to static string, or NULL if unknown enum value */
2012 static const xmlChar *isds_UserType2string(const isds_UserType type) {
2013 switch(type) {
2014 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
2015 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
2016 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
2017 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
2018 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
2019 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
2020 default: return NULL; break;
2025 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2026 static isds_error string2isds_sender_type(const xmlChar *string,
2027 isds_sender_type *type) {
2028 if (!string || !type) return IE_INVAL;
2030 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2031 *type = SENDERTYPE_PRIMARY;
2032 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2033 *type = SENDERTYPE_ENTRUSTED;
2034 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2035 *type = SENDERTYPE_ADMINISTRATOR;
2036 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2037 *type = SENDERTYPE_OFFICIAL;
2038 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2039 *type = SENDERTYPE_VIRTUAL;
2040 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2041 *type = SENDERTYPE_OFFICIAL_CERT;
2042 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2043 *type = SENDERTYPE_LIQUIDATOR;
2044 else
2045 return IE_ENUM;
2046 return IE_SUCCESS;
2050 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2051 static isds_error string2isds_payment_type(const xmlChar *string,
2052 isds_payment_type *type) {
2053 if (!string || !type) return IE_INVAL;
2055 if (!xmlStrcmp(string, BAD_CAST "K"))
2056 *type = PAYMENT_SENDER;
2057 else if (!xmlStrcmp(string, BAD_CAST "O"))
2058 *type = PAYMENT_RESPONSE;
2059 else if (!xmlStrcmp(string, BAD_CAST "G"))
2060 *type = PAYMENT_SPONSOR;
2061 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2062 *type = PAYMENT_SPONSOR_LIMITED;
2063 else if (!xmlStrcmp(string, BAD_CAST "D"))
2064 *type = PAYMENT_SPONSOR_EXTERNAL;
2065 else if (!xmlStrcmp(string, BAD_CAST "E"))
2066 *type = PAYMENT_STAMP;
2067 else
2068 return IE_ENUM;
2069 return IE_SUCCESS;
2073 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2074 * ciEventType is integer but we convert it from string representation
2075 * directly. */
2076 static isds_error string2isds_credit_event_type(const xmlChar *string,
2077 isds_credit_event_type *type) {
2078 if (!string || !type) return IE_INVAL;
2080 if (!xmlStrcmp(string, BAD_CAST "1"))
2081 *type = ISDS_CREDIT_CHARGED;
2082 else if (!xmlStrcmp(string, BAD_CAST "2"))
2083 *type = ISDS_CREDIT_DISCHARGED;
2084 else if (!xmlStrcmp(string, BAD_CAST "3"))
2085 *type = ISDS_CREDIT_MESSAGE_SENT;
2086 else if (!xmlStrcmp(string, BAD_CAST "4"))
2087 *type = ISDS_CREDIT_STORAGE_SET;
2088 else if (!xmlStrcmp(string, BAD_CAST "5"))
2089 *type = ISDS_CREDIT_EXPIRED;
2090 else
2091 return IE_ENUM;
2092 return IE_SUCCESS;
2096 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2097 * @Return pointer to static string, or NULL if unknown enum value */
2098 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2099 switch(type) {
2100 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2101 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2102 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2103 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2104 default: return NULL; break;
2107 #endif /* HAVE_LIBCURL */
2110 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2111 * @Return IE_ENUM if @string is not valid enum member */
2112 static isds_error string2isds_FileMetaType(const xmlChar *string,
2113 isds_FileMetaType *type) {
2114 if (!string || !type) return IE_INVAL;
2116 if (!xmlStrcmp(string, BAD_CAST "main"))
2117 *type = FILEMETATYPE_MAIN;
2118 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2119 *type = FILEMETATYPE_ENCLOSURE;
2120 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2121 *type = FILEMETATYPE_SIGNATURE;
2122 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2123 *type = FILEMETATYPE_META;
2124 else
2125 return IE_ENUM;
2126 return IE_SUCCESS;
2130 /* Convert UTF-8 @string to ISDS hash @algorithm.
2131 * @Return IE_ENUM if @string is not valid enum member */
2132 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2133 isds_hash_algorithm *algorithm) {
2134 if (!string || !algorithm) return IE_INVAL;
2136 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2137 *algorithm = HASH_ALGORITHM_MD5;
2138 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2139 *algorithm = HASH_ALGORITHM_SHA_1;
2140 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2141 *algorithm = HASH_ALGORITHM_SHA_224;
2142 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2143 *algorithm = HASH_ALGORITHM_SHA_256;
2144 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2145 *algorithm = HASH_ALGORITHM_SHA_384;
2146 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2147 *algorithm = HASH_ALGORITHM_SHA_512;
2148 else
2149 return IE_ENUM;
2150 return IE_SUCCESS;
2154 #if HAVE_LIBCURL
2155 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2156 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2157 if (!time || !string) return IE_INVAL;
2159 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2160 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2161 return IE_ERROR;
2163 return IE_SUCCESS;
2167 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2168 * respects the @time microseconds too. */
2169 static isds_error timeval2timestring(const struct timeval *time,
2170 xmlChar **string) {
2171 struct tm broken;
2173 if (!time || !string) return IE_INVAL;
2175 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
2176 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2178 /* TODO: small negative year should be formatted as "-0012". This is not
2179 * true for glibc "%04d". We should implement it.
2180 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
2181 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2182 if (-1 == isds_asprintf((char **) string,
2183 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
2184 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2185 broken.tm_hour, broken.tm_min, broken.tm_sec,
2186 time->tv_usec))
2187 return IE_ERROR;
2189 return IE_SUCCESS;
2191 #endif /* HAVE_LIBCURL */
2194 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2195 * It respects microseconds too.
2196 * In case of error, @time will be freed. */
2197 static isds_error timestring2timeval(const xmlChar *string,
2198 struct timeval **time) {
2199 struct tm broken;
2200 char *offset, *delim, *endptr;
2201 char subseconds[7];
2202 int offset_hours, offset_minutes;
2203 int i;
2204 #ifdef _WIN32
2205 int tmp;
2206 #endif
2208 if (!time) return IE_INVAL;
2209 if (!string) {
2210 zfree(*time);
2211 return IE_INVAL;
2214 memset(&broken, 0, sizeof(broken));
2216 if (!*time) {
2217 *time = calloc(1, sizeof(**time));
2218 if (!*time) return IE_NOMEM;
2219 } else {
2220 memset(*time, 0, sizeof(**time));
2224 /* xsd:date is ISO 8601 string, thus ASCII */
2225 /*TODO: negative year */
2227 #ifdef _WIN32
2228 i = 0;
2229 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2230 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2231 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2232 &i)) < 6) {
2233 zfree(*time);
2234 return IE_DATE;
2237 broken.tm_year -= 1900;
2238 broken.tm_mon--;
2239 offset = (char*)string + i;
2240 #else
2241 /* Parse date and time without subseconds and offset */
2242 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2243 if (!offset) {
2244 zfree(*time);
2245 return IE_DATE;
2247 #endif
2249 /* Get subseconds */
2250 if (*offset == '.' ) {
2251 offset++;
2253 /* Copy first 6 digits, pad it with zeros.
2254 * XXX: It truncates longer number, no round.
2255 * Current server implementation uses only millisecond resolution. */
2256 /* TODO: isdigit() is locale sensitive */
2257 for (i = 0;
2258 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
2259 i++, offset++) {
2260 subseconds[i] = *offset;
2262 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
2263 subseconds[i] = '0';
2265 subseconds[6] = '\0';
2267 /* Convert it into integer */
2268 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
2269 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
2270 (*time)->tv_usec == LONG_MAX) {
2271 zfree(*time);
2272 return IE_DATE;
2275 /* move to the zone offset delimiter or signal NULL*/
2276 delim = strchr(offset, '-');
2277 if (!delim)
2278 delim = strchr(offset, '+');
2279 if (!delim)
2280 delim = strchr(offset, 'Z');
2281 offset = delim;
2284 /* Get zone offset */
2285 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2286 * "" equals to "Z" and it means UTC zone. */
2287 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2288 * colon separator */
2289 if (offset && (*offset == '-' || *offset == '+')) {
2290 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2291 zfree(*time);
2292 return IE_DATE;
2294 if (*offset == '+') {
2295 broken.tm_hour -= offset_hours;
2296 broken.tm_min -= offset_minutes;
2297 } else {
2298 broken.tm_hour += offset_hours;
2299 broken.tm_min += offset_minutes;
2303 /* Convert to time_t */
2304 (*time)->tv_sec = _isds_timegm(&broken);
2305 if ((*time)->tv_sec == (time_t) -1) {
2306 zfree(*time);
2307 return IE_DATE;
2310 return IE_SUCCESS;
2314 /* Convert unsigned int into isds_message_status.
2315 * @context is session context
2316 * @number is pointer to number value. NULL will be treated as invalid value.
2317 * @status is automatically reallocated status
2318 * @return IE_SUCCESS, or error code and free status */
2319 static isds_error uint2isds_message_status(struct isds_ctx *context,
2320 const unsigned long int *number, isds_message_status **status) {
2321 if (!context) return IE_INVALID_CONTEXT;
2322 if (!status) return IE_INVAL;
2324 free(*status); *status = NULL;
2325 if (!number) return IE_INVAL;
2327 if (*number < 1 || *number > 10) {
2328 isds_printf_message(context, _("Invalid message status value: %lu"),
2329 *number);
2330 return IE_ENUM;
2333 *status = malloc(sizeof(**status));
2334 if (!*status) return IE_NOMEM;
2336 **status = 1 << *number;
2337 return IE_SUCCESS;
2341 /* Convert event description string into isds_event members type and
2342 * description
2343 * @string is raw event description starting with event prefix
2344 * @event is structure where to store type and stripped description to
2345 * @return standard error code, unknown prefix is not classified as an error.
2346 * */
2347 static isds_error eventstring2event(const xmlChar *string,
2348 struct isds_event* event) {
2349 const xmlChar *known_prefixes[] = {
2350 BAD_CAST "EV0:",
2351 BAD_CAST "EV1:",
2352 BAD_CAST "EV2:",
2353 BAD_CAST "EV3:",
2354 BAD_CAST "EV4:",
2355 BAD_CAST "EV5:",
2356 BAD_CAST "EV11:",
2357 BAD_CAST "EV12:",
2358 BAD_CAST "EV13:"
2360 const isds_event_type types[] = {
2361 EVENT_ENTERED_SYSTEM,
2362 EVENT_ACCEPTED_BY_RECIPIENT,
2363 EVENT_ACCEPTED_BY_FICTION,
2364 EVENT_UNDELIVERABLE,
2365 EVENT_COMMERCIAL_ACCEPTED,
2366 EVENT_DELIVERED,
2367 EVENT_PRIMARY_LOGIN,
2368 EVENT_ENTRUSTED_LOGIN,
2369 EVENT_SYSCERT_LOGIN
2371 unsigned int index;
2372 size_t length;
2374 if (!string || !event) return IE_INVAL;
2376 if (!event->type) {
2377 event->type = malloc(sizeof(*event->type));
2378 if (!(event->type)) return IE_NOMEM;
2380 zfree(event->description);
2382 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2383 index++) {
2384 length = xmlUTF8Strlen(known_prefixes[index]);
2386 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2387 /* Prefix is known */
2388 *event->type = types[index];
2390 /* Strip prefix from description and spaces */
2391 /* TODO: Recognize all white spaces from UCS blank class and
2392 * operate on UTF-8 chars. */
2393 for (; string[length] != '\0' && string[length] == ' '; length++);
2394 event->description = strdup((char *) (string + length));
2395 if (!(event->description)) return IE_NOMEM;
2397 return IE_SUCCESS;
2401 /* Unknown event prefix.
2402 * XSD allows any string */
2403 char *string_locale = _isds_utf82locale((char *) string);
2404 isds_log(ILF_ISDS, ILL_WARNING,
2405 _("Unknown delivery info event prefix: %s\n"), string_locale);
2406 free(string_locale);
2408 *event->type = EVENT_UKNOWN;
2409 event->description = strdup((char *) string);
2410 if (!(event->description)) return IE_NOMEM;
2412 return IE_SUCCESS;
2416 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2417 * and leave label */
2418 #define EXTRACT_STRING(element, string) { \
2419 xmlXPathFreeObject(result); \
2420 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2421 if (NULL == (result)) { \
2422 err = IE_ERROR; \
2423 goto leave; \
2425 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2426 if (result->nodesetval->nodeNr > 1) { \
2427 isds_printf_message(context, _("Multiple %s element"), element); \
2428 err = IE_ERROR; \
2429 goto leave; \
2431 (string) = (char *) \
2432 xmlXPathCastNodeSetToString(result->nodesetval); \
2433 if (NULL == (string)) { \
2434 err = IE_ERROR; \
2435 goto leave; \
2440 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2442 char *string = NULL; \
2443 EXTRACT_STRING(element, string); \
2445 if (string) { \
2446 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2447 if (!(booleanPtr)) { \
2448 free(string); \
2449 err = IE_NOMEM; \
2450 goto leave; \
2453 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2454 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2455 *(booleanPtr) = 1; \
2456 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2457 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2458 *(booleanPtr) = 0; \
2459 else { \
2460 char *string_locale = _isds_utf82locale((char*)string); \
2461 isds_printf_message(context, \
2462 _("%s value is not valid boolean: %s"), \
2463 element, string_locale); \
2464 free(string_locale); \
2465 free(string); \
2466 err = IE_ERROR; \
2467 goto leave; \
2470 free(string); \
2474 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2476 char *string = NULL; \
2477 EXTRACT_STRING(element, string); \
2478 if (string) { \
2479 long int number; \
2480 char *endptr; \
2482 number = strtol((char*)string, &endptr, 10); \
2484 if (*endptr != '\0') { \
2485 char *string_locale = _isds_utf82locale((char *)string); \
2486 isds_printf_message(context, \
2487 _("%s is not valid integer: %s"), \
2488 element, string_locale); \
2489 free(string_locale); \
2490 free(string); \
2491 err = IE_ISDS; \
2492 goto leave; \
2495 if (number == LONG_MIN || number == LONG_MAX) { \
2496 char *string_locale = _isds_utf82locale((char *)string); \
2497 isds_printf_message(context, \
2498 _("%s value out of range of long int: %s"), \
2499 element, string_locale); \
2500 free(string_locale); \
2501 free(string); \
2502 err = IE_ERROR; \
2503 goto leave; \
2506 free(string); string = NULL; \
2508 if (!(preallocated)) { \
2509 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2510 if (!(longintPtr)) { \
2511 err = IE_NOMEM; \
2512 goto leave; \
2515 *(longintPtr) = number; \
2519 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2521 char *string = NULL; \
2522 EXTRACT_STRING(element, string); \
2523 if (string) { \
2524 long int number; \
2525 char *endptr; \
2527 number = strtol((char*)string, &endptr, 10); \
2529 if (*endptr != '\0') { \
2530 char *string_locale = _isds_utf82locale((char *)string); \
2531 isds_printf_message(context, \
2532 _("%s is not valid integer: %s"), \
2533 element, string_locale); \
2534 free(string_locale); \
2535 free(string); \
2536 err = IE_ISDS; \
2537 goto leave; \
2540 if (number == LONG_MIN || number == LONG_MAX) { \
2541 char *string_locale = _isds_utf82locale((char *)string); \
2542 isds_printf_message(context, \
2543 _("%s value out of range of long int: %s"), \
2544 element, string_locale); \
2545 free(string_locale); \
2546 free(string); \
2547 err = IE_ERROR; \
2548 goto leave; \
2551 free(string); string = NULL; \
2552 if (number < 0) { \
2553 isds_printf_message(context, \
2554 _("%s value is negative: %ld"), element, number); \
2555 err = IE_ERROR; \
2556 goto leave; \
2559 if (!(preallocated)) { \
2560 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2561 if (!(ulongintPtr)) { \
2562 err = IE_NOMEM; \
2563 goto leave; \
2566 *(ulongintPtr) = number; \
2570 #define EXTRACT_DATE(element, tmPtr) { \
2571 char *string = NULL; \
2572 EXTRACT_STRING(element, string); \
2573 if (NULL != string) { \
2574 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2575 if (NULL == (tmPtr)) { \
2576 free(string); \
2577 err = IE_NOMEM; \
2578 goto leave; \
2580 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2581 if (err) { \
2582 if (err == IE_NOTSUP) { \
2583 err = IE_ISDS; \
2584 char *string_locale = _isds_utf82locale(string); \
2585 char *element_locale = _isds_utf82locale(element); \
2586 isds_printf_message(context, _("Invalid %s value: %s"), \
2587 element_locale, string_locale); \
2588 free(string_locale); \
2589 free(element_locale); \
2591 free(string); \
2592 goto leave; \
2594 free(string); \
2598 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2599 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2600 NULL); \
2601 if ((required) && (!string)) { \
2602 char *attribute_locale = _isds_utf82locale(attribute); \
2603 char *element_locale = \
2604 _isds_utf82locale((char *)xpath_ctx->node->name); \
2605 isds_printf_message(context, \
2606 _("Could not extract required %s attribute value from " \
2607 "%s element"), attribute_locale, element_locale); \
2608 free(element_locale); \
2609 free(attribute_locale); \
2610 err = IE_ERROR; \
2611 goto leave; \
2616 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2618 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2619 (xmlChar *) (string)); \
2620 if (!node) { \
2621 isds_printf_message(context, \
2622 _("Could not add %s child to %s element"), \
2623 element, (parent)->name); \
2624 err = IE_ERROR; \
2625 goto leave; \
2629 #define INSERT_STRING(parent, element, string) \
2630 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2632 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2634 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2635 else { INSERT_STRING(parent, element, "false"); } \
2638 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2640 if (booleanPtr) { \
2641 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2642 } else { \
2643 INSERT_STRING(parent, element, NULL); \
2647 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2648 if ((longintPtr)) { \
2649 /* FIXME: locale sensitive */ \
2650 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2651 err = IE_NOMEM; \
2652 goto leave; \
2654 INSERT_STRING(parent, element, buffer) \
2655 free(buffer); (buffer) = NULL; \
2656 } else { INSERT_STRING(parent, element, NULL) } \
2659 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2660 if ((ulongintPtr)) { \
2661 /* FIXME: locale sensitive */ \
2662 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2663 err = IE_NOMEM; \
2664 goto leave; \
2666 INSERT_STRING(parent, element, buffer) \
2667 free(buffer); (buffer) = NULL; \
2668 } else { INSERT_STRING(parent, element, NULL) } \
2671 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2673 /* FIXME: locale sensitive */ \
2674 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2675 err = IE_NOMEM; \
2676 goto leave; \
2678 INSERT_STRING(parent, element, buffer) \
2679 free(buffer); (buffer) = NULL; \
2682 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2683 * new attribute. */
2684 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2686 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2687 (xmlChar *) (string)); \
2688 if (!attribute_node) { \
2689 isds_printf_message(context, _("Could not add %s " \
2690 "attribute to %s element"), \
2691 (attribute), (parent)->name); \
2692 err = IE_ERROR; \
2693 goto leave; \
2697 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2698 if (string) { \
2699 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2700 if (length > (maximum)) { \
2701 isds_printf_message(context, \
2702 ngettext("%s has more than %d characters", \
2703 "%s has more than %d characters", (maximum)), \
2704 (name), (maximum)); \
2705 err = IE_2BIG; \
2706 goto leave; \
2708 if (length < (minimum)) { \
2709 isds_printf_message(context, \
2710 ngettext("%s has less than %d characters", \
2711 "%s has less than %d characters", (minimum)), \
2712 (name), (minimum)); \
2713 err = IE_2SMALL; \
2714 goto leave; \
2719 #define INSERT_ELEMENT(child, parent, element) \
2721 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2722 if (!(child)) { \
2723 isds_printf_message(context, \
2724 _("Could not add %s child to %s element"), \
2725 (element), (parent)->name); \
2726 err = IE_ERROR; \
2727 goto leave; \
2732 /* Find child element by name in given XPath context and switch context onto
2733 * it. The child must be uniq and must exist. Otherwise fails.
2734 * @context is ISDS context
2735 * @child is child element name
2736 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2737 * into it child. In error case, the @xpath_ctx keeps original value. */
2738 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2739 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2740 isds_error err = IE_SUCCESS;
2741 xmlXPathObjectPtr result = NULL;
2743 if (!context) return IE_INVALID_CONTEXT;
2744 if (!child || !xpath_ctx) return IE_INVAL;
2746 /* Find child */
2747 result = xmlXPathEvalExpression(child, xpath_ctx);
2748 if (!result) {
2749 err = IE_XML;
2750 goto leave;
2753 /* No match */
2754 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2755 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2756 char *child_locale = _isds_utf82locale((char*) child);
2757 isds_printf_message(context,
2758 _("%s element does not contain %s child"),
2759 parent_locale, child_locale);
2760 free(child_locale);
2761 free(parent_locale);
2762 err = IE_NOEXIST;
2763 goto leave;
2766 /* More matches */
2767 if (result->nodesetval->nodeNr > 1) {
2768 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2769 char *child_locale = _isds_utf82locale((char*) child);
2770 isds_printf_message(context,
2771 _("%s element contains multiple %s children"),
2772 parent_locale, child_locale);
2773 free(child_locale);
2774 free(parent_locale);
2775 err = IE_NOTUNIQ;
2776 goto leave;
2779 /* Switch context */
2780 xpath_ctx->node = result->nodesetval->nodeTab[0];
2782 leave:
2783 xmlXPathFreeObject(result);
2784 return err;
2789 #if HAVE_LIBCURL
2790 /* Find and convert XSD:gPersonName group in current node into structure
2791 * @context is ISDS context
2792 * @personName is automatically reallocated person name structure. If no member
2793 * value is found, will be freed.
2794 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2795 * elements
2796 * In case of error @personName will be freed. */
2797 static isds_error extract_gPersonName(struct isds_ctx *context,
2798 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2799 isds_error err = IE_SUCCESS;
2800 xmlXPathObjectPtr result = NULL;
2802 if (!context) return IE_INVALID_CONTEXT;
2803 if (!personName) return IE_INVAL;
2804 isds_PersonName_free(personName);
2805 if (!xpath_ctx) return IE_INVAL;
2808 *personName = calloc(1, sizeof(**personName));
2809 if (!*personName) {
2810 err = IE_NOMEM;
2811 goto leave;
2814 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2815 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2816 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2817 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2819 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2820 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2821 isds_PersonName_free(personName);
2823 leave:
2824 if (err) isds_PersonName_free(personName);
2825 xmlXPathFreeObject(result);
2826 return err;
2830 /* Find and convert XSD:gAddress group in current node into structure
2831 * @context is ISDS context
2832 * @address is automatically reallocated address structure. If no member
2833 * value is found, will be freed.
2834 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2835 * elements
2836 * In case of error @address will be freed. */
2837 static isds_error extract_gAddress(struct isds_ctx *context,
2838 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2839 isds_error err = IE_SUCCESS;
2840 xmlXPathObjectPtr result = NULL;
2842 if (!context) return IE_INVALID_CONTEXT;
2843 if (!address) return IE_INVAL;
2844 isds_Address_free(address);
2845 if (!xpath_ctx) return IE_INVAL;
2848 *address = calloc(1, sizeof(**address));
2849 if (!*address) {
2850 err = IE_NOMEM;
2851 goto leave;
2854 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2855 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2856 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2857 EXTRACT_STRING("isds:adNumberInMunicipality",
2858 (*address)->adNumberInMunicipality);
2859 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2860 EXTRACT_STRING("isds:adState", (*address)->adState);
2862 if (!(*address)->adCity && !(*address)->adStreet &&
2863 !(*address)->adNumberInStreet &&
2864 !(*address)->adNumberInMunicipality &&
2865 !(*address)->adZipCode && !(*address)->adState)
2866 isds_Address_free(address);
2868 leave:
2869 if (err) isds_Address_free(address);
2870 xmlXPathFreeObject(result);
2871 return err;
2875 /* Find and convert isds:biDate element in current node into structure
2876 * @context is ISDS context
2877 * @biDate is automatically reallocated birth date structure. If no member
2878 * value is found, will be freed.
2879 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2880 * element
2881 * In case of error @biDate will be freed. */
2882 static isds_error extract_BiDate(struct isds_ctx *context,
2883 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2884 isds_error err = IE_SUCCESS;
2885 xmlXPathObjectPtr result = NULL;
2886 char *string = NULL;
2888 if (!context) return IE_INVALID_CONTEXT;
2889 if (!biDate) return IE_INVAL;
2890 zfree(*biDate);
2891 if (!xpath_ctx) return IE_INVAL;
2893 EXTRACT_STRING("isds:biDate", string);
2894 if (string) {
2895 *biDate = calloc(1, sizeof(**biDate));
2896 if (!*biDate) {
2897 err = IE_NOMEM;
2898 goto leave;
2900 err = _isds_datestring2tm((xmlChar *)string, *biDate);
2901 if (err) {
2902 if (err == IE_NOTSUP) {
2903 err = IE_ISDS;
2904 char *string_locale = _isds_utf82locale(string);
2905 isds_printf_message(context,
2906 _("Invalid isds:biDate value: %s"), string_locale);
2907 free(string_locale);
2909 goto leave;
2913 leave:
2914 if (err) zfree(*biDate);
2915 free(string);
2916 xmlXPathFreeObject(result);
2917 return err;
2921 /* Convert isds:dBOwnerInfo XML tree into structure
2922 * @context is ISDS context
2923 * @db_owner_info is automatically reallocated box owner info structure
2924 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2925 * In case of error @db_owner_info will be freed. */
2926 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2927 struct isds_DbOwnerInfo **db_owner_info,
2928 xmlXPathContextPtr xpath_ctx) {
2929 isds_error err = IE_SUCCESS;
2930 xmlXPathObjectPtr result = NULL;
2931 char *string = NULL;
2933 if (!context) return IE_INVALID_CONTEXT;
2934 if (!db_owner_info) return IE_INVAL;
2935 isds_DbOwnerInfo_free(db_owner_info);
2936 if (!xpath_ctx) return IE_INVAL;
2939 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2940 if (!*db_owner_info) {
2941 err = IE_NOMEM;
2942 goto leave;
2945 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2947 EXTRACT_STRING("isds:dbType", string);
2948 if (string) {
2949 (*db_owner_info)->dbType =
2950 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2951 if (!(*db_owner_info)->dbType) {
2952 err = IE_NOMEM;
2953 goto leave;
2955 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2956 if (err) {
2957 zfree((*db_owner_info)->dbType);
2958 if (err == IE_ENUM) {
2959 err = IE_ISDS;
2960 char *string_locale = _isds_utf82locale(string);
2961 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2962 string_locale);
2963 free(string_locale);
2965 goto leave;
2967 zfree(string);
2970 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2972 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2973 xpath_ctx);
2974 if (err) goto leave;
2976 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2978 (*db_owner_info)->birthInfo =
2979 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2980 if (!(*db_owner_info)->birthInfo) {
2981 err = IE_NOMEM;
2982 goto leave;
2984 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2985 xpath_ctx);
2986 if (err) goto leave;
2987 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2988 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2989 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2990 if (!(*db_owner_info)->birthInfo->biDate &&
2991 !(*db_owner_info)->birthInfo->biCity &&
2992 !(*db_owner_info)->birthInfo->biCounty &&
2993 !(*db_owner_info)->birthInfo->biState)
2994 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2996 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2997 if (err) goto leave;
2999 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3000 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3001 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3002 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3003 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3005 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3007 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3008 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3009 (*db_owner_info)->dbOpenAddressing);
3011 leave:
3012 if (err) isds_DbOwnerInfo_free(db_owner_info);
3013 free(string);
3014 xmlXPathFreeObject(result);
3015 return err;
3019 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3020 * @context is session context
3021 * @owner is libisds structure with box description
3022 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3023 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3024 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
3026 isds_error err = IE_SUCCESS;
3027 xmlNodePtr node;
3028 xmlChar *string = NULL;
3030 if (!context) return IE_INVALID_CONTEXT;
3031 if (!owner || !db_owner_info) return IE_INVAL;
3034 /* Build XSD:tDbOwnerInfo */
3035 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3036 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3038 /* dbType */
3039 if (owner->dbType) {
3040 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
3041 if (!type_string) {
3042 isds_printf_message(context, _("Invalid dbType value: %d"),
3043 *(owner->dbType));
3044 err = IE_ENUM;
3045 goto leave;
3047 INSERT_STRING(db_owner_info, "dbType", type_string);
3049 INSERT_STRING(db_owner_info, "ic", owner->ic);
3050 if (owner->personName) {
3051 INSERT_STRING(db_owner_info, "pnFirstName",
3052 owner->personName->pnFirstName);
3053 INSERT_STRING(db_owner_info, "pnMiddleName",
3054 owner->personName->pnMiddleName);
3055 INSERT_STRING(db_owner_info, "pnLastName",
3056 owner->personName->pnLastName);
3057 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3058 owner->personName->pnLastNameAtBirth);
3060 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3061 if (owner->birthInfo) {
3062 if (owner->birthInfo->biDate) {
3063 if (!tm2datestring(owner->birthInfo->biDate, &string))
3064 INSERT_STRING(db_owner_info, "biDate", string);
3065 free(string); string = NULL;
3067 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
3068 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
3069 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
3071 if (owner->address) {
3072 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
3073 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
3074 INSERT_STRING(db_owner_info, "adNumberInStreet",
3075 owner->address->adNumberInStreet);
3076 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3077 owner->address->adNumberInMunicipality);
3078 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
3079 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
3081 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3082 INSERT_STRING(db_owner_info, "email", owner->email);
3083 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3085 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3086 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3088 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3089 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3091 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3093 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3094 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3095 owner->dbOpenAddressing);
3097 leave:
3098 free(string);
3099 return err;
3103 /* Convert XSD:tDbUserInfo XML tree into structure
3104 * @context is ISDS context
3105 * @db_user_info is automatically reallocated user info structure
3106 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3107 * In case of error @db_user_info will be freed. */
3108 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3109 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3110 isds_error err = IE_SUCCESS;
3111 xmlXPathObjectPtr result = NULL;
3112 char *string = NULL;
3114 if (!context) return IE_INVALID_CONTEXT;
3115 if (!db_user_info) return IE_INVAL;
3116 isds_DbUserInfo_free(db_user_info);
3117 if (!xpath_ctx) return IE_INVAL;
3120 *db_user_info = calloc(1, sizeof(**db_user_info));
3121 if (!*db_user_info) {
3122 err = IE_NOMEM;
3123 goto leave;
3126 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3128 EXTRACT_STRING("isds:userType", string);
3129 if (string) {
3130 (*db_user_info)->userType =
3131 calloc(1, sizeof(*((*db_user_info)->userType)));
3132 if (!(*db_user_info)->userType) {
3133 err = IE_NOMEM;
3134 goto leave;
3136 err = string2isds_UserType((xmlChar *)string,
3137 (*db_user_info)->userType);
3138 if (err) {
3139 zfree((*db_user_info)->userType);
3140 if (err == IE_ENUM) {
3141 err = IE_ISDS;
3142 char *string_locale = _isds_utf82locale(string);
3143 isds_printf_message(context,
3144 _("Unknown isds:userType value: %s"), string_locale);
3145 free(string_locale);
3147 goto leave;
3149 zfree(string);
3152 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3154 (*db_user_info)->personName =
3155 calloc(1, sizeof(*((*db_user_info)->personName)));
3156 if (!(*db_user_info)->personName) {
3157 err = IE_NOMEM;
3158 goto leave;
3161 err = extract_gPersonName(context, &(*db_user_info)->personName,
3162 xpath_ctx);
3163 if (err) goto leave;
3165 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3166 if (err) goto leave;
3168 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3169 if (err) goto leave;
3171 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3172 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3174 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3175 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3176 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3178 /* ???: Default value is "CZ" according specification. Should we provide
3179 * it? */
3180 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3182 leave:
3183 if (err) isds_DbUserInfo_free(db_user_info);
3184 free(string);
3185 xmlXPathFreeObject(result);
3186 return err;
3190 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3191 * @context is session context
3192 * @user is libisds structure with user description
3193 * @db_user_info is XML element of XSD:tDbUserInfo */
3194 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3195 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3197 isds_error err = IE_SUCCESS;
3198 xmlNodePtr node;
3199 xmlChar *string = NULL;
3201 if (!context) return IE_INVALID_CONTEXT;
3202 if (!user || !db_user_info) return IE_INVAL;
3204 /* Build XSD:tDbUserInfo */
3205 if (user->personName) {
3206 INSERT_STRING(db_user_info, "pnFirstName",
3207 user->personName->pnFirstName);
3208 INSERT_STRING(db_user_info, "pnMiddleName",
3209 user->personName->pnMiddleName);
3210 INSERT_STRING(db_user_info, "pnLastName",
3211 user->personName->pnLastName);
3212 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3213 user->personName->pnLastNameAtBirth);
3215 if (user->address) {
3216 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3217 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3218 INSERT_STRING(db_user_info, "adNumberInStreet",
3219 user->address->adNumberInStreet);
3220 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3221 user->address->adNumberInMunicipality);
3222 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3223 INSERT_STRING(db_user_info, "adState", user->address->adState);
3225 if (user->biDate) {
3226 if (!tm2datestring(user->biDate, &string))
3227 INSERT_STRING(db_user_info, "biDate", string);
3228 zfree(string);
3230 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3231 INSERT_STRING(db_user_info, "userID", user->userID);
3233 /* userType */
3234 if (user->userType) {
3235 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3236 if (!type_string) {
3237 isds_printf_message(context, _("Invalid userType value: %d"),
3238 *(user->userType));
3239 err = IE_ENUM;
3240 goto leave;
3242 INSERT_STRING(db_user_info, "userType", type_string);
3245 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3246 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3247 INSERT_STRING(db_user_info, "ic", user->ic);
3248 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3249 INSERT_STRING(db_user_info, "firmName", user->firmName);
3250 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3251 INSERT_STRING(db_user_info, "caCity", user->caCity);
3252 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3253 INSERT_STRING(db_user_info, "caState", user->caState);
3255 leave:
3256 free(string);
3257 return err;
3261 /* Convert XSD:tPDZRec XML tree into structure
3262 * @context is ISDS context
3263 * @permission is automatically reallocated commercial permission structure
3264 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3265 * In case of error @permission will be freed. */
3266 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3267 struct isds_commercial_permission **permission,
3268 xmlXPathContextPtr xpath_ctx) {
3269 isds_error err = IE_SUCCESS;
3270 xmlXPathObjectPtr result = NULL;
3271 char *string = NULL;
3273 if (!context) return IE_INVALID_CONTEXT;
3274 if (!permission) return IE_INVAL;
3275 isds_commercial_permission_free(permission);
3276 if (!xpath_ctx) return IE_INVAL;
3279 *permission = calloc(1, sizeof(**permission));
3280 if (!*permission) {
3281 err = IE_NOMEM;
3282 goto leave;
3285 EXTRACT_STRING("isds:PDZType", string);
3286 if (string) {
3287 err = string2isds_payment_type((xmlChar *)string,
3288 &(*permission)->type);
3289 if (err) {
3290 if (err == IE_ENUM) {
3291 err = IE_ISDS;
3292 char *string_locale = _isds_utf82locale(string);
3293 isds_printf_message(context,
3294 _("Unknown isds:PDZType value: %s"), string_locale);
3295 free(string_locale);
3297 goto leave;
3299 zfree(string);
3302 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3303 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3305 EXTRACT_STRING("isds:PDZExpire", string);
3306 if (string) {
3307 err = timestring2timeval((xmlChar *) string,
3308 &((*permission)->expiration));
3309 if (err) {
3310 char *string_locale = _isds_utf82locale(string);
3311 if (err == IE_DATE) err = IE_ISDS;
3312 isds_printf_message(context,
3313 _("Could not convert PDZExpire as ISO time: %s"),
3314 string_locale);
3315 free(string_locale);
3316 goto leave;
3318 zfree(string);
3321 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3322 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3324 leave:
3325 if (err) isds_commercial_permission_free(permission);
3326 free(string);
3327 xmlXPathFreeObject(result);
3328 return err;
3332 /* Convert XSD:tCiRecord XML tree into structure
3333 * @context is ISDS context
3334 * @event is automatically reallocated commercial credit event structure
3335 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3336 * In case of error @event will be freed. */
3337 static isds_error extract_CiRecord(struct isds_ctx *context,
3338 struct isds_credit_event **event,
3339 xmlXPathContextPtr xpath_ctx) {
3340 isds_error err = IE_SUCCESS;
3341 xmlXPathObjectPtr result = NULL;
3342 char *string = NULL;
3343 long int *number_ptr;
3345 if (!context) return IE_INVALID_CONTEXT;
3346 if (!event) return IE_INVAL;
3347 isds_credit_event_free(event);
3348 if (!xpath_ctx) return IE_INVAL;
3351 *event = calloc(1, sizeof(**event));
3352 if (!*event) {
3353 err = IE_NOMEM;
3354 goto leave;
3357 EXTRACT_STRING("isds:ciEventTime", string);
3358 if (string) {
3359 err = timestring2timeval((xmlChar *) string,
3360 &(*event)->time);
3361 if (err) {
3362 char *string_locale = _isds_utf82locale(string);
3363 if (err == IE_DATE) err = IE_ISDS;
3364 isds_printf_message(context,
3365 _("Could not convert ciEventTime as ISO time: %s"),
3366 string_locale);
3367 free(string_locale);
3368 goto leave;
3370 zfree(string);
3373 EXTRACT_STRING("isds:ciEventType", string);
3374 if (string) {
3375 err = string2isds_credit_event_type((xmlChar *)string,
3376 &(*event)->type);
3377 if (err) {
3378 if (err == IE_ENUM) {
3379 err = IE_ISDS;
3380 char *string_locale = _isds_utf82locale(string);
3381 isds_printf_message(context,
3382 _("Unknown isds:ciEventType value: %s"), string_locale);
3383 free(string_locale);
3385 goto leave;
3387 zfree(string);
3390 number_ptr = &((*event)->credit_change);
3391 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3392 number_ptr = &(*event)->new_credit;
3393 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3395 switch((*event)->type) {
3396 case ISDS_CREDIT_CHARGED:
3397 EXTRACT_STRING("isds:ciTransID",
3398 (*event)->details.charged.transaction);
3399 break;
3400 case ISDS_CREDIT_DISCHARGED:
3401 EXTRACT_STRING("isds:ciTransID",
3402 (*event)->details.discharged.transaction);
3403 break;
3404 case ISDS_CREDIT_MESSAGE_SENT:
3405 EXTRACT_STRING("isds:ciRecipientID",
3406 (*event)->details.message_sent.recipient);
3407 EXTRACT_STRING("isds:ciPDZID",
3408 (*event)->details.message_sent.message_id);
3409 break;
3410 case ISDS_CREDIT_STORAGE_SET:
3411 number_ptr = &((*event)->details.storage_set.new_capacity);
3412 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3413 EXTRACT_DATE("isds:ciNewFrom",
3414 (*event)->details.storage_set.new_valid_from);
3415 EXTRACT_DATE("isds:ciNewTo",
3416 (*event)->details.storage_set.new_valid_to);
3417 EXTRACT_LONGINT("isds:ciOldCapacity",
3418 (*event)->details.storage_set.old_capacity, 0);
3419 EXTRACT_DATE("isds:ciOldFrom",
3420 (*event)->details.storage_set.old_valid_from);
3421 EXTRACT_DATE("isds:ciOldTo",
3422 (*event)->details.storage_set.old_valid_to);
3423 EXTRACT_STRING("isds:ciDoneBy",
3424 (*event)->details.storage_set.initiator);
3425 break;
3426 case ISDS_CREDIT_EXPIRED:
3427 break;
3430 leave:
3431 if (err) isds_credit_event_free(event);
3432 free(string);
3433 xmlXPathFreeObject(result);
3434 return err;
3438 #endif /* HAVE_LIBCURL */
3441 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3442 * isds_envelope structure. The envelope is automatically allocated but not
3443 * reallocated. The date are just appended into envelope structure.
3444 * @context is ISDS context
3445 * @envelope is automatically allocated message envelope structure
3446 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3447 * In case of error @envelope will be freed. */
3448 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3449 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3450 isds_error err = IE_SUCCESS;
3451 xmlXPathObjectPtr result = NULL;
3453 if (!context) return IE_INVALID_CONTEXT;
3454 if (!envelope) return IE_INVAL;
3455 if (!xpath_ctx) return IE_INVAL;
3458 if (!*envelope) {
3459 /* Allocate envelope */
3460 *envelope = calloc(1, sizeof(**envelope));
3461 if (!*envelope) {
3462 err = IE_NOMEM;
3463 goto leave;
3465 } else {
3466 /* Else free former data */
3467 zfree((*envelope)->dmSenderOrgUnit);
3468 zfree((*envelope)->dmSenderOrgUnitNum);
3469 zfree((*envelope)->dbIDRecipient);
3470 zfree((*envelope)->dmRecipientOrgUnit);
3471 zfree((*envelope)->dmRecipientOrgUnitNum);
3472 zfree((*envelope)->dmToHands);
3473 zfree((*envelope)->dmAnnotation);
3474 zfree((*envelope)->dmRecipientRefNumber);
3475 zfree((*envelope)->dmSenderRefNumber);
3476 zfree((*envelope)->dmRecipientIdent);
3477 zfree((*envelope)->dmSenderIdent);
3478 zfree((*envelope)->dmLegalTitleLaw);
3479 zfree((*envelope)->dmLegalTitleYear);
3480 zfree((*envelope)->dmLegalTitleSect);
3481 zfree((*envelope)->dmLegalTitlePar);
3482 zfree((*envelope)->dmLegalTitlePoint);
3483 zfree((*envelope)->dmPersonalDelivery);
3484 zfree((*envelope)->dmAllowSubstDelivery);
3487 /* Extract envelope elements added by sender or ISDS
3488 * (XSD: gMessageEnvelopeSub type) */
3489 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3490 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3491 (*envelope)->dmSenderOrgUnitNum, 0);
3492 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3493 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3494 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3495 (*envelope)->dmRecipientOrgUnitNum, 0);
3496 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3497 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3498 EXTRACT_STRING("isds:dmRecipientRefNumber",
3499 (*envelope)->dmRecipientRefNumber);
3500 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3501 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3502 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3504 /* Extract envelope elements regarding law reference */
3505 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3506 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3507 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3508 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3509 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3511 /* Extract envelope other elements */
3512 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3513 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3514 (*envelope)->dmAllowSubstDelivery);
3516 leave:
3517 if (err) isds_envelope_free(envelope);
3518 xmlXPathFreeObject(result);
3519 return err;
3524 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3525 * isds_envelope structure. The envelope is automatically allocated but not
3526 * reallocated. The date are just appended into envelope structure.
3527 * @context is ISDS context
3528 * @envelope is automatically allocated message envelope structure
3529 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3530 * In case of error @envelope will be freed. */
3531 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3532 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3533 isds_error err = IE_SUCCESS;
3534 xmlXPathObjectPtr result = NULL;
3536 if (!context) return IE_INVALID_CONTEXT;
3537 if (!envelope) return IE_INVAL;
3538 if (!xpath_ctx) return IE_INVAL;
3541 if (!*envelope) {
3542 /* Allocate envelope */
3543 *envelope = calloc(1, sizeof(**envelope));
3544 if (!*envelope) {
3545 err = IE_NOMEM;
3546 goto leave;
3548 } else {
3549 /* Else free former data */
3550 zfree((*envelope)->dmID);
3551 zfree((*envelope)->dbIDSender);
3552 zfree((*envelope)->dmSender);
3553 zfree((*envelope)->dmSenderAddress);
3554 zfree((*envelope)->dmSenderType);
3555 zfree((*envelope)->dmRecipient);
3556 zfree((*envelope)->dmRecipientAddress);
3557 zfree((*envelope)->dmAmbiguousRecipient);
3560 /* Extract envelope elements added by ISDS
3561 * (XSD: gMessageEnvelope type) */
3562 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3563 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3564 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3565 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3566 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3567 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3568 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3569 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3570 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3571 (*envelope)->dmAmbiguousRecipient);
3573 /* Extract envelope elements added by sender and ISDS
3574 * (XSD: gMessageEnvelope type) */
3575 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3576 if (err) goto leave;
3578 leave:
3579 if (err) isds_envelope_free(envelope);
3580 xmlXPathFreeObject(result);
3581 return err;
3585 /* Convert other envelope elements from XML tree into isds_envelope structure:
3586 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3587 * The envelope is automatically allocated but not reallocated.
3588 * The data are just appended into envelope structure.
3589 * @context is ISDS context
3590 * @envelope is automatically allocated message envelope structure
3591 * @xpath_ctx is XPath context with current node as parent desired elements
3592 * In case of error @envelope will be freed. */
3593 static isds_error append_status_size_times(struct isds_ctx *context,
3594 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3595 isds_error err = IE_SUCCESS;
3596 xmlXPathObjectPtr result = NULL;
3597 char *string = NULL;
3598 unsigned long int *unumber = NULL;
3600 if (!context) return IE_INVALID_CONTEXT;
3601 if (!envelope) return IE_INVAL;
3602 if (!xpath_ctx) return IE_INVAL;
3605 if (!*envelope) {
3606 /* Allocate new */
3607 *envelope = calloc(1, sizeof(**envelope));
3608 if (!*envelope) {
3609 err = IE_NOMEM;
3610 goto leave;
3612 } else {
3613 /* Free old data */
3614 zfree((*envelope)->dmMessageStatus);
3615 zfree((*envelope)->dmAttachmentSize);
3616 zfree((*envelope)->dmDeliveryTime);
3617 zfree((*envelope)->dmAcceptanceTime);
3621 /* dmMessageStatus element is mandatory */
3622 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3623 if (!unumber) {
3624 isds_log_message(context,
3625 _("Missing mandatory sisds:dmMessageStatus integer"));
3626 err = IE_ISDS;
3627 goto leave;
3629 err = uint2isds_message_status(context, unumber,
3630 &((*envelope)->dmMessageStatus));
3631 if (err) {
3632 if (err == IE_ENUM) err = IE_ISDS;
3633 goto leave;
3635 free(unumber); unumber = NULL;
3637 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3640 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3641 if (string) {
3642 err = timestring2timeval((xmlChar *) string,
3643 &((*envelope)->dmDeliveryTime));
3644 if (err) {
3645 char *string_locale = _isds_utf82locale(string);
3646 if (err == IE_DATE) err = IE_ISDS;
3647 isds_printf_message(context,
3648 _("Could not convert dmDeliveryTime as ISO time: %s"),
3649 string_locale);
3650 free(string_locale);
3651 goto leave;
3653 zfree(string);
3656 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3657 if (string) {
3658 err = timestring2timeval((xmlChar *) string,
3659 &((*envelope)->dmAcceptanceTime));
3660 if (err) {
3661 char *string_locale = _isds_utf82locale(string);
3662 if (err == IE_DATE) err = IE_ISDS;
3663 isds_printf_message(context,
3664 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3665 string_locale);
3666 free(string_locale);
3667 goto leave;
3669 zfree(string);
3672 leave:
3673 if (err) isds_envelope_free(envelope);
3674 free(unumber);
3675 free(string);
3676 xmlXPathFreeObject(result);
3677 return err;
3681 /* Convert message type attribute of current element into isds_envelope
3682 * structure.
3683 * TODO: This function can be incorporated into append_status_size_times() as
3684 * they are called always together.
3685 * The envelope is automatically allocated but not reallocated.
3686 * The data are just appended into envelope structure.
3687 * @context is ISDS context
3688 * @envelope is automatically allocated message envelope structure
3689 * @xpath_ctx is XPath context with current node as parent of attribute
3690 * carrying message type
3691 * In case of error @envelope will be freed. */
3692 static isds_error append_message_type(struct isds_ctx *context,
3693 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3694 isds_error err = IE_SUCCESS;
3696 if (!context) return IE_INVALID_CONTEXT;
3697 if (!envelope) return IE_INVAL;
3698 if (!xpath_ctx) return IE_INVAL;
3701 if (!*envelope) {
3702 /* Allocate new */
3703 *envelope = calloc(1, sizeof(**envelope));
3704 if (!*envelope) {
3705 err = IE_NOMEM;
3706 goto leave;
3708 } else {
3709 /* Free old data */
3710 zfree((*envelope)->dmType);
3714 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3716 if (!(*envelope)->dmType) {
3717 /* Use default value */
3718 (*envelope)->dmType = strdup("V");
3719 if (!(*envelope)->dmType) {
3720 err = IE_NOMEM;
3721 goto leave;
3723 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3724 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3725 isds_printf_message(context,
3726 _("Message type in dmType attribute is not 1 character long: "
3727 "%s"),
3728 type_locale);
3729 free(type_locale);
3730 err = IE_ISDS;
3731 goto leave;
3734 leave:
3735 if (err) isds_envelope_free(envelope);
3736 return err;
3740 #if HAVE_LIBCURL
3741 /* Convert dmType isds_envelope member into XML attribute and append it to
3742 * current node.
3743 * @context is ISDS context
3744 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3745 * @dm_envelope is XML element the resulting attribute will be appended to.
3746 * @return error code, in case of error context' message is filled. */
3747 static isds_error insert_message_type(struct isds_ctx *context,
3748 const char *type, xmlNodePtr dm_envelope) {
3749 isds_error err = IE_SUCCESS;
3750 xmlAttrPtr attribute_node;
3752 if (!context) return IE_INVALID_CONTEXT;
3753 if (!dm_envelope) return IE_INVAL;
3755 /* Insert optional message type */
3756 if (type) {
3757 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3758 char *type_locale = _isds_utf82locale(type);
3759 isds_printf_message(context,
3760 _("Message type in envelope is not 1 character long: %s"),
3761 type_locale);
3762 free(type_locale);
3763 err = IE_INVAL;
3764 goto leave;
3766 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3769 leave:
3770 return err;
3772 #endif /* HAVE_LIBCURL */
3775 /* Extract message document into reallocated document structure
3776 * @context is ISDS context
3777 * @document is automatically reallocated message documents structure
3778 * @xpath_ctx is XPath context with current node as isds:dmFile
3779 * In case of error @document will be freed. */
3780 static isds_error extract_document(struct isds_ctx *context,
3781 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3782 isds_error err = IE_SUCCESS;
3783 xmlXPathObjectPtr result = NULL;
3784 xmlNodePtr file_node;
3785 char *string = NULL;
3787 if (!context) return IE_INVALID_CONTEXT;
3788 if (!document) return IE_INVAL;
3789 isds_document_free(document);
3790 if (!xpath_ctx) return IE_INVAL;
3791 file_node = xpath_ctx->node;
3793 *document = calloc(1, sizeof(**document));
3794 if (!*document) {
3795 err = IE_NOMEM;
3796 goto leave;
3799 /* Extract document meta data */
3800 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3801 if (context->normalize_mime_type) {
3802 const char *normalized_type =
3803 isds_normalize_mime_type((*document)->dmMimeType);
3804 if (NULL != normalized_type &&
3805 normalized_type != (*document)->dmMimeType) {
3806 char *new_type = strdup(normalized_type);
3807 if (NULL == new_type) {
3808 isds_printf_message(context,
3809 _("Not enough memory to normalize document MIME type"));
3810 err = IE_NOMEM;
3811 goto leave;
3813 free((*document)->dmMimeType);
3814 (*document)->dmMimeType = new_type;
3818 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3819 err = string2isds_FileMetaType((xmlChar*)string,
3820 &((*document)->dmFileMetaType));
3821 if (err) {
3822 char *meta_type_locale = _isds_utf82locale(string);
3823 isds_printf_message(context,
3824 _("Document has invalid dmFileMetaType attribute value: %s"),
3825 meta_type_locale);
3826 free(meta_type_locale);
3827 err = IE_ISDS;
3828 goto leave;
3830 zfree(string);
3832 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3833 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3834 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3835 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3838 /* Extract document data.
3839 * Base64 encoded blob or XML subtree must be presented. */
3841 /* Check for dmEncodedContent */
3842 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3843 xpath_ctx);
3844 if (!result) {
3845 err = IE_XML;
3846 goto leave;
3849 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3850 /* Here we have Base64 blob */
3851 (*document)->is_xml = 0;
3853 if (result->nodesetval->nodeNr > 1) {
3854 isds_printf_message(context,
3855 _("Document has more dmEncodedContent elements"));
3856 err = IE_ISDS;
3857 goto leave;
3860 xmlXPathFreeObject(result); result = NULL;
3861 EXTRACT_STRING("isds:dmEncodedContent", string);
3863 /* Decode non-empty document */
3864 if (string && string[0] != '\0') {
3865 (*document)->data_length =
3866 _isds_b64decode(string, &((*document)->data));
3867 if ((*document)->data_length == (size_t) -1) {
3868 isds_printf_message(context,
3869 _("Error while Base64-decoding document content"));
3870 err = IE_ERROR;
3871 goto leave;
3874 } else {
3875 /* No Base64 blob, try XML document */
3876 xmlXPathFreeObject(result); result = NULL;
3877 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3878 xpath_ctx);
3879 if (!result) {
3880 err = IE_XML;
3881 goto leave;
3884 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3885 /* Here we have XML document */
3886 (*document)->is_xml = 1;
3888 if (result->nodesetval->nodeNr > 1) {
3889 isds_printf_message(context,
3890 _("Document has more dmXMLContent elements"));
3891 err = IE_ISDS;
3892 goto leave;
3895 /* XXX: We cannot serialize the content simply because:
3896 * - XML document may point out of its scope (e.g. to message
3897 * envelope)
3898 * - isds:dmXMLContent can contain more elements, no element,
3899 * a text node only
3900 * - it's not the XML way
3901 * Thus we provide the only right solution: XML DOM. Let's
3902 * application to cope with this hot potato :) */
3903 (*document)->xml_node_list =
3904 result->nodesetval->nodeTab[0]->children;
3905 } else {
3906 /* No base64 blob, nor XML document */
3907 isds_printf_message(context,
3908 _("Document has no dmEncodedContent, nor dmXMLContent "
3909 "element"));
3910 err = IE_ISDS;
3911 goto leave;
3916 leave:
3917 if (err) isds_document_free(document);
3918 free(string);
3919 xmlXPathFreeObject(result);
3920 xpath_ctx->node = file_node;
3921 return err;
3926 /* Extract message documents into reallocated list of documents
3927 * @context is ISDS context
3928 * @documents is automatically reallocated message documents list structure
3929 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3930 * In case of error @documents will be freed. */
3931 static isds_error extract_documents(struct isds_ctx *context,
3932 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
3933 isds_error err = IE_SUCCESS;
3934 xmlXPathObjectPtr result = NULL;
3935 xmlNodePtr files_node;
3936 struct isds_list *document, *prev_document = NULL;
3938 if (!context) return IE_INVALID_CONTEXT;
3939 if (!documents) return IE_INVAL;
3940 isds_list_free(documents);
3941 if (!xpath_ctx) return IE_INVAL;
3942 files_node = xpath_ctx->node;
3944 /* Find documents */
3945 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
3946 if (!result) {
3947 err = IE_XML;
3948 goto leave;
3951 /* No match */
3952 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3953 isds_printf_message(context,
3954 _("Message does not contain any document"));
3955 err = IE_ISDS;
3956 goto leave;
3960 /* Iterate over documents */
3961 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3963 /* Allocate and append list item */
3964 document = calloc(1, sizeof(*document));
3965 if (!document) {
3966 err = IE_NOMEM;
3967 goto leave;
3969 document->destructor = (void (*)(void **))isds_document_free;
3970 if (i == 0) *documents = document;
3971 else prev_document->next = document;
3972 prev_document = document;
3974 /* Extract document */
3975 xpath_ctx->node = result->nodesetval->nodeTab[i];
3976 err = extract_document(context,
3977 (struct isds_document **) &(document->data), xpath_ctx);
3978 if (err) goto leave;
3982 leave:
3983 if (err) isds_list_free(documents);
3984 xmlXPathFreeObject(result);
3985 xpath_ctx->node = files_node;
3986 return err;
3990 #if HAVE_LIBCURL
3991 /* Convert isds:dmRecord XML tree into structure
3992 * @context is ISDS context
3993 * @envelope is automatically reallocated message envelope structure
3994 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3995 * In case of error @envelope will be freed. */
3996 static isds_error extract_DmRecord(struct isds_ctx *context,
3997 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3998 isds_error err = IE_SUCCESS;
3999 xmlXPathObjectPtr result = NULL;
4001 if (!context) return IE_INVALID_CONTEXT;
4002 if (!envelope) return IE_INVAL;
4003 isds_envelope_free(envelope);
4004 if (!xpath_ctx) return IE_INVAL;
4007 *envelope = calloc(1, sizeof(**envelope));
4008 if (!*envelope) {
4009 err = IE_NOMEM;
4010 goto leave;
4014 /* Extract tRecord data */
4015 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4017 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4018 * dmAcceptanceTime. */
4019 err = append_status_size_times(context, envelope, xpath_ctx);
4020 if (err) goto leave;
4022 /* Extract envelope elements added by sender and ISDS
4023 * (XSD: gMessageEnvelope type) */
4024 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4025 if (err) goto leave;
4027 /* Get message type */
4028 err = append_message_type(context, envelope, xpath_ctx);
4029 if (err) goto leave;
4032 leave:
4033 if (err) isds_envelope_free(envelope);
4034 xmlXPathFreeObject(result);
4035 return err;
4039 /* Convert XSD:tStateChangesRecord type XML tree into structure
4040 * @context is ISDS context
4041 * @changed_status is automatically reallocated message state change structure
4042 * @xpath_ctx is XPath context with current node as element of
4043 * XSD:tStateChangesRecord type
4044 * In case of error @changed_status will be freed. */
4045 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4046 struct isds_message_status_change **changed_status,
4047 xmlXPathContextPtr xpath_ctx) {
4048 isds_error err = IE_SUCCESS;
4049 xmlXPathObjectPtr result = NULL;
4050 unsigned long int *unumber = NULL;
4051 char *string = NULL;
4053 if (!context) return IE_INVALID_CONTEXT;
4054 if (!changed_status) return IE_INVAL;
4055 isds_message_status_change_free(changed_status);
4056 if (!xpath_ctx) return IE_INVAL;
4059 *changed_status = calloc(1, sizeof(**changed_status));
4060 if (!*changed_status) {
4061 err = IE_NOMEM;
4062 goto leave;
4066 /* Extract tGetStateChangesInput data */
4067 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4069 /* dmEventTime is mandatory */
4070 EXTRACT_STRING("isds:dmEventTime", string);
4071 if (string) {
4072 err = timestring2timeval((xmlChar *) string,
4073 &((*changed_status)->time));
4074 if (err) {
4075 char *string_locale = _isds_utf82locale(string);
4076 if (err == IE_DATE) err = IE_ISDS;
4077 isds_printf_message(context,
4078 _("Could not convert dmEventTime as ISO time: %s"),
4079 string_locale);
4080 free(string_locale);
4081 goto leave;
4083 zfree(string);
4086 /* dmMessageStatus element is mandatory */
4087 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4088 if (!unumber) {
4089 isds_log_message(context,
4090 _("Missing mandatory isds:dmMessageStatus integer"));
4091 err = IE_ISDS;
4092 goto leave;
4094 err = uint2isds_message_status(context, unumber,
4095 &((*changed_status)->dmMessageStatus));
4096 if (err) {
4097 if (err == IE_ENUM) err = IE_ISDS;
4098 goto leave;
4100 zfree(unumber);
4103 leave:
4104 free(unumber);
4105 free(string);
4106 if (err) isds_message_status_change_free(changed_status);
4107 xmlXPathFreeObject(result);
4108 return err;
4110 #endif /* HAVE_LIBCURL */
4113 /* Find and convert isds:dmHash XML tree into structure
4114 * @context is ISDS context
4115 * @envelope is automatically reallocated message hash structure
4116 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4117 * In case of error @hash will be freed. */
4118 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4119 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4120 isds_error err = IE_SUCCESS;
4121 xmlNodePtr old_ctx_node;
4122 xmlXPathObjectPtr result = NULL;
4123 char *string = NULL;
4125 if (!context) return IE_INVALID_CONTEXT;
4126 if (!hash) return IE_INVAL;
4127 isds_hash_free(hash);
4128 if (!xpath_ctx) return IE_INVAL;
4130 old_ctx_node = xpath_ctx->node;
4132 *hash = calloc(1, sizeof(**hash));
4133 if (!*hash) {
4134 err = IE_NOMEM;
4135 goto leave;
4138 /* Locate dmHash */
4139 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4140 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4141 err = IE_ISDS;
4142 goto leave;
4144 if (err) {
4145 err = IE_ERROR;
4146 goto leave;
4149 /* Get hash algorithm */
4150 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4151 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4152 if (err) {
4153 if (err == IE_ENUM) {
4154 char *string_locale = _isds_utf82locale(string);
4155 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4156 string_locale);
4157 free(string_locale);
4159 goto leave;
4161 zfree(string);
4163 /* Get hash value */
4164 EXTRACT_STRING(".", string);
4165 if (!string) {
4166 isds_printf_message(context,
4167 _("sisds:dmHash element is missing hash value"));
4168 err = IE_ISDS;
4169 goto leave;
4171 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4172 if ((*hash)->length == (size_t) -1) {
4173 isds_printf_message(context,
4174 _("Error while Base64-decoding hash value"));
4175 err = IE_ERROR;
4176 goto leave;
4179 leave:
4180 if (err) isds_hash_free(hash);
4181 free(string);
4182 xmlXPathFreeObject(result);
4183 xpath_ctx->node = old_ctx_node;
4184 return err;
4188 /* Find and append isds:dmQTimestamp XML tree into envelope.
4189 * Because one service is allowed to miss time-stamp content, and we think
4190 * other could too (flaw in specification), this function is deliberated and
4191 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4192 * @context is ISDS context
4193 * @envelope is automatically allocated envelope structure
4194 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4195 * child
4196 * In case of error @envelope will be freed. */
4197 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4198 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4199 isds_error err = IE_SUCCESS;
4200 xmlXPathObjectPtr result = NULL;
4201 char *string = NULL;
4203 if (!context) return IE_INVALID_CONTEXT;
4204 if (!envelope) return IE_INVAL;
4205 if (!xpath_ctx) {
4206 isds_envelope_free(envelope);
4207 return IE_INVAL;
4210 if (!*envelope) {
4211 *envelope = calloc(1, sizeof(**envelope));
4212 if (!*envelope) {
4213 err = IE_NOMEM;
4214 goto leave;
4216 } else {
4217 zfree((*envelope)->timestamp);
4218 (*envelope)->timestamp_length = 0;
4221 /* Get dmQTimestamp */
4222 EXTRACT_STRING("sisds:dmQTimestamp", string);
4223 if (!string) {
4224 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4225 goto leave;
4227 (*envelope)->timestamp_length =
4228 _isds_b64decode(string, &((*envelope)->timestamp));
4229 if ((*envelope)->timestamp_length == (size_t) -1) {
4230 isds_printf_message(context,
4231 _("Error while Base64-decoding time stamp value"));
4232 err = IE_ERROR;
4233 goto leave;
4236 leave:
4237 if (err) isds_envelope_free(envelope);
4238 free(string);
4239 xmlXPathFreeObject(result);
4240 return err;
4244 /* Convert XSD tReturnedMessage XML tree into message structure.
4245 * It does not store serialized XML tree into message->raw.
4246 * It does store (pointer to) parsed XML tree into message->xml if needed.
4247 * @context is ISDS context
4248 * @include_documents Use true if documents must be extracted
4249 * (tReturnedMessage XSD type), use false if documents shall be omitted
4250 * (tReturnedMessageEnvelope).
4251 * @message is automatically reallocated message structure
4252 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4253 * type
4254 * In case of error @message will be freed. */
4255 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4256 const _Bool include_documents, struct isds_message **message,
4257 xmlXPathContextPtr xpath_ctx) {
4258 isds_error err = IE_SUCCESS;
4259 xmlNodePtr message_node;
4261 if (!context) return IE_INVALID_CONTEXT;
4262 if (!message) return IE_INVAL;
4263 isds_message_free(message);
4264 if (!xpath_ctx) return IE_INVAL;
4267 *message = calloc(1, sizeof(**message));
4268 if (!*message) {
4269 err = IE_NOMEM;
4270 goto leave;
4273 /* Save message XPATH context node */
4274 message_node = xpath_ctx->node;
4277 /* Extract dmDM */
4278 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4279 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4280 if (err) { err = IE_ERROR; goto leave; }
4281 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4282 if (err) goto leave;
4284 if (include_documents) {
4285 struct isds_list *item;
4287 /* Extract dmFiles */
4288 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4289 xpath_ctx);
4290 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4291 err = IE_ISDS; goto leave;
4293 if (err) { err = IE_ERROR; goto leave; }
4294 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4295 if (err) goto leave;
4297 /* Store xmlDoc of this message if needed */
4298 /* Only if we got a XML document in all the documents. */
4299 for (item = (*message)->documents; item; item = item->next) {
4300 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4301 (*message)->xml = xpath_ctx->doc;
4302 break;
4308 /* Restore context to message */
4309 xpath_ctx->node = message_node;
4311 /* Extract dmHash */
4312 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4313 xpath_ctx);
4314 if (err) goto leave;
4316 /* Extract dmQTimestamp, */
4317 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4318 xpath_ctx);
4319 if (err) goto leave;
4321 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4322 * dmAcceptanceTime. */
4323 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4324 if (err) goto leave;
4326 /* Get message type */
4327 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4328 if (err) goto leave;
4330 leave:
4331 if (err) isds_message_free(message);
4332 return err;
4336 /* Extract message event into reallocated isds_event structure
4337 * @context is ISDS context
4338 * @event is automatically reallocated message event structure
4339 * @xpath_ctx is XPath context with current node as isds:dmEvent
4340 * In case of error @event will be freed. */
4341 static isds_error extract_event(struct isds_ctx *context,
4342 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4343 isds_error err = IE_SUCCESS;
4344 xmlXPathObjectPtr result = NULL;
4345 xmlNodePtr event_node;
4346 char *string = NULL;
4348 if (!context) return IE_INVALID_CONTEXT;
4349 if (!event) return IE_INVAL;
4350 isds_event_free(event);
4351 if (!xpath_ctx) return IE_INVAL;
4352 event_node = xpath_ctx->node;
4354 *event = calloc(1, sizeof(**event));
4355 if (!*event) {
4356 err = IE_NOMEM;
4357 goto leave;
4360 /* Extract event data.
4361 * All elements are optional according XSD. That's funny. */
4362 EXTRACT_STRING("sisds:dmEventTime", string);
4363 if (string) {
4364 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4365 if (err) {
4366 char *string_locale = _isds_utf82locale(string);
4367 if (err == IE_DATE) err = IE_ISDS;
4368 isds_printf_message(context,
4369 _("Could not convert dmEventTime as ISO time: %s"),
4370 string_locale);
4371 free(string_locale);
4372 goto leave;
4374 zfree(string);
4377 /* dmEventDescr element has prefix and the rest */
4378 EXTRACT_STRING("sisds:dmEventDescr", string);
4379 if (string) {
4380 err = eventstring2event((xmlChar *) string, *event);
4381 if (err) goto leave;
4382 zfree(string);
4385 leave:
4386 if (err) isds_event_free(event);
4387 free(string);
4388 xmlXPathFreeObject(result);
4389 xpath_ctx->node = event_node;
4390 return err;
4394 /* Convert element of XSD tEventsArray type from XML tree into
4395 * isds_list of isds_event's structure. The list is automatically reallocated.
4396 * @context is ISDS context
4397 * @events is automatically reallocated list of event structures
4398 * @xpath_ctx is XPath context with current node as tEventsArray
4399 * In case of error @events will be freed. */
4400 static isds_error extract_events(struct isds_ctx *context,
4401 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4402 isds_error err = IE_SUCCESS;
4403 xmlXPathObjectPtr result = NULL;
4404 xmlNodePtr events_node;
4405 struct isds_list *event, *prev_event = NULL;
4407 if (!context) return IE_INVALID_CONTEXT;
4408 if (!events) return IE_INVAL;
4409 if (!xpath_ctx) return IE_INVAL;
4410 events_node = xpath_ctx->node;
4412 /* Free old list */
4413 isds_list_free(events);
4415 /* Find events */
4416 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4417 if (!result) {
4418 err = IE_XML;
4419 goto leave;
4422 /* No match */
4423 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4424 isds_printf_message(context,
4425 _("Delivery info does not contain any event"));
4426 err = IE_ISDS;
4427 goto leave;
4431 /* Iterate over events */
4432 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4434 /* Allocate and append list item */
4435 event = calloc(1, sizeof(*event));
4436 if (!event) {
4437 err = IE_NOMEM;
4438 goto leave;
4440 event->destructor = (void (*)(void **))isds_event_free;
4441 if (i == 0) *events = event;
4442 else prev_event->next = event;
4443 prev_event = event;
4445 /* Extract event */
4446 xpath_ctx->node = result->nodesetval->nodeTab[i];
4447 err = extract_event(context,
4448 (struct isds_event **) &(event->data), xpath_ctx);
4449 if (err) goto leave;
4453 leave:
4454 if (err) isds_list_free(events);
4455 xmlXPathFreeObject(result);
4456 xpath_ctx->node = events_node;
4457 return err;
4461 #if HAVE_LIBCURL
4462 /* Insert Base64 encoded data as element with text child.
4463 * @context is session context
4464 * @parent is XML node to append @element with @data as child
4465 * @ns is XML namespace of @element, use NULL to inherit from @parent
4466 * @element is UTF-8 encoded name of new element
4467 * @data is bit stream to encode into @element
4468 * @length is size of @data in bytes
4469 * @return standard error code and fill long error message if needed */
4470 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4471 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4472 const void *data, size_t length) {
4473 isds_error err = IE_SUCCESS;
4474 xmlNodePtr node;
4476 if (!context) return IE_INVALID_CONTEXT;
4477 if (!data && length > 0) return IE_INVAL;
4478 if (!parent || !element) return IE_INVAL;
4480 xmlChar *base64data = NULL;
4481 base64data = (xmlChar *) _isds_b64encode(data, length);
4482 if (!base64data) {
4483 isds_printf_message(context,
4484 ngettext("Not enough memory to encode %zd byte into Base64",
4485 "Not enough memory to encode %zd bytes into Base64",
4486 length),
4487 length);
4488 err = IE_NOMEM;
4489 goto leave;
4491 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4493 leave:
4494 free(base64data);
4495 return err;
4499 /* Convert isds_document structure into XML tree and append to dmFiles node.
4500 * @context is session context
4501 * @document is ISDS document
4502 * @dm_files is XML element the resulting tree will be appended to as a child.
4503 * @return error code, in case of error context' message is filled. */
4504 static isds_error insert_document(struct isds_ctx *context,
4505 struct isds_document *document, xmlNodePtr dm_files) {
4506 isds_error err = IE_SUCCESS;
4507 xmlNodePtr new_file = NULL, file = NULL, node;
4508 xmlAttrPtr attribute_node;
4510 if (!context) return IE_INVALID_CONTEXT;
4511 if (!document || !dm_files) return IE_INVAL;
4513 /* Allocate new dmFile */
4514 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4515 if (!new_file) {
4516 isds_printf_message(context, _("Could not allocate main dmFile"));
4517 err = IE_ERROR;
4518 goto leave;
4520 /* Append the new dmFile.
4521 * XXX: Main document must go first */
4522 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4523 file = xmlAddPrevSibling(dm_files->children, new_file);
4524 else
4525 file = xmlAddChild(dm_files, new_file);
4527 if (!file) {
4528 xmlFreeNode(new_file); new_file = NULL;
4529 isds_printf_message(context, _("Could not add dmFile child to "
4530 "%s element"), dm_files->name);
4531 err = IE_ERROR;
4532 goto leave;
4535 /* @dmMimeType is required */
4536 if (!document->dmMimeType) {
4537 isds_log_message(context,
4538 _("Document is missing mandatory MIME type definition"));
4539 err = IE_INVAL;
4540 goto leave;
4542 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4544 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4545 if (!string) {
4546 isds_printf_message(context,
4547 _("Document has unknown dmFileMetaType: %ld"),
4548 document->dmFileMetaType);
4549 err = IE_ENUM;
4550 goto leave;
4552 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4554 if (document->dmFileGuid) {
4555 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4557 if (document->dmUpFileGuid) {
4558 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4561 /* @dmFileDescr is required */
4562 if (!document->dmFileDescr) {
4563 isds_log_message(context,
4564 _("Document is missing mandatory description (title)"));
4565 err = IE_INVAL;
4566 goto leave;
4568 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4570 if (document->dmFormat) {
4571 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4575 /* Insert content (body) of the document. */
4576 if (document->is_xml) {
4577 /* XML document requested */
4579 /* Allocate new dmXMLContent */
4580 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4581 if (!xmlcontent) {
4582 isds_printf_message(context,
4583 _("Could not allocate dmXMLContent element"));
4584 err = IE_ERROR;
4585 goto leave;
4587 /* Append it */
4588 node = xmlAddChild(file, xmlcontent);
4589 if (!node) {
4590 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4591 isds_printf_message(context,
4592 _("Could not add dmXMLContent child to %s element"),
4593 file->name);
4594 err = IE_ERROR;
4595 goto leave;
4598 /* Copy non-empty node list */
4599 if (document->xml_node_list) {
4600 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4601 document->xml_node_list);
4602 if (!content) {
4603 isds_printf_message(context,
4604 _("Not enough memory to copy XML document"));
4605 err = IE_NOMEM;
4606 goto leave;
4609 if (!xmlAddChildList(node, content)) {
4610 xmlFreeNodeList(content);
4611 isds_printf_message(context,
4612 _("Error while adding XML document into dmXMLContent"));
4613 err = IE_XML;
4614 goto leave;
4616 /* XXX: We cannot free the content here because it's part of node's
4617 * document since now. It will be freed with it automatically. */
4619 } else {
4620 /* Binary document requested */
4621 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4622 document->data, document->data_length);
4623 if (err) goto leave;
4626 leave:
4627 return err;
4631 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4632 * The copy must be preallocated, the date are just appended into structure.
4633 * @context is ISDS context
4634 * @copy is message copy structure
4635 * @xpath_ctx is XPath context with current node as tMStatus */
4636 static isds_error append_TMStatus(struct isds_ctx *context,
4637 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4638 isds_error err = IE_SUCCESS;
4639 xmlXPathObjectPtr result = NULL;
4640 char *code = NULL, *message = NULL;
4642 if (!context) return IE_INVALID_CONTEXT;
4643 if (!copy || !xpath_ctx) return IE_INVAL;
4645 /* Free old values */
4646 zfree(copy->dmStatus);
4647 zfree(copy->dmID);
4649 /* Get error specific to this copy */
4650 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4651 if (!code) {
4652 isds_log_message(context,
4653 _("Missing isds:dmStatusCode under "
4654 "XSD:tMStatus type element"));
4655 err = IE_ISDS;
4656 goto leave;
4659 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4660 /* This copy failed */
4661 copy->error = IE_ISDS;
4662 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4663 if (message) {
4664 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4665 if (!copy->dmStatus) {
4666 copy->dmStatus = code;
4667 code = NULL;
4669 } else {
4670 copy->dmStatus = code;
4671 code = NULL;
4673 } else {
4674 /* This copy succeeded. In this case only, message ID is valid */
4675 copy->error = IE_SUCCESS;
4677 EXTRACT_STRING("isds:dmID", copy->dmID);
4678 if (!copy->dmID) {
4679 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4680 "but did not returned assigned message ID\n"));
4681 err = IE_ISDS;
4685 leave:
4686 free(code);
4687 free(message);
4688 xmlXPathFreeObject(result);
4689 return err;
4693 /* Insert struct isds_approval data (box approval) into XML tree
4694 * @context is session context
4695 * @approval is libisds structure with approval description. NULL is
4696 * acceptable.
4697 * @parent is XML element to append @approval to */
4698 static isds_error insert_GExtApproval(struct isds_ctx *context,
4699 const struct isds_approval *approval, xmlNodePtr parent) {
4701 isds_error err = IE_SUCCESS;
4702 xmlNodePtr node;
4704 if (!context) return IE_INVALID_CONTEXT;
4705 if (!parent) return IE_INVAL;
4707 if (!approval) return IE_SUCCESS;
4709 /* Build XSD:gExtApproval */
4710 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4711 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4713 leave:
4714 return err;
4718 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4719 * code
4720 * @context is session context
4721 * @service_name is name of SERVICE_DB_ACCESS
4722 * @response is reallocated server SOAP body response as XML document
4723 * @raw_response is reallocated bit stream with response body. Use
4724 * NULL if you don't care
4725 * @raw_response_length is size of @raw_response in bytes
4726 * @code is reallocated ISDS status code
4727 * @status_message is reallocated ISDS status message
4728 * @return error coded from lower layer, context message will be set up
4729 * appropriately. */
4730 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4731 const xmlChar *service_name,
4732 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4733 xmlChar **code, xmlChar **status_message) {
4735 isds_error err = IE_SUCCESS;
4736 char *service_name_locale = NULL;
4737 xmlNodePtr request = NULL, node;
4738 xmlNsPtr isds_ns = NULL;
4740 if (!context) return IE_INVALID_CONTEXT;
4741 if (!service_name) return IE_INVAL;
4742 if (!response || !code || !status_message) return IE_INVAL;
4743 if (!raw_response_length && raw_response) return IE_INVAL;
4745 /* Free output argument */
4746 xmlFreeDoc(*response); *response = NULL;
4747 if (raw_response) zfree(*raw_response);
4748 zfree(*code);
4749 zfree(*status_message);
4752 /* Check if connection is established
4753 * TODO: This check should be done downstairs. */
4754 if (!context->curl) return IE_CONNECTION_CLOSED;
4756 service_name_locale = _isds_utf82locale((char*)service_name);
4757 if (!service_name_locale) {
4758 err = IE_NOMEM;
4759 goto leave;
4762 /* Build request */
4763 request = xmlNewNode(NULL, service_name);
4764 if (!request) {
4765 isds_printf_message(context,
4766 _("Could not build %s request"), service_name_locale);
4767 err = IE_ERROR;
4768 goto leave;
4770 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4771 if(!isds_ns) {
4772 isds_log_message(context, _("Could not create ISDS name space"));
4773 err = IE_ERROR;
4774 goto leave;
4776 xmlSetNs(request, isds_ns);
4779 /* Add XSD:tDummyInput child */
4780 INSERT_STRING(request, "dbDummy", NULL);
4783 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4784 service_name_locale);
4786 /* Send request */
4787 err = isds(context, SERVICE_DB_ACCESS, request, response,
4788 raw_response, raw_response_length);
4789 xmlFreeNode(request); request = NULL;
4791 if (err) {
4792 isds_log(ILF_ISDS, ILL_DEBUG,
4793 _("Processing ISDS response on %s request failed\n"),
4794 service_name_locale);
4795 goto leave;
4798 /* Check for response status */
4799 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4800 code, status_message, NULL);
4801 if (err) {
4802 isds_log(ILF_ISDS, ILL_DEBUG,
4803 _("ISDS response on %s request is missing status\n"),
4804 service_name_locale);
4805 goto leave;
4808 /* Request processed, but nothing found */
4809 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4810 char *code_locale = _isds_utf82locale((char*) *code);
4811 char *status_message_locale =
4812 _isds_utf82locale((char*) *status_message);
4813 isds_log(ILF_ISDS, ILL_DEBUG,
4814 _("Server refused %s request (code=%s, message=%s)\n"),
4815 service_name_locale, code_locale, status_message_locale);
4816 isds_log_message(context, status_message_locale);
4817 free(code_locale);
4818 free(status_message_locale);
4819 err = IE_ISDS;
4820 goto leave;
4823 leave:
4824 free(service_name_locale);
4825 xmlFreeNode(request);
4826 return err;
4828 #endif
4831 /* Get data about logged in user and his box. */
4832 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4833 struct isds_DbOwnerInfo **db_owner_info) {
4834 isds_error err = IE_SUCCESS;
4835 #if HAVE_LIBCURL
4836 xmlDocPtr response = NULL;
4837 xmlChar *code = NULL, *message = NULL;
4838 xmlXPathContextPtr xpath_ctx = NULL;
4839 xmlXPathObjectPtr result = NULL;
4840 char *string = NULL;
4841 #endif
4843 if (!context) return IE_INVALID_CONTEXT;
4844 zfree(context->long_message);
4845 if (!db_owner_info) return IE_INVAL;
4846 isds_DbOwnerInfo_free(db_owner_info);
4848 #if HAVE_LIBCURL
4849 /* Check if connection is established */
4850 if (!context->curl) return IE_CONNECTION_CLOSED;
4853 /* Do request and check for success */
4854 err = build_send_check_dbdummy_request(context,
4855 BAD_CAST "GetOwnerInfoFromLogin",
4856 &response, NULL, NULL, &code, &message);
4857 if (err) goto leave;
4860 /* Extract data */
4861 /* Prepare structure */
4862 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4863 if (!*db_owner_info) {
4864 err = IE_NOMEM;
4865 goto leave;
4867 xpath_ctx = xmlXPathNewContext(response);
4868 if (!xpath_ctx) {
4869 err = IE_ERROR;
4870 goto leave;
4872 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4873 err = IE_ERROR;
4874 goto leave;
4877 /* Set context node */
4878 result = xmlXPathEvalExpression(BAD_CAST
4879 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4880 if (!result) {
4881 err = IE_ERROR;
4882 goto leave;
4884 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4885 isds_log_message(context, _("Missing dbOwnerInfo element"));
4886 err = IE_ISDS;
4887 goto leave;
4889 if (result->nodesetval->nodeNr > 1) {
4890 isds_log_message(context, _("Multiple dbOwnerInfo element"));
4891 err = IE_ISDS;
4892 goto leave;
4894 xpath_ctx->node = result->nodesetval->nodeTab[0];
4895 xmlXPathFreeObject(result); result = NULL;
4897 /* Extract it */
4898 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
4901 leave:
4902 if (err) {
4903 isds_DbOwnerInfo_free(db_owner_info);
4906 free(string);
4907 xmlXPathFreeObject(result);
4908 xmlXPathFreeContext(xpath_ctx);
4910 free(code);
4911 free(message);
4912 xmlFreeDoc(response);
4914 if (!err)
4915 isds_log(ILF_ISDS, ILL_DEBUG,
4916 _("GetOwnerInfoFromLogin request processed by server "
4917 "successfully.\n"));
4918 #else /* not HAVE_LIBCURL */
4919 err = IE_NOTSUP;
4920 #endif
4922 return err;
4926 /* Get data about logged in user. */
4927 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
4928 struct isds_DbUserInfo **db_user_info) {
4929 isds_error err = IE_SUCCESS;
4930 #if HAVE_LIBCURL
4931 xmlDocPtr response = NULL;
4932 xmlChar *code = NULL, *message = NULL;
4933 xmlXPathContextPtr xpath_ctx = NULL;
4934 xmlXPathObjectPtr result = NULL;
4935 #endif
4937 if (!context) return IE_INVALID_CONTEXT;
4938 zfree(context->long_message);
4939 if (!db_user_info) return IE_INVAL;
4940 isds_DbUserInfo_free(db_user_info);
4942 #if HAVE_LIBCURL
4943 /* Check if connection is established */
4944 if (!context->curl) return IE_CONNECTION_CLOSED;
4947 /* Do request and check for success */
4948 err = build_send_check_dbdummy_request(context,
4949 BAD_CAST "GetUserInfoFromLogin",
4950 &response, NULL, NULL, &code, &message);
4951 if (err) goto leave;
4954 /* Extract data */
4955 /* Prepare structure */
4956 *db_user_info = calloc(1, sizeof(**db_user_info));
4957 if (!*db_user_info) {
4958 err = IE_NOMEM;
4959 goto leave;
4961 xpath_ctx = xmlXPathNewContext(response);
4962 if (!xpath_ctx) {
4963 err = IE_ERROR;
4964 goto leave;
4966 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4967 err = IE_ERROR;
4968 goto leave;
4971 /* Set context node */
4972 result = xmlXPathEvalExpression(BAD_CAST
4973 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
4974 if (!result) {
4975 err = IE_ERROR;
4976 goto leave;
4978 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4979 isds_log_message(context, _("Missing dbUserInfo element"));
4980 err = IE_ISDS;
4981 goto leave;
4983 if (result->nodesetval->nodeNr > 1) {
4984 isds_log_message(context, _("Multiple dbUserInfo element"));
4985 err = IE_ISDS;
4986 goto leave;
4988 xpath_ctx->node = result->nodesetval->nodeTab[0];
4989 xmlXPathFreeObject(result); result = NULL;
4991 /* Extract it */
4992 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
4994 leave:
4995 if (err) {
4996 isds_DbUserInfo_free(db_user_info);
4999 xmlXPathFreeObject(result);
5000 xmlXPathFreeContext(xpath_ctx);
5002 free(code);
5003 free(message);
5004 xmlFreeDoc(response);
5006 if (!err)
5007 isds_log(ILF_ISDS, ILL_DEBUG,
5008 _("GetUserInfoFromLogin request processed by server "
5009 "successfully.\n"));
5010 #else /* not HAVE_LIBCURL */
5011 err = IE_NOTSUP;
5012 #endif
5014 return err;
5018 /* Get expiration time of current password
5019 * @context is session context
5020 * @expiration is automatically reallocated time when password expires. If
5021 * password expiration is disabled, NULL will be returned. In case of error
5022 * it will be nulled too. */
5023 isds_error isds_get_password_expiration(struct isds_ctx *context,
5024 struct timeval **expiration) {
5025 isds_error err = IE_SUCCESS;
5026 #if HAVE_LIBCURL
5027 xmlDocPtr response = NULL;
5028 xmlChar *code = NULL, *message = NULL;
5029 xmlXPathContextPtr xpath_ctx = NULL;
5030 xmlXPathObjectPtr result = NULL;
5031 char *string = NULL;
5032 #endif
5034 if (!context) return IE_INVALID_CONTEXT;
5035 zfree(context->long_message);
5036 if (!expiration) return IE_INVAL;
5037 zfree(*expiration);
5039 #if HAVE_LIBCURL
5040 /* Check if connection is established */
5041 if (!context->curl) return IE_CONNECTION_CLOSED;
5044 /* Do request and check for success */
5045 err = build_send_check_dbdummy_request(context,
5046 BAD_CAST "GetPasswordInfo",
5047 &response, NULL, NULL, &code, &message);
5048 if (err) goto leave;
5051 /* Extract data */
5052 xpath_ctx = xmlXPathNewContext(response);
5053 if (!xpath_ctx) {
5054 err = IE_ERROR;
5055 goto leave;
5057 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5058 err = IE_ERROR;
5059 goto leave;
5062 /* Set context node */
5063 result = xmlXPathEvalExpression(BAD_CAST
5064 "/isds:GetPasswordInfoResponse", xpath_ctx);
5065 if (!result) {
5066 err = IE_ERROR;
5067 goto leave;
5069 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5070 isds_log_message(context,
5071 _("Missing GetPasswordInfoResponse element"));
5072 err = IE_ISDS;
5073 goto leave;
5075 if (result->nodesetval->nodeNr > 1) {
5076 isds_log_message(context,
5077 _("Multiple GetPasswordInfoResponse element"));
5078 err = IE_ISDS;
5079 goto leave;
5081 xpath_ctx->node = result->nodesetval->nodeTab[0];
5082 xmlXPathFreeObject(result); result = NULL;
5084 /* Extract expiration date */
5085 EXTRACT_STRING("isds:pswExpDate", string);
5086 if (string) {
5087 /* And convert it if any returned. Otherwise expiration is disabled. */
5088 err = timestring2timeval((xmlChar *) string, expiration);
5089 if (err) {
5090 char *string_locale = _isds_utf82locale(string);
5091 if (err == IE_DATE) err = IE_ISDS;
5092 isds_printf_message(context,
5093 _("Could not convert pswExpDate as ISO time: %s"),
5094 string_locale);
5095 free(string_locale);
5096 goto leave;
5100 leave:
5101 if (err) {
5102 if (*expiration) {
5103 zfree(*expiration);
5107 free(string);
5108 xmlXPathFreeObject(result);
5109 xmlXPathFreeContext(xpath_ctx);
5111 free(code);
5112 free(message);
5113 xmlFreeDoc(response);
5115 if (!err)
5116 isds_log(ILF_ISDS, ILL_DEBUG,
5117 _("GetPasswordInfo request processed by server "
5118 "successfully.\n"));
5119 #else /* not HAVE_LIBCURL */
5120 err = IE_NOTSUP;
5121 #endif
5123 return err;
5127 #if HAVE_LIBCURL
5128 /* Request delivering new TOTP code from ISDS through side channel before
5129 * changing password.
5130 * @context is session context
5131 * @password is current password.
5132 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5133 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5134 * function for more details.
5135 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5136 * NULL, if you don't care.
5137 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5138 * error code. */
5139 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5140 const char *password, struct isds_otp *otp, char **refnumber) {
5141 isds_error err = IE_SUCCESS;
5142 char *saved_url = NULL; /* No copy */
5143 #if HAVE_CURL_REAUTHORIZATION_BUG
5144 CURL *saved_curl = NULL; /* No copy */
5145 #endif
5146 xmlNsPtr isds_ns = NULL;
5147 xmlNodePtr request = NULL;
5148 xmlDocPtr response = NULL;
5149 xmlChar *code = NULL, *message = NULL;
5150 const xmlChar *codes[] = {
5151 BAD_CAST "2300",
5152 BAD_CAST "2301",
5153 BAD_CAST "2302"
5155 const char *meanings[] = {
5156 N_("Unexpected error"),
5157 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5158 N_("One-time code could not been sent. Try later again.")
5160 const isds_otp_resolution resolutions[] = {
5161 OTP_RESOLUTION_UNKNOWN,
5162 OTP_RESOLUTION_TO_FAST,
5163 OTP_RESOLUTION_TOTP_NOT_SENT
5166 if (NULL == context) return IE_INVALID_CONTEXT;
5167 zfree(context->long_message);
5168 if (NULL == password) {
5169 isds_log_message(context,
5170 _("Second argument (password) of isds_change_password() "
5171 "is NULL"));
5172 return IE_INVAL;
5175 /* Check if connection is established
5176 * TODO: This check should be done downstairs. */
5177 if (!context->curl) return IE_CONNECTION_CLOSED;
5179 if (!context->otp) {
5180 isds_log_message(context, _("This function requires OTP-authenticated "
5181 "context"));
5182 return IE_INVALID_CONTEXT;
5184 if (NULL == otp) {
5185 isds_log_message(context, _("If one-time password authentication "
5186 "method is in use, requesting new OTP code requires "
5187 "one-time credentials argument either"));
5188 return IE_INVAL;
5190 if (otp->method != OTP_TIME) {
5191 isds_log_message(context, _("Requesting new time-based OTP code from "
5192 "server requires one-time password authentication "
5193 "method"));
5194 return IE_INVAL;
5196 if (otp->otp_code != NULL) {
5197 isds_log_message(context, _("Requesting new time-based OTP code from "
5198 "server requires undefined OTP code member in "
5199 "one-time credentials argument"));
5200 return IE_INVAL;
5204 /* Build request */
5205 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5206 if (!request) {
5207 isds_log_message(context, _("Could not build SendSMSCode request"));
5208 return IE_ERROR;
5210 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5211 if(!isds_ns) {
5212 isds_log_message(context, _("Could not create ISDS name space"));
5213 xmlFreeNode(request);
5214 return IE_ERROR;
5216 xmlSetNs(request, isds_ns);
5218 /* Change URL temporarily for sending this request only */
5220 char *new_url = NULL;
5221 if ((err = _isds_build_url_from_context(context,
5222 "%1$.*2$sasws/changePassword", &new_url))) {
5223 goto leave;
5225 saved_url = context->url;
5226 context->url = new_url;
5229 /* Store credentials for sending this request only */
5230 context->otp_credentials = otp;
5231 _isds_discard_credentials(context, 0);
5232 if ((err = _isds_store_credentials(context, context->saved_username,
5233 password, NULL))) {
5234 _isds_discard_credentials(context, 0);
5235 goto leave;
5237 #if HAVE_CURL_REAUTHORIZATION_BUG
5238 saved_curl = context->curl;
5239 context->curl = curl_easy_init();
5240 if (NULL == context->curl) {
5241 err = IE_ERROR;
5242 goto leave;
5244 if (context->timeout) {
5245 err = isds_set_timeout(context, context->timeout);
5246 if (err) goto leave;
5248 #endif
5250 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5252 /* Sent request */
5253 err = isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5255 /* Remove temporal credentials */
5256 _isds_discard_credentials(context, 0);
5257 /* Detach pointer to OTP credentials from context */
5258 context->otp_credentials = NULL;
5259 /* Keep context->otp true to keep signaling this is OTP session */
5261 /* Destroy request */
5262 xmlFreeNode(request); request = NULL;
5264 if (err) {
5265 isds_log(ILF_ISDS, ILL_DEBUG,
5266 _("Processing ISDS response on SendSMSCode request failed\n"));
5267 goto leave;
5270 /* Check for response status */
5271 err = isds_response_status(context, SERVICE_ASWS, response,
5272 &code, &message, (xmlChar **)refnumber);
5273 if (err) {
5274 isds_log(ILF_ISDS, ILL_DEBUG,
5275 _("ISDS response on SendSMSCode request is missing "
5276 "status\n"));
5277 goto leave;
5280 /* Check for error */
5281 if (xmlStrcmp(code, BAD_CAST "0000")) {
5282 char *code_locale = _isds_utf82locale((char*)code);
5283 char *message_locale = _isds_utf82locale((char*)message);
5284 int i;
5285 isds_log(ILF_ISDS, ILL_DEBUG,
5286 _("Server refused to send new code on SendSMSCode "
5287 "request (code=%s, message=%s)\n"),
5288 code_locale, message_locale);
5290 /* Check for known error codes */
5291 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5292 if (!xmlStrcmp(code, codes[i])) break;
5294 if (i < sizeof(codes)/sizeof(*codes)) {
5295 isds_log_message(context, _(meanings[i]));
5296 /* Mimic otp->resolution according to the code, specification does
5297 * prescribe OTP header to be available. */
5298 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5299 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5300 otp->resolution = resolutions[i];
5301 } else
5302 isds_log_message(context, message_locale);
5304 free(code_locale);
5305 free(message_locale);
5307 err = IE_ISDS;
5308 goto leave;
5311 /* Otherwise new code sent successfully */
5312 /* Mimic otp->resolution according to the code, specification does
5313 * prescribe OTP header to be available. */
5314 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5315 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5317 leave:
5318 if (NULL != saved_url) {
5319 /* Revert URL to original one */
5320 zfree(context->url);
5321 context->url = saved_url;
5323 #if HAVE_CURL_REAUTHORIZATION_BUG
5324 if (NULL != saved_curl) {
5325 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5326 context->curl = saved_curl;
5328 #endif
5330 free(code);
5331 free(message);
5332 xmlFreeDoc(response);
5333 xmlFreeNode(request);
5335 if (!err)
5336 isds_log(ILF_ISDS, ILL_DEBUG,
5337 _("New OTP code has been sent successfully on SendSMSCode "
5338 "request.\n"));
5339 return err;
5343 /* Convert response status code to isds_error code and set long message
5344 * @context is context to save long message to
5345 * @map is mapping from codes to errors and messages. Pass NULL for generic
5346 * handling.
5347 * @code is status code to translate
5348 * @message is non-localized status message to put into long message in case
5349 * of uknown error. It can be NULL if server did not provide any.
5350 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5351 * invalid invocation. */
5352 static isds_error statuscode2isds_error(struct isds_ctx *context,
5353 const struct code_map_isds_error *map,
5354 const xmlChar *code, const xmlChar *message) {
5355 if (NULL == code) {
5356 isds_log_message(context,
5357 _("NULL status code passed to statuscode2isds_error()"));
5358 return IE_INVAL;
5361 if (NULL != map) {
5362 /* Check for known error codes */
5363 for (int i=0; map->codes[i] != NULL; i++) {
5364 if (!xmlStrcmp(code, map->codes[i])) {
5365 isds_log_message(context, _(map->meanings[i]));
5366 return map->errors[i];
5371 /* Other error */
5372 if (xmlStrcmp(code, BAD_CAST "0000")) {
5373 char *message_locale = _isds_utf82locale((char*)message);
5374 if (NULL == message_locale)
5375 isds_log_message(context, _("ISDS server returned unknown error"));
5376 else
5377 isds_log_message(context, message_locale);
5378 free(message_locale);
5379 return IE_ISDS;
5382 return IE_SUCCESS;
5384 #endif
5387 /* Change user password in ISDS.
5388 * User must supply old password, new password will takes effect after some
5389 * time, current session can continue. Password must fulfill some constraints.
5390 * @context is session context
5391 * @old_password is current password.
5392 * @new_password is requested new password
5393 * @otp auxiliary data required if one-time password authentication is in use,
5394 * defines OTP code (if known) and returns fine grade resolution of OTP
5395 * procedure. Pass NULL, if one-time password authentication is not needed.
5396 * Please note the @otp argument must match OTP method used at log-in time. See
5397 * isds_login() function for more details.
5398 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5399 * NULL, if you don't care.
5400 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5401 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5402 * awaiting OTP code that has been delivered by side channel to the user. */
5403 isds_error isds_change_password(struct isds_ctx *context,
5404 const char *old_password, const char *new_password,
5405 struct isds_otp *otp, char **refnumber) {
5406 isds_error err = IE_SUCCESS;
5407 #if HAVE_LIBCURL
5408 char *saved_url = NULL; /* No copy */
5409 #if HAVE_CURL_REAUTHORIZATION_BUG
5410 CURL *saved_curl = NULL; /* No copy */
5411 #endif
5412 xmlNsPtr isds_ns = NULL;
5413 xmlNodePtr request = NULL, node;
5414 xmlDocPtr response = NULL;
5415 xmlChar *code = NULL, *message = NULL;
5416 const xmlChar *codes[] = {
5417 BAD_CAST "1066",
5418 BAD_CAST "1067",
5419 BAD_CAST "1079",
5420 BAD_CAST "1080",
5421 BAD_CAST "1081",
5422 BAD_CAST "1082",
5423 BAD_CAST "1083",
5424 BAD_CAST "1090",
5425 BAD_CAST "1091",
5426 BAD_CAST "2300",
5427 BAD_CAST "9204"
5429 const char *meanings[] = {
5430 N_("Password length must be between 8 and 32 characters"),
5431 N_("Password cannot be reused"), /* Server does not distinguish 1067
5432 and 1091 on ChangePasswordOTP */
5433 N_("Password contains forbidden character"),
5434 N_("Password must contain at least one upper-case letter, "
5435 "one lower-case, and one digit"),
5436 N_("Password cannot contain sequence of three identical characters"),
5437 N_("Password cannot contain user identifier"),
5438 N_("Password is too simmple"),
5439 N_("Old password is not valid"),
5440 N_("Password cannot be reused"),
5441 N_("Unexpected error"),
5442 N_("LDAP update error")
5444 #endif
5446 if (!context) return IE_INVALID_CONTEXT;
5447 zfree(context->long_message);
5448 if (NULL != refnumber)
5449 zfree(*refnumber);
5450 if (NULL == old_password) {
5451 isds_log_message(context,
5452 _("Second argument (old password) of isds_change_password() "
5453 "is NULL"));
5454 return IE_INVAL;
5456 if (NULL == otp && NULL == new_password) {
5457 isds_log_message(context,
5458 _("Third argument (new password) of isds_change_password() "
5459 "is NULL"));
5460 return IE_INVAL;
5463 #if HAVE_LIBCURL
5464 /* Check if connection is established
5465 * TODO: This check should be done downstairs. */
5466 if (!context->curl) return IE_CONNECTION_CLOSED;
5468 if (context->otp && NULL == otp) {
5469 isds_log_message(context, _("If one-time password authentication "
5470 "method is in use, changing password requires one-time "
5471 "credentials either"));
5472 return IE_INVAL;
5475 /* Build ChangeISDSPassword request */
5476 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5477 BAD_CAST "ChangePasswordOTP");
5478 if (!request) {
5479 isds_log_message(context, (NULL == otp) ?
5480 _("Could not build ChangeISDSPassword request") :
5481 _("Could not build ChangePasswordOTP request"));
5482 return IE_ERROR;
5484 isds_ns = xmlNewNs(request,
5485 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5486 NULL);
5487 if(!isds_ns) {
5488 isds_log_message(context, _("Could not create ISDS name space"));
5489 xmlFreeNode(request);
5490 return IE_ERROR;
5492 xmlSetNs(request, isds_ns);
5494 INSERT_STRING(request, "dbOldPassword", old_password);
5495 INSERT_STRING(request, "dbNewPassword", new_password);
5497 if (NULL != otp) {
5498 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5499 switch (otp->method) {
5500 case OTP_HMAC:
5501 isds_log(ILF_SEC, ILL_INFO,
5502 _("Selected authentication method: "
5503 "HMAC-based one-time password\n"));
5504 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5505 break;
5506 case OTP_TIME:
5507 isds_log(ILF_SEC, ILL_INFO,
5508 _("Selected authentication method: "
5509 "Time-based one-time password\n"));
5510 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5511 if (otp->otp_code == NULL) {
5512 isds_log(ILF_SEC, ILL_INFO,
5513 _("OTP code has not been provided by "
5514 "application, requesting server for "
5515 "new one.\n"));
5516 err = _isds_request_totp_code(context, old_password, otp,
5517 refnumber);
5518 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5519 goto leave;
5521 } else {
5522 isds_log(ILF_SEC, ILL_INFO,
5523 _("OTP code has been provided by "
5524 "application, not requesting server "
5525 "for new one.\n"));
5527 break;
5528 default:
5529 isds_log_message(context,
5530 _("Unknown one-time password authentication "
5531 "method requested by application"));
5532 err = IE_ENUM;
5533 goto leave;
5536 /* Change URL temporarily for sending this request only */
5538 char *new_url = NULL;
5539 if ((err = _isds_build_url_from_context(context,
5540 "%1$.*2$sasws/changePassword", &new_url))) {
5541 goto leave;
5543 saved_url = context->url;
5544 context->url = new_url;
5547 /* Store credentials for sending this request only */
5548 context->otp_credentials = otp;
5549 _isds_discard_credentials(context, 0);
5550 if ((err = _isds_store_credentials(context, context->saved_username,
5551 old_password, NULL))) {
5552 _isds_discard_credentials(context, 0);
5553 goto leave;
5555 #if HAVE_CURL_REAUTHORIZATION_BUG
5556 saved_curl = context->curl;
5557 context->curl = curl_easy_init();
5558 if (NULL == context->curl) {
5559 err = IE_ERROR;
5560 goto leave;
5562 if (context->timeout) {
5563 err = isds_set_timeout(context, context->timeout);
5564 if (err) goto leave;
5566 #endif
5569 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5570 _("Sending ChangeISDSPassword request to ISDS\n") :
5571 _("Sending ChangePasswordOTP request to ISDS\n"));
5573 /* Sent request */
5574 err = isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5575 request, &response, NULL, NULL);
5577 if (otp) {
5578 /* Remove temporal credentials */
5579 _isds_discard_credentials(context, 0);
5580 /* Detach pointer to OTP credentials from context */
5581 context->otp_credentials = NULL;
5582 /* Keep context->otp true to keep signaling this is OTP session */
5585 /* Destroy request */
5586 xmlFreeNode(request); request = NULL;
5588 if (err) {
5589 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5590 _("Processing ISDS response on ChangeISDSPassword "
5591 "request failed\n") :
5592 _("Processing ISDS response on ChangePasswordOTP "
5593 "request failed\n"));
5594 goto leave;
5597 /* Check for response status */
5598 err = isds_response_status(context,
5599 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5600 &code, &message, (xmlChar **)refnumber);
5601 if (err) {
5602 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5603 _("ISDS response on ChangeISDSPassword request is missing "
5604 "status\n") :
5605 _("ISDS response on ChangePasswordOTP request is missing "
5606 "status\n"));
5607 goto leave;
5610 /* Check for known error codes */
5611 for (int i=0; i < sizeof(codes)/sizeof(*codes); i++) {
5612 if (!xmlStrcmp(code, codes[i])) {
5613 char *code_locale = _isds_utf82locale((char*)code);
5614 char *message_locale = _isds_utf82locale((char*)message);
5615 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5616 _("Server refused to change password on ChangeISDSPassword "
5617 "request (code=%s, message=%s)\n") :
5618 _("Server refused to change password on ChangePasswordOTP "
5619 "request (code=%s, message=%s)\n"),
5620 code_locale, message_locale);
5621 free(code_locale);
5622 free(message_locale);
5623 isds_log_message(context, _(meanings[i]));
5624 err = IE_INVAL;
5625 goto leave;
5629 /* Other error */
5630 if (xmlStrcmp(code, BAD_CAST "0000")) {
5631 char *code_locale = _isds_utf82locale((char*)code);
5632 char *message_locale = _isds_utf82locale((char*)message);
5633 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5634 _("Server refused to change password on ChangeISDSPassword "
5635 "request (code=%s, message=%s)\n") :
5636 _("Server refused to change password on ChangePasswordOTP "
5637 "request (code=%s, message=%s)\n"),
5638 code_locale, message_locale);
5639 isds_log_message(context, message_locale);
5640 free(code_locale);
5641 free(message_locale);
5642 err = IE_ISDS;
5643 goto leave;
5646 /* Otherwise password changed successfully */
5648 leave:
5649 if (NULL != saved_url) {
5650 /* Revert URL to original one */
5651 zfree(context->url);
5652 context->url = saved_url;
5654 #if HAVE_CURL_REAUTHORIZATION_BUG
5655 if (NULL != saved_curl) {
5656 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5657 context->curl = saved_curl;
5659 #endif
5661 free(code);
5662 free(message);
5663 xmlFreeDoc(response);
5664 xmlFreeNode(request);
5666 if (!err)
5667 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5668 _("Password changed successfully on ChangeISDSPassword "
5669 "request.\n") :
5670 _("Password changed successfully on ChangePasswordOTP "
5671 "request.\n"));
5672 #else /* not HAVE_LIBCURL */
5673 err = IE_NOTSUP;
5674 #endif
5676 return err;
5680 #if HAVE_LIBCURL
5681 /* Generic middle part with request sending and response check.
5682 * It sends prepared request and checks for error code.
5683 * @context is ISDS session context.
5684 * @service is ISDS service handler
5685 * @service_name is name in scope of given @service
5686 * @request is XML tree with request. Will be freed to save memory.
5687 * @response is XML document outputting ISDS response.
5688 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5689 * @map is mapping from status code to library error. Pass NULL if no special
5690 * handling is requested.
5691 * NULL, if you don't care. */
5692 static isds_error send_destroy_request_check_response(
5693 struct isds_ctx *context,
5694 const isds_service service, const xmlChar *service_name,
5695 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5696 const struct code_map_isds_error *map) {
5697 isds_error err = IE_SUCCESS;
5698 char *service_name_locale = NULL;
5699 xmlChar *code = NULL, *message = NULL;
5702 if (!context) return IE_INVALID_CONTEXT;
5703 if (!service_name || *service_name == '\0' || !request || !*request ||
5704 !response)
5705 return IE_INVAL;
5707 /* Check if connection is established
5708 * TODO: This check should be done downstairs. */
5709 if (!context->curl) return IE_CONNECTION_CLOSED;
5711 service_name_locale = _isds_utf82locale((char*) service_name);
5712 if (!service_name_locale) {
5713 err = IE_NOMEM;
5714 goto leave;
5717 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5718 service_name_locale);
5720 /* Send request */
5721 err = isds(context, service, *request, response, NULL, NULL);
5722 xmlFreeNode(*request); *request = NULL;
5724 if (err) {
5725 isds_log(ILF_ISDS, ILL_DEBUG,
5726 _("Processing ISDS response on %s request failed\n"),
5727 service_name_locale);
5728 goto leave;
5731 /* Check for response status */
5732 err = isds_response_status(context, service, *response,
5733 &code, &message, refnumber);
5734 if (err) {
5735 isds_log(ILF_ISDS, ILL_DEBUG,
5736 _("ISDS response on %s request is missing status\n"),
5737 service_name_locale);
5738 goto leave;
5741 err = statuscode2isds_error(context, map, code, message);
5743 /* Request processed, but server failed */
5744 if (xmlStrcmp(code, BAD_CAST "0000")) {
5745 char *code_locale = _isds_utf82locale((char*) code);
5746 char *message_locale = _isds_utf82locale((char*) message);
5747 isds_log(ILF_ISDS, ILL_DEBUG,
5748 _("Server refused %s request (code=%s, message=%s)\n"),
5749 service_name_locale, code_locale, message_locale);
5750 free(code_locale);
5751 free(message_locale);
5752 goto leave;
5756 leave:
5757 free(code);
5758 free(message);
5759 if (err && *response) {
5760 xmlFreeDoc(*response);
5761 *response = NULL;
5763 if (*request) {
5764 xmlFreeNode(*request);
5765 *request = NULL;
5767 free(service_name_locale);
5769 return err;
5773 /* Generic bottom half with request sending.
5774 * It sends prepared request, checks for error code, destroys response and
5775 * request and log success or failure.
5776 * @context is ISDS session context.
5777 * @service is ISDS service handler
5778 * @service_name is name in scope of given @service
5779 * @request is XML tree with request. Will be freed to save memory.
5780 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5781 * NULL, if you don't care. */
5782 static isds_error send_request_check_drop_response(
5783 struct isds_ctx *context,
5784 const isds_service service, const xmlChar *service_name,
5785 xmlNodePtr *request, xmlChar **refnumber) {
5786 isds_error err = IE_SUCCESS;
5787 xmlDocPtr response = NULL;
5790 if (!context) return IE_INVALID_CONTEXT;
5791 if (!service_name || *service_name == '\0' || !request || !*request)
5792 return IE_INVAL;
5794 /* Send request and check response*/
5795 err = send_destroy_request_check_response(context,
5796 service, service_name, request, &response, refnumber, NULL);
5798 xmlFreeDoc(response);
5800 if (*request) {
5801 xmlFreeNode(*request);
5802 *request = NULL;
5805 if (!err) {
5806 char *service_name_locale = _isds_utf82locale((char *) service_name);
5807 isds_log(ILF_ISDS, ILL_DEBUG,
5808 _("%s request processed by server successfully.\n"),
5809 service_name_locale);
5810 free(service_name_locale);
5813 return err;
5817 /* Insert isds_credentials_delivery structure into XML request if not NULL
5818 * @context is session context
5819 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5820 * credentials delivery. The email field is passed.
5821 * @parent is XML element where to insert */
5822 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5823 const struct isds_credentials_delivery *credentials_delivery,
5824 xmlNodePtr parent) {
5825 isds_error err = IE_SUCCESS;
5826 xmlNodePtr node;
5828 if (!context) return IE_INVALID_CONTEXT;
5829 if (!parent) return IE_INVAL;
5831 if (credentials_delivery) {
5832 /* Following elements are valid only for services:
5833 * NewAccessData, AddDataBoxUser, CreateDataBox */
5834 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5835 INSERT_STRING(parent, "email", credentials_delivery->email);
5838 leave:
5839 return err;
5843 /* Extract credentials delivery from ISDS response.
5844 * @context is session context
5845 * @credentials_delivery is pointer to valid structure to fill in returned
5846 * user's password (and new log-in name). If NULL, do not extract the data.
5847 * @response is pointer to XML document with ISDS response
5848 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5849 * @return IE_SUCCESS even if new user name has not been found because it's not
5850 * clear whether it's returned always. */
5851 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5852 struct isds_credentials_delivery *credentials_delivery,
5853 xmlDocPtr response, const char *request_name) {
5854 isds_error err = IE_SUCCESS;
5855 xmlXPathContextPtr xpath_ctx = NULL;
5856 xmlXPathObjectPtr result = NULL;
5857 char *xpath_query = NULL;
5859 if (!context) return IE_INVALID_CONTEXT;
5860 if (credentials_delivery) {
5861 zfree(credentials_delivery->token);
5862 zfree(credentials_delivery->new_user_name);
5864 if (!response || !request_name || !*request_name) return IE_INVAL;
5867 /* Extract optional token */
5868 if (credentials_delivery) {
5869 xpath_ctx = xmlXPathNewContext(response);
5870 if (!xpath_ctx) {
5871 err = IE_ERROR;
5872 goto leave;
5874 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5875 err = IE_ERROR;
5876 goto leave;
5879 /* Verify root element */
5880 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
5881 request_name)) {
5882 err = IE_NOMEM;
5883 goto leave;
5885 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
5886 if (!result) {
5887 err = IE_ERROR;
5888 goto leave;
5890 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5891 char *request_name_locale = _isds_utf82locale(request_name);
5892 isds_log(ILF_ISDS, ILL_WARNING,
5893 _("Wrong element in ISDS response for %s request "
5894 "while extracting credentials delivery details\n"),
5895 request_name_locale);
5896 free(request_name_locale);
5897 err = IE_ERROR;
5898 goto leave;
5900 xpath_ctx->node = result->nodesetval->nodeTab[0];
5903 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5904 * optional. */
5905 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
5907 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
5908 if (!credentials_delivery->token) {
5909 char *request_name_locale = _isds_utf82locale(request_name);
5910 isds_log(ILF_ISDS, ILL_ERR,
5911 _("ISDS did not return token on %s request "
5912 "even if requested\n"), request_name_locale);
5913 free(request_name_locale);
5914 err = IE_ERROR;
5918 leave:
5919 free(xpath_query);
5920 xmlXPathFreeObject(result);
5921 xmlXPathFreeContext(xpath_ctx);
5923 return err;
5927 /* Build XSD:tCreateDBInput request type for box creating.
5928 * @context is session context
5929 * @request outputs built XML tree
5930 * @service_name is request name of SERVICE_DB_MANIPULATION service
5931 * @box is box description to create including single primary user (in case of
5932 * FO box type)
5933 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5934 * box, or contact address of PFO box owner)
5935 * @former_names is optional former name of box owner. Pass NULL if otherwise.
5936 * @upper_box_id is optional ID of supper box if currently created box is
5937 * subordinated.
5938 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
5939 * don't care.
5940 * @credentials_delivery is valid pointer if ISDS should return token that box
5941 * owner can use to obtain his new credentials in on-line way. Then valid email
5942 * member value should be supplied.
5943 * @approval is optional external approval of box manipulation */
5944 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
5945 xmlNodePtr *request, const xmlChar *service_name,
5946 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
5947 const xmlChar *former_names, const xmlChar *upper_box_id,
5948 const xmlChar *ceo_label,
5949 const struct isds_credentials_delivery *credentials_delivery,
5950 const struct isds_approval *approval) {
5951 isds_error err = IE_SUCCESS;
5952 xmlNsPtr isds_ns = NULL;
5953 xmlNodePtr node, dbPrimaryUsers;
5954 xmlChar *string = NULL;
5955 const struct isds_list *item;
5958 if (!context) return IE_INVALID_CONTEXT;
5959 if (!request || !service_name || service_name[0] == '\0' || !box)
5960 return IE_INVAL;
5963 /* Build CreateDataBox-similar request */
5964 *request = xmlNewNode(NULL, service_name);
5965 if (!*request) {
5966 char *service_name_locale = _isds_utf82locale((char*) service_name);
5967 isds_printf_message(context, _("Could build %s request"),
5968 service_name_locale);
5969 free(service_name_locale);
5970 return IE_ERROR;
5972 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
5973 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
5974 if (!isds_ns) {
5975 isds_log_message(context, _("Could not create ISDS1 name space"));
5976 xmlFreeNode(*request);
5977 return IE_ERROR;
5979 } else {
5980 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
5981 if (!isds_ns) {
5982 isds_log_message(context, _("Could not create ISDS name space"));
5983 xmlFreeNode(*request);
5984 return IE_ERROR;
5987 xmlSetNs(*request, isds_ns);
5989 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
5990 err = insert_DbOwnerInfo(context, box, node);
5991 if (err) goto leave;
5993 /* Insert users */
5994 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
5995 * verbose documentation allows none dbUserInfo */
5996 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
5997 for (item = users; item; item = item->next) {
5998 if (item->data) {
5999 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6000 err = insert_DbUserInfo(context,
6001 (struct isds_DbUserInfo *) item->data, node);
6002 if (err) goto leave;
6006 INSERT_STRING(*request, "dbFormerNames", former_names);
6007 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6008 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6010 err = insert_credentials_delivery(context, credentials_delivery, *request);
6011 if (err) goto leave;
6013 err = insert_GExtApproval(context, approval, *request);
6014 if (err) goto leave;
6016 leave:
6017 if (err) {
6018 xmlFreeNode(*request);
6019 *request = NULL;
6021 free(string);
6022 return err;
6024 #endif /* HAVE_LIBCURL */
6027 /* Create new box.
6028 * @context is session context
6029 * @box is box description to create including single primary user (in case of
6030 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6031 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6032 * box, or contact address of PFO box owner)
6033 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6034 * @upper_box_id is optional ID of supper box if currently created box is
6035 * subordinated.
6036 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6037 * @credentials_delivery is NULL if new password should be delivered off-line
6038 * to box owner. It is valid pointer if owner should obtain new password on-line
6039 * on dedicated web server. Then input @credentials_delivery.email value is
6040 * his e-mail address he must provide to dedicated web server together
6041 * with output reallocated @credentials_delivery.token member. Output
6042 * member @credentials_delivery.new_user_name is unused up on this call.
6043 * @approval is optional external approval of box manipulation
6044 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6045 * NULL, if you don't care.*/
6046 isds_error isds_add_box(struct isds_ctx *context,
6047 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6048 const char *former_names, const char *upper_box_id,
6049 const char *ceo_label,
6050 struct isds_credentials_delivery *credentials_delivery,
6051 const struct isds_approval *approval, char **refnumber) {
6052 isds_error err = IE_SUCCESS;
6053 #if HAVE_LIBCURL
6054 xmlNodePtr request = NULL;
6055 xmlDocPtr response = NULL;
6056 xmlXPathContextPtr xpath_ctx = NULL;
6057 xmlXPathObjectPtr result = NULL;
6058 #endif
6061 if (!context) return IE_INVALID_CONTEXT;
6062 zfree(context->long_message);
6063 if (credentials_delivery) {
6064 zfree(credentials_delivery->token);
6065 zfree(credentials_delivery->new_user_name);
6067 if (!box) return IE_INVAL;
6069 #if HAVE_LIBCURL
6070 /* Scratch box ID */
6071 zfree(box->dbID);
6073 /* Build CreateDataBox request */
6074 err = build_CreateDBInput_request(context,
6075 &request, BAD_CAST "CreateDataBox",
6076 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6077 (xmlChar *) ceo_label, credentials_delivery, approval);
6078 if (err) goto leave;
6080 /* Send it to server and process response */
6081 err = send_destroy_request_check_response(context,
6082 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6083 &response, (xmlChar **) refnumber, NULL);
6085 /* Extract box ID */
6086 xpath_ctx = xmlXPathNewContext(response);
6087 if (!xpath_ctx) {
6088 err = IE_ERROR;
6089 goto leave;
6091 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6092 err = IE_ERROR;
6093 goto leave;
6095 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6097 /* Extract optional token */
6098 err = extract_credentials_delivery(context, credentials_delivery, response,
6099 "CreateDataBox");
6101 leave:
6102 xmlXPathFreeObject(result);
6103 xmlXPathFreeContext(xpath_ctx);
6104 xmlFreeDoc(response);
6105 xmlFreeNode(request);
6107 if (!err) {
6108 isds_log(ILF_ISDS, ILL_DEBUG,
6109 _("CreateDataBox request processed by server successfully.\n"));
6111 #else /* not HAVE_LIBCURL */
6112 err = IE_NOTSUP;
6113 #endif
6115 return err;
6119 /* Notify ISDS about new PFO entity.
6120 * This function has no real effect.
6121 * @context is session context
6122 * @box is PFO description including single primary user.
6123 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6124 * @former_names is optional undocumented string. Pass NULL if you don't care.
6125 * @upper_box_id is optional ID of supper box if currently created box is
6126 * subordinated.
6127 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6128 * @approval is optional external approval of box manipulation
6129 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6130 * NULL, if you don't care.*/
6131 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6132 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6133 const char *former_names, const char *upper_box_id,
6134 const char *ceo_label, const struct isds_approval *approval,
6135 char **refnumber) {
6136 isds_error err = IE_SUCCESS;
6137 #if HAVE_LIBCURL
6138 xmlNodePtr request = NULL;
6139 #endif
6141 if (!context) return IE_INVALID_CONTEXT;
6142 zfree(context->long_message);
6143 if (!box) return IE_INVAL;
6145 #if HAVE_LIBCURL
6146 /* Build CreateDataBoxPFOInfo request */
6147 err = build_CreateDBInput_request(context,
6148 &request, BAD_CAST "CreateDataBoxPFOInfo",
6149 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6150 (xmlChar *) ceo_label, NULL, approval);
6151 if (err) goto leave;
6153 /* Send it to server and process response */
6154 err = send_request_check_drop_response(context,
6155 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6156 (xmlChar **) refnumber);
6157 /* XXX: XML Schema names output dbID element but textual documentation
6158 * states no box identifier is returned. */
6159 leave:
6160 xmlFreeNode(request);
6161 #else /* not HAVE_LIBCURL */
6162 err = IE_NOTSUP;
6163 #endif
6164 return err;
6168 /* Common implementation for removing given box.
6169 * @context is session context
6170 * @service_name is UTF-8 encoded name fo ISDS service
6171 * @box is box description to delete
6172 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6173 * carry sane value. If NULL, do not inject this information into request.
6174 * @approval is optional external approval of box manipulation
6175 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6176 * NULL, if you don't care.*/
6177 isds_error _isds_delete_box_common(struct isds_ctx *context,
6178 const xmlChar *service_name,
6179 const struct isds_DbOwnerInfo *box, const struct tm *since,
6180 const struct isds_approval *approval, char **refnumber) {
6181 isds_error err = IE_SUCCESS;
6182 #if HAVE_LIBCURL
6183 xmlNsPtr isds_ns = NULL;
6184 xmlNodePtr request = NULL;
6185 xmlNodePtr node;
6186 xmlChar *string = NULL;
6187 #endif
6190 if (!context) return IE_INVALID_CONTEXT;
6191 zfree(context->long_message);
6192 if (!service_name || !*service_name || !box) return IE_INVAL;
6195 #if HAVE_LIBCURL
6196 /* Build DeleteDataBox(Promptly) request */
6197 request = xmlNewNode(NULL, service_name);
6198 if (!request) {
6199 char *service_name_locale = _isds_utf82locale((char*)service_name);
6200 isds_printf_message(context,
6201 _("Could build %s request"), service_name_locale);
6202 free(service_name_locale);
6203 return IE_ERROR;
6205 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6206 if(!isds_ns) {
6207 isds_log_message(context, _("Could not create ISDS name space"));
6208 xmlFreeNode(request);
6209 return IE_ERROR;
6211 xmlSetNs(request, isds_ns);
6213 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6214 err = insert_DbOwnerInfo(context, box, node);
6215 if (err) goto leave;
6217 if (since) {
6218 err = tm2datestring(since, &string);
6219 if (err) {
6220 isds_log_message(context,
6221 _("Could not convert `since' argument to ISO date string"));
6222 goto leave;
6224 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6225 zfree(string);
6228 err = insert_GExtApproval(context, approval, request);
6229 if (err) goto leave;
6232 /* Send it to server and process response */
6233 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6234 service_name, &request, (xmlChar **) refnumber);
6236 leave:
6237 xmlFreeNode(request);
6238 free(string);
6239 #else /* not HAVE_LIBCURL */
6240 err = IE_NOTSUP;
6241 #endif
6242 return err;
6246 /* Remove given box permanently.
6247 * @context is session context
6248 * @box is box description to delete
6249 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6250 * carry sane value.
6251 * @approval is optional external approval of box manipulation
6252 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6253 * NULL, if you don't care.*/
6254 isds_error isds_delete_box(struct isds_ctx *context,
6255 const struct isds_DbOwnerInfo *box, const struct tm *since,
6256 const struct isds_approval *approval, char **refnumber) {
6257 if (!context) return IE_INVALID_CONTEXT;
6258 zfree(context->long_message);
6259 if (!box || !since) return IE_INVAL;
6261 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6262 box, since, approval, refnumber);
6266 /* Undocumented function.
6267 * @context is session context
6268 * @box is box description to delete
6269 * @approval is optional external approval of box manipulation
6270 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6271 * NULL, if you don't care.*/
6272 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6273 const struct isds_DbOwnerInfo *box,
6274 const struct isds_approval *approval, char **refnumber) {
6275 if (!context) return IE_INVALID_CONTEXT;
6276 zfree(context->long_message);
6277 if (!box) return IE_INVAL;
6279 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6280 box, NULL, approval, refnumber);
6284 /* Update data about given box.
6285 * @context is session context
6286 * @old_box current box description
6287 * @new_box are updated data about @old_box
6288 * @approval is optional external approval of box manipulation
6289 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6290 * NULL, if you don't care.*/
6291 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6292 const struct isds_DbOwnerInfo *old_box,
6293 const struct isds_DbOwnerInfo *new_box,
6294 const struct isds_approval *approval, char **refnumber) {
6295 isds_error err = IE_SUCCESS;
6296 #if HAVE_LIBCURL
6297 xmlNsPtr isds_ns = NULL;
6298 xmlNodePtr request = NULL;
6299 xmlNodePtr node;
6300 #endif
6303 if (!context) return IE_INVALID_CONTEXT;
6304 zfree(context->long_message);
6305 if (!old_box || !new_box) return IE_INVAL;
6308 #if HAVE_LIBCURL
6309 /* Build UpdateDataBoxDescr request */
6310 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6311 if (!request) {
6312 isds_log_message(context,
6313 _("Could build UpdateDataBoxDescr request"));
6314 return IE_ERROR;
6316 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6317 if(!isds_ns) {
6318 isds_log_message(context, _("Could not create ISDS name space"));
6319 xmlFreeNode(request);
6320 return IE_ERROR;
6322 xmlSetNs(request, isds_ns);
6324 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6325 err = insert_DbOwnerInfo(context, old_box, node);
6326 if (err) goto leave;
6328 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6329 err = insert_DbOwnerInfo(context, new_box, node);
6330 if (err) goto leave;
6332 err = insert_GExtApproval(context, approval, request);
6333 if (err) goto leave;
6336 /* Send it to server and process response */
6337 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6338 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6340 leave:
6341 xmlFreeNode(request);
6342 #else /* not HAVE_LIBCURL */
6343 err = IE_NOTSUP;
6344 #endif
6346 return err;
6350 #if HAVE_LIBCURL
6351 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6352 * code
6353 * @context is session context
6354 * @service is SOAP service
6355 * @service_name is name of request in @service
6356 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6357 * @box_id is box ID of interest
6358 * @approval is optional external approval of box manipulation
6359 * @response is server SOAP body response as XML document
6360 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6361 * NULL, if you don't care.
6362 * @return error coded from lower layer, context message will be set up
6363 * appropriately. */
6364 static isds_error build_send_dbid_request_check_response(
6365 struct isds_ctx *context, const isds_service service,
6366 const xmlChar *service_name, const xmlChar *box_id_element,
6367 const xmlChar *box_id, const struct isds_approval *approval,
6368 xmlDocPtr *response, xmlChar **refnumber) {
6370 isds_error err = IE_SUCCESS;
6371 char *service_name_locale = NULL, *box_id_locale = NULL;
6372 xmlNodePtr request = NULL, node;
6373 xmlNsPtr isds_ns = NULL;
6375 if (!context) return IE_INVALID_CONTEXT;
6376 if (!service_name || !box_id) return IE_INVAL;
6377 if (!response) return IE_INVAL;
6379 /* Free output argument */
6380 xmlFreeDoc(*response); *response = NULL;
6382 /* Prepare strings */
6383 service_name_locale = _isds_utf82locale((char*)service_name);
6384 if (!service_name_locale) {
6385 err = IE_NOMEM;
6386 goto leave;
6388 box_id_locale = _isds_utf82locale((char*)box_id);
6389 if (!box_id_locale) {
6390 err = IE_NOMEM;
6391 goto leave;
6394 /* Build request */
6395 request = xmlNewNode(NULL, service_name);
6396 if (!request) {
6397 isds_printf_message(context,
6398 _("Could not build %s request for %s box"), service_name_locale,
6399 box_id_locale);
6400 err = IE_ERROR;
6401 goto leave;
6403 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6404 if(!isds_ns) {
6405 isds_log_message(context, _("Could not create ISDS name space"));
6406 err = IE_ERROR;
6407 goto leave;
6409 xmlSetNs(request, isds_ns);
6411 /* Add XSD:tIdDbInput children */
6412 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6413 INSERT_STRING(request, box_id_element, box_id);
6414 err = insert_GExtApproval(context, approval, request);
6415 if (err) goto leave;
6417 /* Send request and check response*/
6418 err = send_destroy_request_check_response(context,
6419 service, service_name, &request, response, refnumber, NULL);
6421 leave:
6422 free(service_name_locale);
6423 free(box_id_locale);
6424 xmlFreeNode(request);
6425 return err;
6427 #endif /* HAVE_LIBCURL */
6430 /* Get data about all users assigned to given box.
6431 * @context is session context
6432 * @box_id is box ID
6433 * @users is automatically reallocated list of struct isds_DbUserInfo */
6434 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6435 struct isds_list **users) {
6436 isds_error err = IE_SUCCESS;
6437 #if HAVE_LIBCURL
6438 xmlDocPtr response = NULL;
6439 xmlXPathContextPtr xpath_ctx = NULL;
6440 xmlXPathObjectPtr result = NULL;
6441 int i;
6442 struct isds_list *item, *prev_item = NULL;
6443 #endif
6445 if (!context) return IE_INVALID_CONTEXT;
6446 zfree(context->long_message);
6447 if (!users || !box_id) return IE_INVAL;
6448 isds_list_free(users);
6451 #if HAVE_LIBCURL
6452 /* Do request and check for success */
6453 err = build_send_dbid_request_check_response(context,
6454 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6455 BAD_CAST box_id, NULL, &response, NULL);
6456 if (err) goto leave;
6459 /* Extract data */
6460 /* Prepare structure */
6461 xpath_ctx = xmlXPathNewContext(response);
6462 if (!xpath_ctx) {
6463 err = IE_ERROR;
6464 goto leave;
6466 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6467 err = IE_ERROR;
6468 goto leave;
6471 /* Set context node */
6472 result = xmlXPathEvalExpression(BAD_CAST
6473 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6474 xpath_ctx);
6475 if (!result) {
6476 err = IE_ERROR;
6477 goto leave;
6479 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6480 /* Iterate over all users */
6481 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6483 /* Prepare structure */
6484 item = calloc(1, sizeof(*item));
6485 if (!item) {
6486 err = IE_NOMEM;
6487 goto leave;
6489 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6490 if (i == 0) *users = item;
6491 else prev_item->next = item;
6492 prev_item = item;
6494 /* Extract it */
6495 xpath_ctx->node = result->nodesetval->nodeTab[i];
6496 err = extract_DbUserInfo(context,
6497 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6498 if (err) goto leave;
6502 leave:
6503 if (err) {
6504 isds_list_free(users);
6507 xmlXPathFreeObject(result);
6508 xmlXPathFreeContext(xpath_ctx);
6509 xmlFreeDoc(response);
6511 if (!err)
6512 isds_log(ILF_ISDS, ILL_DEBUG,
6513 _("GetDataBoxUsers request processed by server "
6514 "successfully.\n"));
6515 #else /* not HAVE_LIBCURL */
6516 err = IE_NOTSUP;
6517 #endif
6519 return err;
6523 /* Update data about user assigned to given box.
6524 * @context is session context
6525 * @box is box identification
6526 * @old_user identifies user to update
6527 * @new_user are updated data about @old_user
6528 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6529 * NULL, if you don't care.*/
6530 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6531 const struct isds_DbOwnerInfo *box,
6532 const struct isds_DbUserInfo *old_user,
6533 const struct isds_DbUserInfo *new_user,
6534 char **refnumber) {
6535 isds_error err = IE_SUCCESS;
6536 #if HAVE_LIBCURL
6537 xmlNsPtr isds_ns = NULL;
6538 xmlNodePtr request = NULL;
6539 xmlNodePtr node;
6540 #endif
6543 if (!context) return IE_INVALID_CONTEXT;
6544 zfree(context->long_message);
6545 if (!box || !old_user || !new_user) return IE_INVAL;
6548 #if HAVE_LIBCURL
6549 /* Build UpdateDataBoxUser request */
6550 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6551 if (!request) {
6552 isds_log_message(context,
6553 _("Could build UpdateDataBoxUser request"));
6554 return IE_ERROR;
6556 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6557 if(!isds_ns) {
6558 isds_log_message(context, _("Could not create ISDS name space"));
6559 xmlFreeNode(request);
6560 return IE_ERROR;
6562 xmlSetNs(request, isds_ns);
6564 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6565 err = insert_DbOwnerInfo(context, box, node);
6566 if (err) goto leave;
6568 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6569 err = insert_DbUserInfo(context, old_user, node);
6570 if (err) goto leave;
6572 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6573 err = insert_DbUserInfo(context, new_user, node);
6574 if (err) goto leave;
6576 /* Send it to server and process response */
6577 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6578 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6580 leave:
6581 xmlFreeNode(request);
6582 #else /* not HAVE_LIBCURL */
6583 err = IE_NOTSUP;
6584 #endif
6586 return err;
6590 /* Undocumented function.
6591 * @context is session context
6592 * @box_id is UTF-8 encoded box identifier
6593 * @token is UTF-8 encoded temporary password
6594 * @user_id outputs UTF-8 encoded reallocated user identifier
6595 * @password outpus UTF-8 encoded reallocated user password
6596 * Output arguments will be nulled in case of error */
6597 isds_error isds_activate(struct isds_ctx *context,
6598 const char *box_id, const char *token,
6599 char **user_id, char **password) {
6600 isds_error err = IE_SUCCESS;
6601 #if HAVE_LIBCURL
6602 xmlNsPtr isds_ns = NULL;
6603 xmlNodePtr request = NULL, node;
6604 xmlDocPtr response = NULL;
6605 xmlXPathContextPtr xpath_ctx = NULL;
6606 xmlXPathObjectPtr result = NULL;
6607 #endif
6610 if (!context) return IE_INVALID_CONTEXT;
6611 zfree(context->long_message);
6613 if (user_id) zfree(*user_id);
6614 if (password) zfree(*password);
6616 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6619 #if HAVE_LIBCURL
6620 /* Build Activate request */
6621 request = xmlNewNode(NULL, BAD_CAST "Activate");
6622 if (!request) {
6623 isds_log_message(context, _("Could build Activate request"));
6624 return IE_ERROR;
6626 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6627 if(!isds_ns) {
6628 isds_log_message(context, _("Could not create ISDS name space"));
6629 xmlFreeNode(request);
6630 return IE_ERROR;
6632 xmlSetNs(request, isds_ns);
6634 INSERT_STRING(request, "dbAccessDataId", token);
6635 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6636 INSERT_STRING(request, "dbID", box_id);
6639 /* Send request and check response*/
6640 err = send_destroy_request_check_response(context,
6641 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6642 &response, NULL, NULL);
6643 if (err) goto leave;
6646 /* Extract data */
6647 xpath_ctx = xmlXPathNewContext(response);
6648 if (!xpath_ctx) {
6649 err = IE_ERROR;
6650 goto leave;
6652 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6653 err = IE_ERROR;
6654 goto leave;
6656 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6657 xpath_ctx);
6658 if (!result) {
6659 err = IE_ERROR;
6660 goto leave;
6662 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6663 isds_log_message(context, _("Missing ActivateResponse element"));
6664 err = IE_ISDS;
6665 goto leave;
6667 if (result->nodesetval->nodeNr > 1) {
6668 isds_log_message(context, _("Multiple ActivateResponse element"));
6669 err = IE_ISDS;
6670 goto leave;
6672 xpath_ctx->node = result->nodesetval->nodeTab[0];
6673 xmlXPathFreeObject(result); result = NULL;
6675 EXTRACT_STRING("isds:userId", *user_id);
6676 if (!*user_id)
6677 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6678 "but did not return `userId' element.\n"));
6680 EXTRACT_STRING("isds:password", *password);
6681 if (!*password)
6682 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6683 "but did not return `password' element.\n"));
6685 leave:
6686 xmlXPathFreeObject(result);
6687 xmlXPathFreeContext(xpath_ctx);
6688 xmlFreeDoc(response);
6689 xmlFreeNode(request);
6691 if (!err)
6692 isds_log(ILF_ISDS, ILL_DEBUG,
6693 _("Activate request processed by server successfully.\n"));
6694 #else /* not HAVE_LIBCURL */
6695 err = IE_NOTSUP;
6696 #endif
6698 return err;
6702 /* Reset credentials of user assigned to given box.
6703 * @context is session context
6704 * @box is box identification
6705 * @user identifies user to reset password
6706 * @fee_paid is true if fee has been paid, false otherwise
6707 * @approval is optional external approval of box manipulation
6708 * @credentials_delivery is NULL if new password should be delivered off-line
6709 * to the user. It is valid pointer if user should obtain new password on-line
6710 * on dedicated web server. Then input @credentials_delivery.email value is
6711 * user's e-mail address user must provide to dedicated web server together
6712 * with @credentials_delivery.token. The output reallocated token user needs
6713 * to use to authorize on the web server to view his new password. Output
6714 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6715 * ISDS changed up on this call. (No reason why server could change the name
6716 * is known now.)
6717 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6718 * NULL, if you don't care.*/
6719 isds_error isds_reset_password(struct isds_ctx *context,
6720 const struct isds_DbOwnerInfo *box,
6721 const struct isds_DbUserInfo *user,
6722 const _Bool fee_paid, const struct isds_approval *approval,
6723 struct isds_credentials_delivery *credentials_delivery,
6724 char **refnumber) {
6725 isds_error err = IE_SUCCESS;
6726 #if HAVE_LIBCURL
6727 xmlNsPtr isds_ns = NULL;
6728 xmlNodePtr request = NULL, node;
6729 xmlDocPtr response = NULL;
6730 #endif
6733 if (!context) return IE_INVALID_CONTEXT;
6734 zfree(context->long_message);
6736 if (credentials_delivery) {
6737 zfree(credentials_delivery->token);
6738 zfree(credentials_delivery->new_user_name);
6740 if (!box || !user) return IE_INVAL;
6743 #if HAVE_LIBCURL
6744 /* Build NewAccessData request */
6745 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6746 if (!request) {
6747 isds_log_message(context,
6748 _("Could build NewAccessData request"));
6749 return IE_ERROR;
6751 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6752 if(!isds_ns) {
6753 isds_log_message(context, _("Could not create ISDS name space"));
6754 xmlFreeNode(request);
6755 return IE_ERROR;
6757 xmlSetNs(request, isds_ns);
6759 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6760 err = insert_DbOwnerInfo(context, box, node);
6761 if (err) goto leave;
6763 INSERT_ELEMENT(node, request, "dbUserInfo");
6764 err = insert_DbUserInfo(context, user, node);
6765 if (err) goto leave;
6767 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6769 err = insert_credentials_delivery(context, credentials_delivery, request);
6770 if (err) goto leave;
6772 err = insert_GExtApproval(context, approval, request);
6773 if (err) goto leave;
6775 /* Send request and check response*/
6776 err = send_destroy_request_check_response(context,
6777 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6778 &response, (xmlChar **) refnumber, NULL);
6779 if (err) goto leave;
6782 /* Extract optional token */
6783 err = extract_credentials_delivery(context, credentials_delivery,
6784 response, "NewAccessData");
6786 leave:
6787 xmlFreeDoc(response);
6788 xmlFreeNode(request);
6790 if (!err)
6791 isds_log(ILF_ISDS, ILL_DEBUG,
6792 _("NewAccessData request processed by server "
6793 "successfully.\n"));
6794 #else /* not HAVE_LIBCURL */
6795 err = IE_NOTSUP;
6796 #endif
6798 return err;
6802 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6803 * code, destroy response and log success.
6804 * @context is ISDS session context.
6805 * @service_name is name of SERVICE_DB_MANIPULATION service
6806 * @box is box identification
6807 * @user identifies user to remove
6808 * @credentials_delivery is NULL if new user's password should be delivered
6809 * off-line to the user. It is valid pointer if user should obtain new
6810 * password on-line on dedicated web server. Then input
6811 * @credentials_delivery.email value is user's e-mail address user must
6812 * provide to dedicated web server together with @credentials_delivery.token.
6813 * The output reallocated token user needs to use to authorize on the web
6814 * server to view his new password. Output reallocated
6815 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6816 * assingned or changed up on this call.
6817 * @approval is optional external approval of box manipulation
6818 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6819 * NULL, if you don't care. */
6820 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6821 struct isds_ctx *context, const xmlChar *service_name,
6822 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6823 struct isds_credentials_delivery *credentials_delivery,
6824 const struct isds_approval *approval, xmlChar **refnumber) {
6825 isds_error err = IE_SUCCESS;
6826 #if HAVE_LIBCURL
6827 xmlNsPtr isds_ns = NULL;
6828 xmlNodePtr request = NULL, node;
6829 xmlDocPtr response = NULL;
6830 #endif
6833 if (!context) return IE_INVALID_CONTEXT;
6834 zfree(context->long_message);
6835 if (credentials_delivery) {
6836 zfree(credentials_delivery->token);
6837 zfree(credentials_delivery->new_user_name);
6839 if (!service_name || service_name[0] == '\0' || !box || !user)
6840 return IE_INVAL;
6843 #if HAVE_LIBCURL
6844 /* Build NewAccessData or similar request */
6845 request = xmlNewNode(NULL, service_name);
6846 if (!request) {
6847 char *service_name_locale = _isds_utf82locale((char *) service_name);
6848 isds_printf_message(context, _("Could not build %s request"),
6849 service_name_locale);
6850 free(service_name_locale);
6851 return IE_ERROR;
6853 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6854 if(!isds_ns) {
6855 isds_log_message(context, _("Could not create ISDS name space"));
6856 xmlFreeNode(request);
6857 return IE_ERROR;
6859 xmlSetNs(request, isds_ns);
6861 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6862 err = insert_DbOwnerInfo(context, box, node);
6863 if (err) goto leave;
6865 INSERT_ELEMENT(node, request, "dbUserInfo");
6866 err = insert_DbUserInfo(context, user, node);
6867 if (err) goto leave;
6869 err = insert_credentials_delivery(context, credentials_delivery, request);
6870 if (err) goto leave;
6872 err = insert_GExtApproval(context, approval, request);
6873 if (err) goto leave;
6876 /* Send request and check response*/
6877 err = send_destroy_request_check_response(context,
6878 SERVICE_DB_MANIPULATION, service_name, &request, &response,
6879 refnumber, NULL);
6881 xmlFreeNode(request);
6882 request = NULL;
6884 /* Pick up credentials_delivery if requested */
6885 err = extract_credentials_delivery(context, credentials_delivery, response,
6886 (char *)service_name);
6888 leave:
6889 xmlFreeDoc(response);
6890 if (request) xmlFreeNode(request);
6892 if (!err) {
6893 char *service_name_locale = _isds_utf82locale((char *) service_name);
6894 isds_log(ILF_ISDS, ILL_DEBUG,
6895 _("%s request processed by server successfully.\n"),
6896 service_name_locale);
6897 free(service_name_locale);
6899 #else /* not HAVE_LIBCURL */
6900 err = IE_NOTSUP;
6901 #endif
6903 return err;
6907 /* Assign new user to given box.
6908 * @context is session context
6909 * @box is box identification
6910 * @user defines new user to add
6911 * @credentials_delivery is NULL if new user's password should be delivered
6912 * off-line to the user. It is valid pointer if user should obtain new
6913 * password on-line on dedicated web server. Then input
6914 * @credentials_delivery.email value is user's e-mail address user must
6915 * provide to dedicated web server together with @credentials_delivery.token.
6916 * The output reallocated token user needs to use to authorize on the web
6917 * server to view his new password. Output reallocated
6918 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6919 * assingned up on this call.
6920 * @approval is optional external approval of box manipulation
6921 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6922 * NULL, if you don't care.*/
6923 isds_error isds_add_user(struct isds_ctx *context,
6924 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6925 struct isds_credentials_delivery *credentials_delivery,
6926 const struct isds_approval *approval, char **refnumber) {
6927 return build_send_manipulationboxuser_request_check_drop_response(context,
6928 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
6929 approval, (xmlChar **) refnumber);
6933 /* Remove user assigned to given box.
6934 * @context is session context
6935 * @box is box identification
6936 * @user identifies user to remove
6937 * @approval is optional external approval of box manipulation
6938 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6939 * NULL, if you don't care.*/
6940 isds_error isds_delete_user(struct isds_ctx *context,
6941 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6942 const struct isds_approval *approval, char **refnumber) {
6943 return build_send_manipulationboxuser_request_check_drop_response(context,
6944 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
6945 (xmlChar **) refnumber);
6949 /* Get list of boxes in ZIP archive.
6950 * @context is session context
6951 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
6952 * System recognizes following values currently: ALL (all boxes), UPG
6953 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
6954 * receiving commercial messages). This argument is a string because
6955 * specification states new values can appear in the future. Not all list
6956 * types are available to all users.
6957 * @buffer is automatically reallocated memory to store the list of boxes. The
6958 * list is zipped CSV file.
6959 * @buffer_length is size of @buffer data in bytes.
6960 * In case of error @buffer will be freed and @buffer_length will be
6961 * undefined.*/
6962 isds_error isds_get_box_list_archive(struct isds_ctx *context,
6963 const char *list_identifier, void **buffer, size_t *buffer_length) {
6964 isds_error err = IE_SUCCESS;
6965 #if HAVE_LIBCURL
6966 xmlNsPtr isds_ns = NULL;
6967 xmlNodePtr request = NULL, node;
6968 xmlDocPtr response = NULL;
6969 xmlXPathContextPtr xpath_ctx = NULL;
6970 xmlXPathObjectPtr result = NULL;
6971 char *string = NULL;
6972 #endif
6975 if (!context) return IE_INVALID_CONTEXT;
6976 zfree(context->long_message);
6977 if (buffer) zfree(*buffer);
6978 if (!buffer || !buffer_length) return IE_INVAL;
6981 #if HAVE_LIBCURL
6982 /* Check if connection is established
6983 * TODO: This check should be done downstairs. */
6984 if (!context->curl) return IE_CONNECTION_CLOSED;
6987 /* Build AuthenticateMessage request */
6988 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
6989 if (!request) {
6990 isds_log_message(context,
6991 _("Could not build GetDataBoxList request"));
6992 return IE_ERROR;
6994 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6995 if(!isds_ns) {
6996 isds_log_message(context, _("Could not create ISDS name space"));
6997 xmlFreeNode(request);
6998 return IE_ERROR;
7000 xmlSetNs(request, isds_ns);
7001 INSERT_STRING(request, "dblType", list_identifier);
7003 /* Send request to server and process response */
7004 err = send_destroy_request_check_response(context,
7005 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7006 &response, NULL, NULL);
7007 if (err) goto leave;
7010 /* Extract Base-64 encoded ZIP file */
7011 xpath_ctx = xmlXPathNewContext(response);
7012 if (!xpath_ctx) {
7013 err = IE_ERROR;
7014 goto leave;
7016 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7017 err = IE_ERROR;
7018 goto leave;
7020 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7022 /* Decode non-empty archive */
7023 if (string && string[0] != '\0') {
7024 *buffer_length = _isds_b64decode(string, buffer);
7025 if (*buffer_length == (size_t) -1) {
7026 isds_printf_message(context,
7027 _("Error while Base64-decoding box list archive"));
7028 err = IE_ERROR;
7029 goto leave;
7034 leave:
7035 free(string);
7036 xmlXPathFreeObject(result);
7037 xmlXPathFreeContext(xpath_ctx);
7038 xmlFreeDoc(response);
7039 xmlFreeNode(request);
7041 if (!err) {
7042 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7043 "processed by server successfully.\n"));
7045 #else /* not HAVE_LIBCURL */
7046 err = IE_NOTSUP;
7047 #endif
7049 return err;
7053 /* Find boxes suiting given criteria.
7054 * @criteria is filter. You should fill in at least some members.
7055 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7056 * possibly empty. Input NULL or valid old structure.
7057 * @return:
7058 * IE_SUCCESS if search succeeded, @boxes contains useful data
7059 * IE_NOEXIST if no such box exists, @boxes will be NULL
7060 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7061 * contains still valid data
7062 * other code if something bad happens. @boxes will be NULL. */
7063 isds_error isds_FindDataBox(struct isds_ctx *context,
7064 const struct isds_DbOwnerInfo *criteria,
7065 struct isds_list **boxes) {
7066 isds_error err = IE_SUCCESS;
7067 #if HAVE_LIBCURL
7068 _Bool truncated = 0;
7069 xmlNsPtr isds_ns = NULL;
7070 xmlNodePtr request = NULL;
7071 xmlDocPtr response = NULL;
7072 xmlChar *code = NULL, *message = NULL;
7073 xmlNodePtr db_owner_info;
7074 xmlXPathContextPtr xpath_ctx = NULL;
7075 xmlXPathObjectPtr result = NULL;
7076 xmlChar *string = NULL;
7077 #endif
7080 if (!context) return IE_INVALID_CONTEXT;
7081 zfree(context->long_message);
7082 if (!boxes) return IE_INVAL;
7083 isds_list_free(boxes);
7085 if (!criteria) {
7086 return IE_INVAL;
7089 #if HAVE_LIBCURL
7090 /* Check if connection is established
7091 * TODO: This check should be done downstairs. */
7092 if (!context->curl) return IE_CONNECTION_CLOSED;
7095 /* Build FindDataBox request */
7096 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
7097 if (!request) {
7098 isds_log_message(context,
7099 _("Could build FindDataBox request"));
7100 return IE_ERROR;
7102 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7103 if(!isds_ns) {
7104 isds_log_message(context, _("Could not create ISDS name space"));
7105 xmlFreeNode(request);
7106 return IE_ERROR;
7108 xmlSetNs(request, isds_ns);
7109 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7110 if (!db_owner_info) {
7111 isds_log_message(context, _("Could not add dbOwnerInfo child to "
7112 "FindDataBox element"));
7113 xmlFreeNode(request);
7114 return IE_ERROR;
7117 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
7118 if (err) goto leave;
7121 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
7123 /* Sent request */
7124 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7126 /* Destroy request */
7127 xmlFreeNode(request); request = NULL;
7129 if (err) {
7130 isds_log(ILF_ISDS, ILL_DEBUG,
7131 _("Processing ISDS response on FindDataBox "
7132 "request failed\n"));
7133 goto leave;
7136 /* Check for response status */
7137 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7138 &code, &message, NULL);
7139 if (err) {
7140 isds_log(ILF_ISDS, ILL_DEBUG,
7141 _("ISDS response on FindDataBox request is missing status\n"));
7142 goto leave;
7145 /* Request processed, but nothing found */
7146 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7147 !xmlStrcmp(code, BAD_CAST "5001")) {
7148 char *code_locale = _isds_utf82locale((char*)code);
7149 char *message_locale = _isds_utf82locale((char*)message);
7150 isds_log(ILF_ISDS, ILL_DEBUG,
7151 _("Server did not found any box on FindDataBox request "
7152 "(code=%s, message=%s)\n"), code_locale, message_locale);
7153 isds_log_message(context, message_locale);
7154 free(code_locale);
7155 free(message_locale);
7156 err = IE_NOEXIST;
7157 goto leave;
7160 /* Warning, not a error */
7161 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7162 char *code_locale = _isds_utf82locale((char*)code);
7163 char *message_locale = _isds_utf82locale((char*)message);
7164 isds_log(ILF_ISDS, ILL_DEBUG,
7165 _("Server truncated response on FindDataBox request "
7166 "(code=%s, message=%s)\n"), code_locale, message_locale);
7167 isds_log_message(context, message_locale);
7168 free(code_locale);
7169 free(message_locale);
7170 truncated = 1;
7173 /* Other error */
7174 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7175 char *code_locale = _isds_utf82locale((char*)code);
7176 char *message_locale = _isds_utf82locale((char*)message);
7177 isds_log(ILF_ISDS, ILL_DEBUG,
7178 _("Server refused FindDataBox request "
7179 "(code=%s, message=%s)\n"), code_locale, message_locale);
7180 isds_log_message(context, message_locale);
7181 free(code_locale);
7182 free(message_locale);
7183 err = IE_ISDS;
7184 goto leave;
7187 xpath_ctx = xmlXPathNewContext(response);
7188 if (!xpath_ctx) {
7189 err = IE_ERROR;
7190 goto leave;
7192 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7193 err = IE_ERROR;
7194 goto leave;
7197 /* Extract boxes if they present */
7198 result = xmlXPathEvalExpression(BAD_CAST
7199 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7200 xpath_ctx);
7201 if (!result) {
7202 err = IE_ERROR;
7203 goto leave;
7205 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7206 struct isds_list *item, *prev_item = NULL;
7207 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7208 item = calloc(1, sizeof(*item));
7209 if (!item) {
7210 err = IE_NOMEM;
7211 goto leave;
7214 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7215 if (i == 0) *boxes = item;
7216 else prev_item->next = item;
7217 prev_item = item;
7219 xpath_ctx->node = result->nodesetval->nodeTab[i];
7220 err = extract_DbOwnerInfo(context,
7221 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7222 if (err) goto leave;
7226 leave:
7227 if (err) {
7228 isds_list_free(boxes);
7229 } else {
7230 if (truncated) err = IE_2BIG;
7233 free(string);
7234 xmlFreeNode(request);
7235 xmlXPathFreeObject(result);
7236 xmlXPathFreeContext(xpath_ctx);
7238 free(code);
7239 free(message);
7240 xmlFreeDoc(response);
7242 if (!err)
7243 isds_log(ILF_ISDS, ILL_DEBUG,
7244 _("FindDataBox request processed by server successfully.\n"));
7245 #else /* not HAVE_LIBCURL */
7246 err = IE_NOTSUP;
7247 #endif
7249 return err;
7253 /* Get status of a box.
7254 * @context is ISDS session context.
7255 * @box_id is UTF-8 encoded box identifier as zero terminated string
7256 * @box_status is return value of box status.
7257 * @return:
7258 * IE_SUCCESS if box has been found and its status retrieved
7259 * IE_NOEXIST if box is not known to ISDS server
7260 * or other appropriate error.
7261 * You can use isds_DbState to enumerate box status. However out of enum
7262 * range value can be returned too. This is feature because ISDS
7263 * specification leaves the set of values open.
7264 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7265 * the box has been deleted, but ISDS still lists its former existence. */
7266 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7267 long int *box_status) {
7268 isds_error err = IE_SUCCESS;
7269 #if HAVE_LIBCURL
7270 xmlNsPtr isds_ns = NULL;
7271 xmlNodePtr request = NULL, db_id;
7272 xmlDocPtr response = NULL;
7273 xmlXPathContextPtr xpath_ctx = NULL;
7274 xmlXPathObjectPtr result = NULL;
7275 xmlChar *string = NULL;
7277 const xmlChar *codes[] = {
7278 BAD_CAST "5001",
7279 BAD_CAST "1007",
7280 BAD_CAST "2011",
7281 NULL
7283 const char *meanings[] = {
7284 "The box does not exist",
7285 "Box ID is malformed",
7286 "Box ID malformed",
7288 const isds_error errors[] = {
7289 IE_NOEXIST,
7290 IE_INVAL,
7291 IE_INVAL,
7293 struct code_map_isds_error map = {
7294 .codes = codes,
7295 .meanings = meanings,
7296 .errors = errors
7298 #endif
7300 if (!context) return IE_INVALID_CONTEXT;
7301 zfree(context->long_message);
7302 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7304 #if HAVE_LIBCURL
7305 /* Check if connection is established
7306 * TODO: This check should be done downstairs. */
7307 if (!context->curl) return IE_CONNECTION_CLOSED;
7310 /* Build CheckDataBox request */
7311 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7312 if (!request) {
7313 isds_log_message(context,
7314 _("Could build CheckDataBox request"));
7315 return IE_ERROR;
7317 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7318 if(!isds_ns) {
7319 isds_log_message(context, _("Could not create ISDS name space"));
7320 xmlFreeNode(request);
7321 return IE_ERROR;
7323 xmlSetNs(request, isds_ns);
7324 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7325 if (!db_id) {
7326 isds_log_message(context, _("Could not add dbID child to "
7327 "CheckDataBox element"));
7328 xmlFreeNode(request);
7329 return IE_ERROR;
7333 /* Send request and check response*/
7334 err = send_destroy_request_check_response(context,
7335 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7336 &request, &response, NULL, &map);
7337 if (err) goto leave;
7340 /* Extract data */
7341 xpath_ctx = xmlXPathNewContext(response);
7342 if (!xpath_ctx) {
7343 err = IE_ERROR;
7344 goto leave;
7346 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7347 err = IE_ERROR;
7348 goto leave;
7350 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7351 xpath_ctx);
7352 if (!result) {
7353 err = IE_ERROR;
7354 goto leave;
7356 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7357 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7358 err = IE_ISDS;
7359 goto leave;
7361 if (result->nodesetval->nodeNr > 1) {
7362 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7363 err = IE_ISDS;
7364 goto leave;
7366 xpath_ctx->node = result->nodesetval->nodeTab[0];
7367 xmlXPathFreeObject(result); result = NULL;
7369 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7372 leave:
7373 free(string);
7374 xmlXPathFreeObject(result);
7375 xmlXPathFreeContext(xpath_ctx);
7377 xmlFreeDoc(response);
7379 if (!err)
7380 isds_log(ILF_ISDS, ILL_DEBUG,
7381 _("CheckDataBox request processed by server successfully.\n"));
7382 #else /* not HAVE_LIBCURL */
7383 err = IE_NOTSUP;
7384 #endif
7386 return err;
7390 /* Get list of permissions to send commercial messages.
7391 * @context is ISDS session context.
7392 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7393 * @permissions is a reallocated list of permissions (struct
7394 * isds_commercial_permission*) to send commercial messages from @box_id. The
7395 * order of permissions is significant as the server applies the permissions
7396 * and associated pre-paid credits in the order. Empty list means no
7397 * permission.
7398 * @return:
7399 * IE_SUCCESS if the list has been obtained correctly,
7400 * or other appropriate error. */
7401 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7402 const char *box_id, struct isds_list **permissions) {
7403 isds_error err = IE_SUCCESS;
7404 #if HAVE_LIBCURL
7405 xmlDocPtr response = NULL;
7406 xmlXPathContextPtr xpath_ctx = NULL;
7407 xmlXPathObjectPtr result = NULL;
7408 #endif
7410 if (!context) return IE_INVALID_CONTEXT;
7411 zfree(context->long_message);
7412 if (NULL == permissions) return IE_INVAL;
7413 isds_list_free(permissions);
7414 if (NULL == box_id) return IE_INVAL;
7416 #if HAVE_LIBCURL
7417 /* Check if connection is established */
7418 if (!context->curl) return IE_CONNECTION_CLOSED;
7420 /* Do request and check for success */
7421 err = build_send_dbid_request_check_response(context,
7422 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7423 BAD_CAST box_id, NULL, &response, NULL);
7424 if (!err) {
7425 isds_log(ILF_ISDS, ILL_DEBUG,
7426 _("PDZInfo request processed by server successfully.\n"));
7429 /* Extract data */
7430 /* Prepare structure */
7431 xpath_ctx = xmlXPathNewContext(response);
7432 if (!xpath_ctx) {
7433 err = IE_ERROR;
7434 goto leave;
7436 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7437 err = IE_ERROR;
7438 goto leave;
7441 /* Set context node */
7442 result = xmlXPathEvalExpression(BAD_CAST
7443 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7444 xpath_ctx);
7445 if (!result) {
7446 err = IE_ERROR;
7447 goto leave;
7449 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7450 struct isds_list *prev_item = NULL;
7452 /* Iterate over all permission records */
7453 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7454 struct isds_list *item;
7456 /* Prepare structure */
7457 item = calloc(1, sizeof(*item));
7458 if (!item) {
7459 err = IE_NOMEM;
7460 goto leave;
7462 item->destructor = (void(*)(void**))isds_commercial_permission_free;
7463 if (i == 0) *permissions = item;
7464 else prev_item->next = item;
7465 prev_item = item;
7467 /* Extract it */
7468 xpath_ctx->node = result->nodesetval->nodeTab[i];
7469 err = extract_DbPDZRecord(context,
7470 (struct isds_commercial_permission **) (&item->data),
7471 xpath_ctx);
7472 if (err) goto leave;
7476 leave:
7477 if (err) {
7478 isds_list_free(permissions);
7481 xmlXPathFreeObject(result);
7482 xmlXPathFreeContext(xpath_ctx);
7483 xmlFreeDoc(response);
7485 #else /* not HAVE_LIBCURL */
7486 err = IE_NOTSUP;
7487 #endif
7489 return err;
7493 /* Get details about credit for sending pre-paid commercial messages.
7494 * @context is ISDS session context.
7495 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
7496 * @from_date is first day of credit history to return in @history. Only
7497 * tm_year, tm_mon and tm_mday carry sane value.
7498 * @to_date is last day of credit history to return in @history. Only
7499 * tm_year, tm_mon and tm_mday carry sane value.
7500 * @credit outputs current credit value into pre-allocated memory. Pass NULL
7501 * if you don't care. This and all other credit values are integers in
7502 * hundredths of Czech Crowns.
7503 * @email outputs notification e-mail address where notifications about credit
7504 * are sent. This is automatically reallocated string. Pass NULL if you don't
7505 * care. It can return NULL if no address is defined.
7506 * @history outputs auto-reallocated list of pointers to struct
7507 * isds_credit_event. Events in closed interval @from_time to @to_time are
7508 * returned. Pass NULL @to_time and @from_time if you don't care. The events
7509 * are sorted by time.
7510 * @return:
7511 * IE_SUCCESS if the credit details have been obtained correctly,
7512 * or other appropriate error. Please note that server allows to retrieve
7513 * only limited history of events. */
7514 isds_error isds_get_commercial_credit(struct isds_ctx *context,
7515 const char *box_id,
7516 const struct tm *from_date, const struct tm *to_date,
7517 long int *credit, char **email, struct isds_list **history) {
7518 isds_error err = IE_SUCCESS;
7519 #if HAVE_LIBCURL
7520 char *box_id_locale = NULL;
7521 xmlNodePtr request = NULL, node;
7522 xmlNsPtr isds_ns = NULL;
7523 xmlChar *string = NULL;
7525 xmlDocPtr response = NULL;
7526 xmlXPathContextPtr xpath_ctx = NULL;
7527 xmlXPathObjectPtr result = NULL;
7529 const xmlChar *codes[] = {
7530 BAD_CAST "1004",
7531 BAD_CAST "2011",
7532 BAD_CAST "1093",
7533 BAD_CAST "1137",
7534 BAD_CAST "1058",
7535 NULL
7537 const char *meanings[] = {
7538 "Insufficient priviledges for the box",
7539 "The box does not exist",
7540 "Date is too long (history is not available after 15 months)",
7541 "Interval is too long (limit is 3 months)",
7542 "Invalid date"
7544 const isds_error errors[] = {
7545 IE_ISDS,
7546 IE_NOEXIST,
7547 IE_DATE,
7548 IE_DATE,
7549 IE_DATE,
7551 struct code_map_isds_error map = {
7552 .codes = codes,
7553 .meanings = meanings,
7554 .errors = errors
7556 #endif
7558 if (!context) return IE_INVALID_CONTEXT;
7559 zfree(context->long_message);
7561 /* Free output argument */
7562 if (NULL != credit) *credit = 0;
7563 if (NULL != email) zfree(*email);
7564 isds_list_free(history);
7566 if (NULL == box_id) return IE_INVAL;
7568 #if HAVE_LIBCURL
7569 /* Check if connection is established */
7570 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7572 box_id_locale = _isds_utf82locale((char*)box_id);
7573 if (NULL == box_id_locale) {
7574 err = IE_NOMEM;
7575 goto leave;
7578 /* Build request */
7579 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
7580 if (NULL == request) {
7581 isds_printf_message(context,
7582 _("Could not build DataBoxCreditInfo request for %s box"),
7583 box_id_locale);
7584 err = IE_ERROR;
7585 goto leave;
7587 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7588 if(!isds_ns) {
7589 isds_log_message(context, _("Could not create ISDS name space"));
7590 err = IE_ERROR;
7591 goto leave;
7593 xmlSetNs(request, isds_ns);
7595 /* Add mandatory XSD:tIdDbInput child */
7596 INSERT_STRING(request, BAD_CAST "dbID", box_id);
7597 /* Add mandatory dates elements with optional values */
7598 if (from_date) {
7599 err = tm2datestring(from_date, &string);
7600 if (err) {
7601 isds_log_message(context,
7602 _("Could not convert `from_date' argument to ISO date "
7603 "string"));
7604 goto leave;
7606 INSERT_STRING(request, "ciFromDate", string);
7607 zfree(string);
7608 } else {
7609 INSERT_STRING(request, "ciFromDate", NULL);
7611 if (to_date) {
7612 err = tm2datestring(to_date, &string);
7613 if (err) {
7614 isds_log_message(context,
7615 _("Could not convert `to_date' argument to ISO date "
7616 "string"));
7617 goto leave;
7619 INSERT_STRING(request, "ciTodate", string);
7620 zfree(string);
7621 } else {
7622 INSERT_STRING(request, "ciTodate", NULL);
7625 /* Send request and check response*/
7626 err = send_destroy_request_check_response(context,
7627 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
7628 &request, &response, NULL, &map);
7629 if (err) goto leave;
7632 /* Extract data */
7633 /* Set context to the root */
7634 xpath_ctx = xmlXPathNewContext(response);
7635 if (!xpath_ctx) {
7636 err = IE_ERROR;
7637 goto leave;
7639 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7640 err = IE_ERROR;
7641 goto leave;
7643 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
7644 xpath_ctx);
7645 if (!result) {
7646 err = IE_ERROR;
7647 goto leave;
7649 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7650 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
7651 err = IE_ISDS;
7652 goto leave;
7654 if (result->nodesetval->nodeNr > 1) {
7655 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
7656 err = IE_ISDS;
7657 goto leave;
7659 xpath_ctx->node = result->nodesetval->nodeTab[0];
7660 xmlXPathFreeObject(result); result = NULL;
7662 /* Extract common data */
7663 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
7664 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
7666 /* Extract records */
7667 if (NULL == history) goto leave;
7668 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
7669 xpath_ctx);
7670 if (!result) {
7671 err = IE_ERROR;
7672 goto leave;
7674 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7675 struct isds_list *prev_item = NULL;
7677 /* Iterate over all records */
7678 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7679 struct isds_list *item;
7681 /* Prepare structure */
7682 item = calloc(1, sizeof(*item));
7683 if (!item) {
7684 err = IE_NOMEM;
7685 goto leave;
7687 item->destructor = (void(*)(void**))isds_credit_event_free;
7688 if (i == 0) *history = item;
7689 else prev_item->next = item;
7690 prev_item = item;
7692 /* Extract it */
7693 xpath_ctx->node = result->nodesetval->nodeTab[i];
7694 err = extract_CiRecord(context,
7695 (struct isds_credit_event **) (&item->data),
7696 xpath_ctx);
7697 if (err) goto leave;
7701 leave:
7702 if (!err) {
7703 isds_log(ILF_ISDS, ILL_DEBUG,
7704 _("DataBoxCreditInfo request processed by server successfully.\n"));
7706 if (err) {
7707 isds_list_free(history);
7708 if (NULL != email) zfree(*email)
7711 free(box_id_locale);
7712 xmlXPathFreeObject(result);
7713 xmlXPathFreeContext(xpath_ctx);
7714 xmlFreeDoc(response);
7716 #else /* not HAVE_LIBCURL */
7717 err = IE_NOTSUP;
7718 #endif
7720 return err;
7724 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
7725 * code, destroy response and log success.
7726 * @context is ISDS session context.
7727 * @service_name is name of SERVICE_DB_MANIPULATION service
7728 * @box_id is UTF-8 encoded box identifier as zero terminated string
7729 * @approval is optional external approval of box manipulation
7730 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7731 * NULL, if you don't care. */
7732 static isds_error build_send_manipulationdbid_request_check_drop_response(
7733 struct isds_ctx *context, const xmlChar *service_name,
7734 const xmlChar *box_id, const struct isds_approval *approval,
7735 xmlChar **refnumber) {
7736 isds_error err = IE_SUCCESS;
7737 #if HAVE_LIBCURL
7738 xmlDocPtr response = NULL;
7739 #endif
7741 if (!context) return IE_INVALID_CONTEXT;
7742 zfree(context->long_message);
7743 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
7745 #if HAVE_LIBCURL
7746 /* Check if connection is established */
7747 if (!context->curl) return IE_CONNECTION_CLOSED;
7749 /* Do request and check for success */
7750 err = build_send_dbid_request_check_response(context,
7751 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
7752 &response, refnumber);
7753 xmlFreeDoc(response);
7755 if (!err) {
7756 char *service_name_locale = _isds_utf82locale((char *) service_name);
7757 isds_log(ILF_ISDS, ILL_DEBUG,
7758 _("%s request processed by server successfully.\n"),
7759 service_name_locale);
7760 free(service_name_locale);
7762 #else /* not HAVE_LIBCURL */
7763 err = IE_NOTSUP;
7764 #endif
7766 return err;
7770 /* Switch box into state where box can receive commercial messages (off by
7771 * default)
7772 * @context is ISDS session context.
7773 * @box_id is UTF-8 encoded box identifier as zero terminated string
7774 * @allow is true for enable, false for disable commercial messages income
7775 * @approval is optional external approval of box manipulation
7776 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7777 * NULL, if you don't care. */
7778 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
7779 const char *box_id, const _Bool allow,
7780 const struct isds_approval *approval, char **refnumber) {
7781 return build_send_manipulationdbid_request_check_drop_response(context,
7782 (allow) ? BAD_CAST "SetOpenAddressing" :
7783 BAD_CAST "ClearOpenAddressing",
7784 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7788 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
7789 * message acceptance). This is just a box permission. Sender must apply
7790 * such role by sending each message.
7791 * @context is ISDS session context.
7792 * @box_id is UTF-8 encoded box identifier as zero terminated string
7793 * @allow is true for enable, false for disable OVM role permission
7794 * @approval is optional external approval of box manipulation
7795 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7796 * NULL, if you don't care. */
7797 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
7798 const char *box_id, const _Bool allow,
7799 const struct isds_approval *approval, char **refnumber) {
7800 return build_send_manipulationdbid_request_check_drop_response(context,
7801 (allow) ? BAD_CAST "SetEffectiveOVM" :
7802 BAD_CAST "ClearEffectiveOVM",
7803 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7807 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
7808 * code, destroy response and log success.
7809 * @context is ISDS session context.
7810 * @service_name is name of SERVICE_DB_MANIPULATION service
7811 * @owner is structure describing box
7812 * @approval is optional external approval of box manipulation
7813 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7814 * NULL, if you don't care. */
7815 static isds_error build_send_manipulationdbowner_request_check_drop_response(
7816 struct isds_ctx *context, const xmlChar *service_name,
7817 const struct isds_DbOwnerInfo *owner,
7818 const struct isds_approval *approval, xmlChar **refnumber) {
7819 isds_error err = IE_SUCCESS;
7820 #if HAVE_LIBCURL
7821 char *service_name_locale = NULL;
7822 xmlNodePtr request = NULL, db_owner_info;
7823 xmlNsPtr isds_ns = NULL;
7824 #endif
7827 if (!context) return IE_INVALID_CONTEXT;
7828 zfree(context->long_message);
7829 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
7831 #if HAVE_LIBCURL
7832 service_name_locale = _isds_utf82locale((char*)service_name);
7833 if (!service_name_locale) {
7834 err = IE_NOMEM;
7835 goto leave;
7838 /* Build request */
7839 request = xmlNewNode(NULL, service_name);
7840 if (!request) {
7841 isds_printf_message(context,
7842 _("Could not build %s request"), service_name_locale);
7843 err = IE_ERROR;
7844 goto leave;
7846 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7847 if(!isds_ns) {
7848 isds_log_message(context, _("Could not create ISDS name space"));
7849 err = IE_ERROR;
7850 goto leave;
7852 xmlSetNs(request, isds_ns);
7855 /* Add XSD:tOwnerInfoInput child*/
7856 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
7857 err = insert_DbOwnerInfo(context, owner, db_owner_info);
7858 if (err) goto leave;
7860 /* Add XSD:gExtApproval*/
7861 err = insert_GExtApproval(context, approval, request);
7862 if (err) goto leave;
7864 /* Send it to server and process response */
7865 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7866 service_name, &request, refnumber);
7868 leave:
7869 xmlFreeNode(request);
7870 free(service_name_locale);
7871 #else /* not HAVE_LIBCURL */
7872 err = IE_NOTSUP;
7873 #endif
7875 return err;
7879 /* Switch box accessibility state on request of box owner.
7880 * Despite the name, owner must do the request off-line. This function is
7881 * designed for such off-line meeting points (e.g. Czech POINT).
7882 * @context is ISDS session context.
7883 * @box identifies box to switch accessibility state.
7884 * @allow is true for making accessible, false to disallow access.
7885 * @approval is optional external approval of box manipulation
7886 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7887 * NULL, if you don't care. */
7888 isds_error isds_switch_box_accessibility_on_owner_request(
7889 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7890 const _Bool allow, const struct isds_approval *approval,
7891 char **refnumber) {
7892 return build_send_manipulationdbowner_request_check_drop_response(context,
7893 (allow) ? BAD_CAST "EnableOwnDataBox" :
7894 BAD_CAST "DisableOwnDataBox",
7895 box, approval, (xmlChar **) refnumber);
7899 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
7900 * date.
7901 * @context is ISDS session context.
7902 * @box identifies box to switch accessibility state.
7903 * @since is date since accessibility has been denied. This can be past too.
7904 * Only tm_year, tm_mon and tm_mday carry sane value.
7905 * @approval is optional external approval of box manipulation
7906 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7907 * NULL, if you don't care. */
7908 isds_error isds_disable_box_accessibility_externaly(
7909 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7910 const struct tm *since, const struct isds_approval *approval,
7911 char **refnumber) {
7912 isds_error err = IE_SUCCESS;
7913 #if HAVE_LIBCURL
7914 char *service_name_locale = NULL;
7915 xmlNodePtr request = NULL, node;
7916 xmlNsPtr isds_ns = NULL;
7917 xmlChar *string = NULL;
7918 #endif
7921 if (!context) return IE_INVALID_CONTEXT;
7922 zfree(context->long_message);
7923 if (!box || !since) return IE_INVAL;
7925 #if HAVE_LIBCURL
7926 /* Build request */
7927 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
7928 if (!request) {
7929 isds_printf_message(context,
7930 _("Could not build %s request"), "DisableDataBoxExternally");
7931 err = IE_ERROR;
7932 goto leave;
7934 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7935 if(!isds_ns) {
7936 isds_log_message(context, _("Could not create ISDS name space"));
7937 err = IE_ERROR;
7938 goto leave;
7940 xmlSetNs(request, isds_ns);
7943 /* Add @box identification */
7944 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7945 err = insert_DbOwnerInfo(context, box, node);
7946 if (err) goto leave;
7948 /* Add @since date */
7949 err = tm2datestring(since, &string);
7950 if(err) {
7951 isds_log_message(context,
7952 _("Could not convert `since' argument to ISO date string"));
7953 goto leave;
7955 INSERT_STRING(request, "dbOwnerDisableDate", string);
7956 zfree(string);
7958 /* Add @approval */
7959 err = insert_GExtApproval(context, approval, request);
7960 if (err) goto leave;
7962 /* Send it to server and process response */
7963 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7964 BAD_CAST "DisableDataBoxExternally", &request,
7965 (xmlChar **) refnumber);
7967 leave:
7968 free(string);
7969 xmlFreeNode(request);
7970 free(service_name_locale);
7971 #else /* not HAVE_LIBCURL */
7972 err = IE_NOTSUP;
7973 #endif
7975 return err;
7979 #if HAVE_LIBCURL
7980 /* Insert struct isds_message data (envelope (recipient data optional) and
7981 * documents into XML tree
7982 * @context is session context
7983 * @outgoing_message is libisds structure with message data
7984 * @create_message is XML CreateMessage or CreateMultipleMessage element
7985 * @process_recipient true for recipient data serialization, false for no
7986 * serialization */
7987 static isds_error insert_envelope_files(struct isds_ctx *context,
7988 const struct isds_message *outgoing_message, xmlNodePtr create_message,
7989 const _Bool process_recipient) {
7991 isds_error err = IE_SUCCESS;
7992 xmlNodePtr envelope, dm_files, node;
7993 xmlChar *string = NULL;
7995 if (!context) return IE_INVALID_CONTEXT;
7996 if (!outgoing_message || !create_message) return IE_INVAL;
7999 /* Build envelope */
8000 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8001 if (!envelope) {
8002 isds_printf_message(context, _("Could not add dmEnvelope child to "
8003 "%s element"), create_message->name);
8004 return IE_ERROR;
8007 if (!outgoing_message->envelope) {
8008 isds_log_message(context, _("Outgoing message is missing envelope"));
8009 err = IE_INVAL;
8010 goto leave;
8013 /* Insert optional message type */
8014 err = insert_message_type(context, outgoing_message->envelope->dmType,
8015 envelope);
8016 if (err) goto leave;
8018 INSERT_STRING(envelope, "dmSenderOrgUnit",
8019 outgoing_message->envelope->dmSenderOrgUnit);
8020 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8021 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8023 if (process_recipient) {
8024 if (!outgoing_message->envelope->dbIDRecipient) {
8025 isds_log_message(context,
8026 _("Outgoing message is missing recipient box identifier"));
8027 err = IE_INVAL;
8028 goto leave;
8030 INSERT_STRING(envelope, "dbIDRecipient",
8031 outgoing_message->envelope->dbIDRecipient);
8033 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8034 outgoing_message->envelope->dmRecipientOrgUnit);
8035 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8036 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8037 INSERT_STRING(envelope, "dmToHands",
8038 outgoing_message->envelope->dmToHands);
8041 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8042 "dmAnnotation");
8043 INSERT_STRING(envelope, "dmAnnotation",
8044 outgoing_message->envelope->dmAnnotation);
8046 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8047 0, 50, "dmRecipientRefNumber");
8048 INSERT_STRING(envelope, "dmRecipientRefNumber",
8049 outgoing_message->envelope->dmRecipientRefNumber);
8051 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8052 0, 50, "dmSenderRefNumber");
8053 INSERT_STRING(envelope, "dmSenderRefNumber",
8054 outgoing_message->envelope->dmSenderRefNumber);
8056 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8057 0, 50, "dmRecipientIdent");
8058 INSERT_STRING(envelope, "dmRecipientIdent",
8059 outgoing_message->envelope->dmRecipientIdent);
8061 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8062 0, 50, "dmSenderIdent");
8063 INSERT_STRING(envelope, "dmSenderIdent",
8064 outgoing_message->envelope->dmSenderIdent);
8066 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8067 outgoing_message->envelope->dmLegalTitleLaw, string);
8068 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8069 outgoing_message->envelope->dmLegalTitleYear, string);
8070 INSERT_STRING(envelope, "dmLegalTitleSect",
8071 outgoing_message->envelope->dmLegalTitleSect);
8072 INSERT_STRING(envelope, "dmLegalTitlePar",
8073 outgoing_message->envelope->dmLegalTitlePar);
8074 INSERT_STRING(envelope, "dmLegalTitlePoint",
8075 outgoing_message->envelope->dmLegalTitlePoint);
8077 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8078 outgoing_message->envelope->dmPersonalDelivery);
8079 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8080 outgoing_message->envelope->dmAllowSubstDelivery);
8082 /* ???: Should we require value for dbEffectiveOVM sender?
8083 * ISDS has default as true */
8084 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8085 INSERT_BOOLEAN(envelope, "dmOVM",
8086 outgoing_message->envelope->dmPublishOwnID);
8089 /* Append dmFiles */
8090 if (!outgoing_message->documents) {
8091 isds_log_message(context,
8092 _("Outgoing message is missing list of documents"));
8093 err = IE_INVAL;
8094 goto leave;
8096 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8097 if (!dm_files) {
8098 isds_printf_message(context, _("Could not add dmFiles child to "
8099 "%s element"), create_message->name);
8100 err = IE_ERROR;
8101 goto leave;
8104 /* Check for document hierarchy */
8105 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8106 if (err) goto leave;
8108 /* Process each document */
8109 for (struct isds_list *item =
8110 (struct isds_list *) outgoing_message->documents;
8111 item; item = item->next) {
8112 if (!item->data) {
8113 isds_log_message(context,
8114 _("List of documents contains empty item"));
8115 err = IE_INVAL;
8116 goto leave;
8118 /* FIXME: Check for dmFileMetaType and for document references.
8119 * Only first document can be of MAIN type */
8120 err = insert_document(context, (struct isds_document*) item->data,
8121 dm_files);
8123 if (err) goto leave;
8126 leave:
8127 free(string);
8128 return err;
8130 #endif /* HAVE_LIBCURL */
8133 /* Send a message via ISDS to a recipient
8134 * @context is session context
8135 * @outgoing_message is message to send; Some members are mandatory (like
8136 * dbIDRecipient), some are optional and some are irrelevant (especially data
8137 * about sender). Included pointer to isds_list documents must contain at
8138 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8139 * members will be filled with valid data from ISDS. Exact list of write
8140 * members is subject to change. Currently dmID is changed.
8141 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8142 isds_error isds_send_message(struct isds_ctx *context,
8143 struct isds_message *outgoing_message) {
8145 isds_error err = IE_SUCCESS;
8146 #if HAVE_LIBCURL
8147 xmlNsPtr isds_ns = NULL;
8148 xmlNodePtr request = NULL;
8149 xmlDocPtr response = NULL;
8150 xmlChar *code = NULL, *message = NULL;
8151 xmlXPathContextPtr xpath_ctx = NULL;
8152 xmlXPathObjectPtr result = NULL;
8153 /*_Bool message_is_complete = 0;*/
8154 #endif
8156 if (!context) return IE_INVALID_CONTEXT;
8157 zfree(context->long_message);
8158 if (!outgoing_message) return IE_INVAL;
8160 #if HAVE_LIBCURL
8161 /* Check if connection is established
8162 * TODO: This check should be done downstairs. */
8163 if (!context->curl) return IE_CONNECTION_CLOSED;
8166 /* Build CreateMessage request */
8167 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8168 if (!request) {
8169 isds_log_message(context,
8170 _("Could not build CreateMessage request"));
8171 return IE_ERROR;
8173 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8174 if(!isds_ns) {
8175 isds_log_message(context, _("Could not create ISDS name space"));
8176 xmlFreeNode(request);
8177 return IE_ERROR;
8179 xmlSetNs(request, isds_ns);
8181 /* Append envelope and files */
8182 err = insert_envelope_files(context, outgoing_message, request, 1);
8183 if (err) goto leave;
8186 /* Signal we can serialize message since now */
8187 /*message_is_complete = 1;*/
8190 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8192 /* Sent request */
8193 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8195 /* Don't' destroy request, we want to provide it to application later */
8197 if (err) {
8198 isds_log(ILF_ISDS, ILL_DEBUG,
8199 _("Processing ISDS response on CreateMessage "
8200 "request failed\n"));
8201 goto leave;
8204 /* Check for response status */
8205 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8206 &code, &message, NULL);
8207 if (err) {
8208 isds_log(ILF_ISDS, ILL_DEBUG,
8209 _("ISDS response on CreateMessage request "
8210 "is missing status\n"));
8211 goto leave;
8214 /* Request processed, but refused by server or server failed */
8215 if (xmlStrcmp(code, BAD_CAST "0000")) {
8216 char *box_id_locale =
8217 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8218 char *code_locale = _isds_utf82locale((char*)code);
8219 char *message_locale = _isds_utf82locale((char*)message);
8220 isds_log(ILF_ISDS, ILL_DEBUG,
8221 _("Server did not accept message for %s on CreateMessage "
8222 "request (code=%s, message=%s)\n"),
8223 box_id_locale, code_locale, message_locale);
8224 isds_log_message(context, message_locale);
8225 free(box_id_locale);
8226 free(code_locale);
8227 free(message_locale);
8228 err = IE_ISDS;
8229 goto leave;
8233 /* Extract data */
8234 xpath_ctx = xmlXPathNewContext(response);
8235 if (!xpath_ctx) {
8236 err = IE_ERROR;
8237 goto leave;
8239 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8240 err = IE_ERROR;
8241 goto leave;
8243 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8244 xpath_ctx);
8245 if (!result) {
8246 err = IE_ERROR;
8247 goto leave;
8249 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8250 isds_log_message(context, _("Missing CreateMessageResponse element"));
8251 err = IE_ISDS;
8252 goto leave;
8254 if (result->nodesetval->nodeNr > 1) {
8255 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8256 err = IE_ISDS;
8257 goto leave;
8259 xpath_ctx->node = result->nodesetval->nodeTab[0];
8260 xmlXPathFreeObject(result); result = NULL;
8262 if (outgoing_message->envelope->dmID) {
8263 free(outgoing_message->envelope->dmID);
8264 outgoing_message->envelope->dmID = NULL;
8266 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8267 if (!outgoing_message->envelope->dmID) {
8268 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8269 "but did not return assigned message ID\n"));
8272 leave:
8273 /* TODO: Serialize message into structure member raw */
8274 /* XXX: Each web service transport message in different format.
8275 * Therefore it's not possible to save them directly.
8276 * To save them, one must figure out common format.
8277 * We can leave it on application, or we can implement the ESS format. */
8278 /*if (message_is_complete) {
8279 if (outgoing_message->envelope->dmID) {
8281 /* Add assigned message ID as first child*/
8282 /*xmlNodePtr dmid_text = xmlNewText(
8283 (xmlChar *) outgoing_message->envelope->dmID);
8284 if (!dmid_text) goto serialization_failed;
8286 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8287 BAD_CAST "dmID");
8288 if (!dmid_element) {
8289 xmlFreeNode(dmid_text);
8290 goto serialization_failed;
8293 xmlNodePtr dmid_element_with_text =
8294 xmlAddChild(dmid_element, dmid_text);
8295 if (!dmid_element_with_text) {
8296 xmlFreeNode(dmid_element);
8297 xmlFreeNode(dmid_text);
8298 goto serialization_failed;
8301 node = xmlAddPrevSibling(envelope->childern,
8302 dmid_element_with_text);
8303 if (!node) {
8304 xmlFreeNodeList(dmid_element_with_text);
8305 goto serialization_failed;
8309 /* Serialize message with ID into raw */
8310 /*buffer = serialize_element(envelope)*/
8311 /* }
8313 serialization_failed:
8317 /* Clean up */
8318 xmlXPathFreeObject(result);
8319 xmlXPathFreeContext(xpath_ctx);
8321 free(code);
8322 free(message);
8323 xmlFreeDoc(response);
8324 xmlFreeNode(request);
8326 if (!err)
8327 isds_log(ILF_ISDS, ILL_DEBUG,
8328 _("CreateMessage request processed by server "
8329 "successfully.\n"));
8330 #else /* not HAVE_LIBCURL */
8331 err = IE_NOTSUP;
8332 #endif
8334 return err;
8338 /* Send a message via ISDS to a multiple recipients
8339 * @context is session context
8340 * @outgoing_message is message to send; Some members are mandatory,
8341 * some are optional and some are irrelevant (especially data
8342 * about sender). Data about recipient will be substituted by ISDS from
8343 * @copies. Included pointer to isds_list documents must
8344 * contain at least one document of FILEMETATYPE_MAIN.
8345 * @copies is list of isds_message_copy structures addressing all desired
8346 * recipients. This is read-write structure, some members will be filled with
8347 * valid data from ISDS (message IDs, error codes, error descriptions).
8348 * @return
8349 * ISDS_SUCCESS if all messages have been sent
8350 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8351 * succeeded messages can be identified by copies->data->error),
8352 * or other error code if something other goes wrong. */
8353 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8354 const struct isds_message *outgoing_message,
8355 struct isds_list *copies) {
8357 isds_error err = IE_SUCCESS;
8358 #if HAVE_LIBCURL
8359 isds_error append_err;
8360 xmlNsPtr isds_ns = NULL;
8361 xmlNodePtr request = NULL, recipients, recipient, node;
8362 struct isds_list *item;
8363 struct isds_message_copy *copy;
8364 xmlDocPtr response = NULL;
8365 xmlChar *code = NULL, *message = NULL;
8366 xmlXPathContextPtr xpath_ctx = NULL;
8367 xmlXPathObjectPtr result = NULL;
8368 xmlChar *string = NULL;
8369 int i;
8370 #endif
8372 if (!context) return IE_INVALID_CONTEXT;
8373 zfree(context->long_message);
8374 if (!outgoing_message || !copies) return IE_INVAL;
8376 #if HAVE_LIBCURL
8377 /* Check if connection is established
8378 * TODO: This check should be done downstairs. */
8379 if (!context->curl) return IE_CONNECTION_CLOSED;
8382 /* Build CreateMultipleMessage request */
8383 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
8384 if (!request) {
8385 isds_log_message(context,
8386 _("Could not build CreateMultipleMessage request"));
8387 return IE_ERROR;
8389 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8390 if(!isds_ns) {
8391 isds_log_message(context, _("Could not create ISDS name space"));
8392 xmlFreeNode(request);
8393 return IE_ERROR;
8395 xmlSetNs(request, isds_ns);
8398 /* Build recipients */
8399 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8400 if (!recipients) {
8401 isds_log_message(context, _("Could not add dmRecipients child to "
8402 "CreateMultipleMessage element"));
8403 xmlFreeNode(request);
8404 return IE_ERROR;
8407 /* Insert each recipient */
8408 for (item = copies; item; item = item->next) {
8409 copy = (struct isds_message_copy *) item->data;
8410 if (!copy) {
8411 isds_log_message(context,
8412 _("`copies' list item contains empty data"));
8413 err = IE_INVAL;
8414 goto leave;
8417 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
8418 if (!recipient) {
8419 isds_log_message(context, _("Could not add dmRecipient child to "
8420 "dmRecipients element"));
8421 err = IE_ERROR;
8422 goto leave;
8425 if (!copy->dbIDRecipient) {
8426 isds_log_message(context,
8427 _("Message copy is missing recipient box identifier"));
8428 err = IE_INVAL;
8429 goto leave;
8431 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
8432 INSERT_STRING(recipient, "dmRecipientOrgUnit",
8433 copy->dmRecipientOrgUnit);
8434 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
8435 copy->dmRecipientOrgUnitNum, string);
8436 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
8439 /* Append envelope and files */
8440 err = insert_envelope_files(context, outgoing_message, request, 0);
8441 if (err) goto leave;
8444 isds_log(ILF_ISDS, ILL_DEBUG,
8445 _("Sending CreateMultipleMessage request to ISDS\n"));
8447 /* Sent request */
8448 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8449 if (err) {
8450 isds_log(ILF_ISDS, ILL_DEBUG,
8451 _("Processing ISDS response on CreateMultipleMessage "
8452 "request failed\n"));
8453 goto leave;
8456 /* Check for response status */
8457 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8458 &code, &message, NULL);
8459 if (err) {
8460 isds_log(ILF_ISDS, ILL_DEBUG,
8461 _("ISDS response on CreateMultipleMessage request "
8462 "is missing status\n"));
8463 goto leave;
8466 /* Request processed, but some copies failed */
8467 if (!xmlStrcmp(code, BAD_CAST "0004")) {
8468 char *box_id_locale =
8469 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8470 char *code_locale = _isds_utf82locale((char*)code);
8471 char *message_locale = _isds_utf82locale((char*)message);
8472 isds_log(ILF_ISDS, ILL_DEBUG,
8473 _("Server did accept message for multiple recipients "
8474 "on CreateMultipleMessage request but delivery to "
8475 "some of them failed (code=%s, message=%s)\n"),
8476 box_id_locale, code_locale, message_locale);
8477 isds_log_message(context, message_locale);
8478 free(box_id_locale);
8479 free(code_locale);
8480 free(message_locale);
8481 err = IE_PARTIAL_SUCCESS;
8484 /* Request refused by server as whole */
8485 else if (xmlStrcmp(code, BAD_CAST "0000")) {
8486 char *box_id_locale =
8487 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8488 char *code_locale = _isds_utf82locale((char*)code);
8489 char *message_locale = _isds_utf82locale((char*)message);
8490 isds_log(ILF_ISDS, ILL_DEBUG,
8491 _("Server did not accept message for multiple recipients "
8492 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
8493 box_id_locale, code_locale, message_locale);
8494 isds_log_message(context, message_locale);
8495 free(box_id_locale);
8496 free(code_locale);
8497 free(message_locale);
8498 err = IE_ISDS;
8499 goto leave;
8503 /* Extract data */
8504 xpath_ctx = xmlXPathNewContext(response);
8505 if (!xpath_ctx) {
8506 err = IE_ERROR;
8507 goto leave;
8509 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8510 err = IE_ERROR;
8511 goto leave;
8513 result = xmlXPathEvalExpression(
8514 BAD_CAST "/isds:CreateMultipleMessageResponse"
8515 "/isds:dmMultipleStatus/isds:dmSingleStatus",
8516 xpath_ctx);
8517 if (!result) {
8518 err = IE_ERROR;
8519 goto leave;
8521 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8522 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
8523 err = IE_ISDS;
8524 goto leave;
8527 /* Extract message ID and delivery status for each copy */
8528 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
8529 item = item->next, i++) {
8530 copy = (struct isds_message_copy *) item->data;
8531 xpath_ctx->node = result->nodesetval->nodeTab[i];
8533 append_err = append_TMStatus(context, copy, xpath_ctx);
8534 if (append_err) {
8535 err = append_err;
8536 goto leave;
8539 if (item || i < result->nodesetval->nodeNr) {
8540 isds_printf_message(context, _("ISDS returned unexpected number of "
8541 "message copy delivery states: %d"),
8542 result->nodesetval->nodeNr);
8543 err = IE_ISDS;
8544 goto leave;
8548 leave:
8549 /* Clean up */
8550 free(string);
8551 xmlXPathFreeObject(result);
8552 xmlXPathFreeContext(xpath_ctx);
8554 free(code);
8555 free(message);
8556 xmlFreeDoc(response);
8557 xmlFreeNode(request);
8559 if (!err)
8560 isds_log(ILF_ISDS, ILL_DEBUG,
8561 _("CreateMultipleMessageResponse request processed by server "
8562 "successfully.\n"));
8563 #else /* not HAVE_LIBCURL */
8564 err = IE_NOTSUP;
8565 #endif
8567 return err;
8571 /* Get list of messages. This is common core for getting sent or received
8572 * messages.
8573 * Any criterion argument can be NULL, if you don't care about it.
8574 * @context is session context. Must not be NULL.
8575 * @outgoing_direction is true if you want list of outgoing messages,
8576 * it's false if you want incoming messages.
8577 * @from_time is minimal time and date of message sending inclusive.
8578 * @to_time is maximal time and date of message sending inclusive
8579 * @organization_unit_number is number of sender/recipient respectively.
8580 * @status_filter is bit field of isds_message_status values. Use special
8581 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8582 * all values, you can use bit-wise arithmetic if you want.)
8583 * @offset is index of first message we are interested in. First message is 1.
8584 * Set to 0 (or 1) if you don't care.
8585 * @number is maximal length of list you want to get as input value, outputs
8586 * number of messages matching these criteria. Can be NULL if you don't care
8587 * (applies to output value either).
8588 * @messages is automatically reallocated list of isds_message's. Be ware that
8589 * it returns only brief overview (envelope and some other fields) about each
8590 * message, not the complete message. FIXME: Specify exact fields.
8591 * The list is sorted by delivery time in ascending order.
8592 * Use NULL if you don't care about don't need the data (useful if you want to
8593 * know only the @number). If you provide &NULL, list will be allocated on
8594 * heap, if you provide pointer to non-NULL, list will be freed automatically
8595 * at first. Also in case of error the list will be NULLed.
8596 * @return IE_SUCCESS or appropriate error code. */
8597 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
8598 _Bool outgoing_direction,
8599 const struct timeval *from_time, const struct timeval *to_time,
8600 const long int *organization_unit_number,
8601 const unsigned int status_filter,
8602 const unsigned long int offset, unsigned long int *number,
8603 struct isds_list **messages) {
8605 isds_error err = IE_SUCCESS;
8606 #if HAVE_LIBCURL
8607 xmlNsPtr isds_ns = NULL;
8608 xmlNodePtr request = NULL, node;
8609 xmlDocPtr response = NULL;
8610 xmlChar *code = NULL, *message = NULL;
8611 xmlXPathContextPtr xpath_ctx = NULL;
8612 xmlXPathObjectPtr result = NULL;
8613 xmlChar *string = NULL;
8614 long unsigned int count = 0;
8615 #endif
8617 if (!context) return IE_INVALID_CONTEXT;
8618 zfree(context->long_message);
8620 /* Free former message list if any */
8621 if (messages) isds_list_free(messages);
8623 #if HAVE_LIBCURL
8624 /* Check if connection is established
8625 * TODO: This check should be done downstairs. */
8626 if (!context->curl) return IE_CONNECTION_CLOSED;
8628 /* Build GetListOf*Messages request */
8629 request = xmlNewNode(NULL,
8630 (outgoing_direction) ?
8631 BAD_CAST "GetListOfSentMessages" :
8632 BAD_CAST "GetListOfReceivedMessages"
8634 if (!request) {
8635 isds_log_message(context,
8636 (outgoing_direction) ?
8637 _("Could not build GetListOfSentMessages request") :
8638 _("Could not build GetListOfReceivedMessages request")
8640 return IE_ERROR;
8642 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8643 if(!isds_ns) {
8644 isds_log_message(context, _("Could not create ISDS name space"));
8645 xmlFreeNode(request);
8646 return IE_ERROR;
8648 xmlSetNs(request, isds_ns);
8651 if (from_time) {
8652 err = timeval2timestring(from_time, &string);
8653 if (err) goto leave;
8655 INSERT_STRING(request, "dmFromTime", string);
8656 free(string); string = NULL;
8658 if (to_time) {
8659 err = timeval2timestring(to_time, &string);
8660 if (err) goto leave;
8662 INSERT_STRING(request, "dmToTime", string);
8663 free(string); string = NULL;
8665 if (outgoing_direction) {
8666 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
8667 organization_unit_number, string);
8668 } else {
8669 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
8670 organization_unit_number, string);
8673 if (status_filter > MESSAGESTATE_ANY) {
8674 isds_printf_message(context,
8675 _("Invalid message state filter value: %ld"), status_filter);
8676 err = IE_INVAL;
8677 goto leave;
8679 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
8681 if (offset > 0 ) {
8682 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
8683 } else {
8684 INSERT_STRING(request, "dmOffset", "1");
8687 /* number 0 means no limit */
8688 if (number && *number == 0) {
8689 INSERT_STRING(request, "dmLimit", NULL);
8690 } else {
8691 INSERT_ULONGINT(request, "dmLimit", number, string);
8695 isds_log(ILF_ISDS, ILL_DEBUG,
8696 (outgoing_direction) ?
8697 _("Sending GetListOfSentMessages request to ISDS\n") :
8698 _("Sending GetListOfReceivedMessages request to ISDS\n")
8701 /* Sent request */
8702 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
8703 xmlFreeNode(request); request = NULL;
8705 if (err) {
8706 isds_log(ILF_ISDS, ILL_DEBUG,
8707 (outgoing_direction) ?
8708 _("Processing ISDS response on GetListOfSentMessages "
8709 "request failed\n") :
8710 _("Processing ISDS response on GetListOfReceivedMessages "
8711 "request failed\n")
8713 goto leave;
8716 /* Check for response status */
8717 err = isds_response_status(context, SERVICE_DM_INFO, response,
8718 &code, &message, NULL);
8719 if (err) {
8720 isds_log(ILF_ISDS, ILL_DEBUG,
8721 (outgoing_direction) ?
8722 _("ISDS response on GetListOfSentMessages request "
8723 "is missing status\n") :
8724 _("ISDS response on GetListOfReceivedMessages request "
8725 "is missing status\n")
8727 goto leave;
8730 /* Request processed, but nothing found */
8731 if (xmlStrcmp(code, BAD_CAST "0000")) {
8732 char *code_locale = _isds_utf82locale((char*)code);
8733 char *message_locale = _isds_utf82locale((char*)message);
8734 isds_log(ILF_ISDS, ILL_DEBUG,
8735 (outgoing_direction) ?
8736 _("Server refused GetListOfSentMessages request "
8737 "(code=%s, message=%s)\n") :
8738 _("Server refused GetListOfReceivedMessages request "
8739 "(code=%s, message=%s)\n"),
8740 code_locale, message_locale);
8741 isds_log_message(context, message_locale);
8742 free(code_locale);
8743 free(message_locale);
8744 err = IE_ISDS;
8745 goto leave;
8749 /* Extract data */
8750 xpath_ctx = xmlXPathNewContext(response);
8751 if (!xpath_ctx) {
8752 err = IE_ERROR;
8753 goto leave;
8755 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8756 err = IE_ERROR;
8757 goto leave;
8759 result = xmlXPathEvalExpression(
8760 (outgoing_direction) ?
8761 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
8762 "isds:dmRecords/isds:dmRecord" :
8763 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
8764 "isds:dmRecords/isds:dmRecord",
8765 xpath_ctx);
8766 if (!result) {
8767 err = IE_ERROR;
8768 goto leave;
8771 /* Fill output arguments in */
8772 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8773 struct isds_envelope *envelope;
8774 struct isds_list *item = NULL, *last_item = NULL;
8776 for (count = 0; count < result->nodesetval->nodeNr; count++) {
8777 /* Create new message */
8778 item = calloc(1, sizeof(*item));
8779 if (!item) {
8780 err = IE_NOMEM;
8781 goto leave;
8783 item->destructor = (void(*)(void**)) &isds_message_free;
8784 item->data = calloc(1, sizeof(struct isds_message));
8785 if (!item->data) {
8786 isds_list_free(&item);
8787 err = IE_NOMEM;
8788 goto leave;
8791 /* Extract envelope data */
8792 xpath_ctx->node = result->nodesetval->nodeTab[count];
8793 envelope = NULL;
8794 err = extract_DmRecord(context, &envelope, xpath_ctx);
8795 if (err) {
8796 isds_list_free(&item);
8797 goto leave;
8800 /* Attach extracted envelope */
8801 ((struct isds_message *) item->data)->envelope = envelope;
8803 /* Append new message into the list */
8804 if (!*messages) {
8805 *messages = last_item = item;
8806 } else {
8807 last_item->next = item;
8808 last_item = item;
8812 if (number) *number = count;
8814 leave:
8815 if (err) {
8816 isds_list_free(messages);
8819 free(string);
8820 xmlXPathFreeObject(result);
8821 xmlXPathFreeContext(xpath_ctx);
8823 free(code);
8824 free(message);
8825 xmlFreeDoc(response);
8826 xmlFreeNode(request);
8828 if (!err)
8829 isds_log(ILF_ISDS, ILL_DEBUG,
8830 (outgoing_direction) ?
8831 _("GetListOfSentMessages request processed by server "
8832 "successfully.\n") :
8833 _("GetListOfReceivedMessages request processed by server "
8834 "successfully.\n")
8836 #else /* not HAVE_LIBCURL */
8837 err = IE_NOTSUP;
8838 #endif
8839 return err;
8843 /* Get list of outgoing (already sent) messages.
8844 * Any criterion argument can be NULL, if you don't care about it.
8845 * @context is session context. Must not be NULL.
8846 * @from_time is minimal time and date of message sending inclusive.
8847 * @to_time is maximal time and date of message sending inclusive
8848 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
8849 * @status_filter is bit field of isds_message_status values. Use special
8850 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8851 * all values, you can use bit-wise arithmetic if you want.)
8852 * @offset is index of first message we are interested in. First message is 1.
8853 * Set to 0 (or 1) if you don't care.
8854 * @number is maximal length of list you want to get as input value, outputs
8855 * number of messages matching these criteria. Can be NULL if you don't care
8856 * (applies to output value either).
8857 * @messages is automatically reallocated list of isds_message's. Be ware that
8858 * it returns only brief overview (envelope and some other fields) about each
8859 * message, not the complete message. FIXME: Specify exact fields.
8860 * The list is sorted by delivery time in ascending order.
8861 * Use NULL if you don't care about the meta data (useful if you want to know
8862 * only the @number). If you provide &NULL, list will be allocated on heap,
8863 * if you provide pointer to non-NULL, list will be freed automatically at
8864 * first. Also in case of error the list will be NULLed.
8865 * @return IE_SUCCESS or appropriate error code. */
8866 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
8867 const struct timeval *from_time, const struct timeval *to_time,
8868 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
8869 const unsigned long int offset, unsigned long int *number,
8870 struct isds_list **messages) {
8872 return isds_get_list_of_messages(
8873 context, 1,
8874 from_time, to_time, dmSenderOrgUnitNum, status_filter,
8875 offset, number,
8876 messages);
8880 /* Get list of incoming (addressed to you) messages.
8881 * Any criterion argument can be NULL, if you don't care about it.
8882 * @context is session context. Must not be NULL.
8883 * @from_time is minimal time and date of message sending inclusive.
8884 * @to_time is maximal time and date of message sending inclusive
8885 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
8886 * @status_filter is bit field of isds_message_status values. Use special
8887 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8888 * all values, you can use bit-wise arithmetic if you want.)
8889 * @offset is index of first message we are interested in. First message is 1.
8890 * Set to 0 (or 1) if you don't care.
8891 * @number is maximal length of list you want to get as input value, outputs
8892 * number of messages matching these criteria. Can be NULL if you don't care
8893 * (applies to output value either).
8894 * @messages is automatically reallocated list of isds_message's. Be ware that
8895 * it returns only brief overview (envelope and some other fields) about each
8896 * message, not the complete message. FIXME: Specify exact fields.
8897 * Use NULL if you don't care about the meta data (useful if you want to know
8898 * only the @number). If you provide &NULL, list will be allocated on heap,
8899 * if you provide pointer to non-NULL, list will be freed automatically at
8900 * first. Also in case of error the list will be NULLed.
8901 * @return IE_SUCCESS or appropriate error code. */
8902 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
8903 const struct timeval *from_time, const struct timeval *to_time,
8904 const long int *dmRecipientOrgUnitNum,
8905 const unsigned int status_filter,
8906 const unsigned long int offset, unsigned long int *number,
8907 struct isds_list **messages) {
8909 return isds_get_list_of_messages(
8910 context, 0,
8911 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
8912 offset, number,
8913 messages);
8917 /* Get list of sent message state changes.
8918 * Any criterion argument can be NULL, if you don't care about it.
8919 * @context is session context. Must not be NULL.
8920 * @from_time is minimal time and date of status changes inclusive
8921 * @to_time is maximal time and date of status changes inclusive
8922 * @changed_states is automatically reallocated list of
8923 * isds_message_status_change's. If you provide &NULL, list will be allocated
8924 * on heap, if you provide pointer to non-NULL, list will be freed
8925 * automatically at first. Also in case of error the list will be NULLed.
8926 * XXX: The list item ordering is not specified.
8927 * XXX: Server provides only `recent' changes.
8928 * @return IE_SUCCESS or appropriate error code. */
8929 isds_error isds_get_list_of_sent_message_state_changes(
8930 struct isds_ctx *context,
8931 const struct timeval *from_time, const struct timeval *to_time,
8932 struct isds_list **changed_states) {
8934 isds_error err = IE_SUCCESS;
8935 #if HAVE_LIBCURL
8936 xmlNsPtr isds_ns = NULL;
8937 xmlNodePtr request = NULL, node;
8938 xmlDocPtr response = NULL;
8939 xmlXPathContextPtr xpath_ctx = NULL;
8940 xmlXPathObjectPtr result = NULL;
8941 xmlChar *string = NULL;
8942 long unsigned int count = 0;
8943 #endif
8945 if (!context) return IE_INVALID_CONTEXT;
8946 zfree(context->long_message);
8948 /* Free former message list if any */
8949 isds_list_free(changed_states);
8951 #if HAVE_LIBCURL
8952 /* Check if connection is established
8953 * TODO: This check should be done downstairs. */
8954 if (!context->curl) return IE_CONNECTION_CLOSED;
8956 /* Build GetMessageStateChanges request */
8957 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
8958 if (!request) {
8959 isds_log_message(context,
8960 _("Could not build GetMessageStateChanges request"));
8961 return IE_ERROR;
8963 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8964 if(!isds_ns) {
8965 isds_log_message(context, _("Could not create ISDS name space"));
8966 xmlFreeNode(request);
8967 return IE_ERROR;
8969 xmlSetNs(request, isds_ns);
8972 if (from_time) {
8973 err = timeval2timestring(from_time, &string);
8974 if (err) goto leave;
8976 INSERT_STRING(request, "dmFromTime", string);
8977 zfree(string);
8979 if (to_time) {
8980 err = timeval2timestring(to_time, &string);
8981 if (err) goto leave;
8983 INSERT_STRING(request, "dmToTime", string);
8984 zfree(string);
8987 /* Sent request */
8988 err = send_destroy_request_check_response(context,
8989 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
8990 &response, NULL, NULL);
8991 if (err) goto leave;
8994 /* Extract data */
8995 xpath_ctx = xmlXPathNewContext(response);
8996 if (!xpath_ctx) {
8997 err = IE_ERROR;
8998 goto leave;
9000 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9001 err = IE_ERROR;
9002 goto leave;
9004 result = xmlXPathEvalExpression(
9005 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9006 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9007 if (!result) {
9008 err = IE_ERROR;
9009 goto leave;
9012 /* Fill output arguments in */
9013 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9014 struct isds_list *item = NULL, *last_item = NULL;
9016 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9017 /* Create new status change */
9018 item = calloc(1, sizeof(*item));
9019 if (!item) {
9020 err = IE_NOMEM;
9021 goto leave;
9023 item->destructor =
9024 (void(*)(void**)) &isds_message_status_change_free;
9026 /* Extract message status change */
9027 xpath_ctx->node = result->nodesetval->nodeTab[count];
9028 err = extract_StateChangesRecord(context,
9029 (struct isds_message_status_change **) &item->data,
9030 xpath_ctx);
9031 if (err) {
9032 isds_list_free(&item);
9033 goto leave;
9036 /* Append new message status change into the list */
9037 if (!*changed_states) {
9038 *changed_states = last_item = item;
9039 } else {
9040 last_item->next = item;
9041 last_item = item;
9046 leave:
9047 if (err) {
9048 isds_list_free(changed_states);
9051 free(string);
9052 xmlXPathFreeObject(result);
9053 xmlXPathFreeContext(xpath_ctx);
9054 xmlFreeDoc(response);
9055 xmlFreeNode(request);
9057 if (!err)
9058 isds_log(ILF_ISDS, ILL_DEBUG,
9059 _("GetMessageStateChanges request processed by server "
9060 "successfully.\n"));
9061 #else /* not HAVE_LIBCURL */
9062 err = IE_NOTSUP;
9063 #endif
9064 return err;
9068 #if HAVE_LIBCURL
9069 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9070 * code
9071 * @context is session context
9072 * @service is ISDS WS service handler
9073 * @service_name is name of SERVICE_DM_OPERATIONS
9074 * @message_id is message ID to send as service argument to ISDS
9075 * @response is reallocated server SOAP body response as XML document
9076 * @raw_response is reallocated bit stream with response body. Use
9077 * NULL if you don't care
9078 * @raw_response_length is size of @raw_response in bytes
9079 * @code is reallocated ISDS status code
9080 * @status_message is reallocated ISDS status message
9081 * @return error coded from lower layer, context message will be set up
9082 * appropriately. */
9083 static isds_error build_send_check_message_request(struct isds_ctx *context,
9084 const isds_service service, const xmlChar *service_name,
9085 const char *message_id,
9086 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9087 xmlChar **code, xmlChar **status_message) {
9089 isds_error err = IE_SUCCESS;
9090 char *service_name_locale = NULL, *message_id_locale = NULL;
9091 xmlNodePtr request = NULL, node;
9092 xmlNsPtr isds_ns = NULL;
9094 if (!context) return IE_INVALID_CONTEXT;
9095 if (!service_name || !message_id) return IE_INVAL;
9096 if (!response || !code || !status_message) return IE_INVAL;
9097 if (!raw_response_length && raw_response) return IE_INVAL;
9099 /* Free output argument */
9100 xmlFreeDoc(*response); *response = NULL;
9101 if (raw_response) zfree(*raw_response);
9102 zfree(*code);
9103 zfree(*status_message);
9106 /* Check if connection is established
9107 * TODO: This check should be done downstairs. */
9108 if (!context->curl) return IE_CONNECTION_CLOSED;
9110 service_name_locale = _isds_utf82locale((char*)service_name);
9111 message_id_locale = _isds_utf82locale(message_id);
9112 if (!service_name_locale || !message_id_locale) {
9113 err = IE_NOMEM;
9114 goto leave;
9117 /* Build request */
9118 request = xmlNewNode(NULL, service_name);
9119 if (!request) {
9120 isds_printf_message(context,
9121 _("Could not build %s request for %s message ID"),
9122 service_name_locale, message_id_locale);
9123 err = IE_ERROR;
9124 goto leave;
9126 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9127 if(!isds_ns) {
9128 isds_log_message(context, _("Could not create ISDS name space"));
9129 err = IE_ERROR;
9130 goto leave;
9132 xmlSetNs(request, isds_ns);
9135 /* Add requested ID */
9136 err = validate_message_id_length(context, (xmlChar *) message_id);
9137 if (err) goto leave;
9138 INSERT_STRING(request, "dmID", message_id);
9141 isds_log(ILF_ISDS, ILL_DEBUG,
9142 _("Sending %s request for %s message ID to ISDS\n"),
9143 service_name_locale, message_id_locale);
9145 /* Send request */
9146 err = isds(context, service, request, response,
9147 raw_response, raw_response_length);
9148 xmlFreeNode(request); request = NULL;
9150 if (err) {
9151 isds_log(ILF_ISDS, ILL_DEBUG,
9152 _("Processing ISDS response on %s request failed\n"),
9153 service_name_locale);
9154 goto leave;
9157 /* Check for response status */
9158 err = isds_response_status(context, service, *response,
9159 code, status_message, NULL);
9160 if (err) {
9161 isds_log(ILF_ISDS, ILL_DEBUG,
9162 _("ISDS response on %s request is missing status\n"),
9163 service_name_locale);
9164 goto leave;
9167 /* Request processed, but nothing found */
9168 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9169 char *code_locale = _isds_utf82locale((char*) *code);
9170 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9171 isds_log(ILF_ISDS, ILL_DEBUG,
9172 _("Server refused %s request for %s message ID "
9173 "(code=%s, message=%s)\n"),
9174 service_name_locale, message_id_locale,
9175 code_locale, status_message_locale);
9176 isds_log_message(context, status_message_locale);
9177 free(code_locale);
9178 free(status_message_locale);
9179 err = IE_ISDS;
9180 goto leave;
9183 leave:
9184 free(message_id_locale);
9185 free(service_name_locale);
9186 xmlFreeNode(request);
9187 return err;
9191 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9192 * signed data and free ISDS response.
9193 * @context is session context
9194 * @message_id is UTF-8 encoded message ID for logging purpose
9195 * @response is parsed XML document. It will be freed and NULLed in the middle
9196 * of function run to save memory. This is not guaranteed in case of error.
9197 * @request_name is name of ISDS request used to construct response root
9198 * element name and for logging purpose.
9199 * @raw is reallocated output buffer with DER encoded CMS data
9200 * @raw_length is size of @raw buffer in bytes
9201 * @returns standard error codes, in case of error, @raw will be freed and
9202 * NULLed, @response sometimes. */
9203 static isds_error find_extract_signed_data_free_response(
9204 struct isds_ctx *context, const xmlChar *message_id,
9205 xmlDocPtr *response, const xmlChar *request_name,
9206 void **raw, size_t *raw_length) {
9208 isds_error err = IE_SUCCESS;
9209 char *xpath_expression = NULL;
9210 xmlXPathContextPtr xpath_ctx = NULL;
9211 xmlXPathObjectPtr result = NULL;
9212 char *encoded_structure = NULL;
9214 if (!context) return IE_INVALID_CONTEXT;
9215 if (!raw) return IE_INVAL;
9216 zfree(*raw);
9217 if (!message_id || !response || !*response || !request_name || !raw_length)
9218 return IE_INVAL;
9220 /* Build XPath expression */
9221 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9222 "Response/isds:dmSignature");
9223 if (!xpath_expression) return IE_NOMEM;
9225 /* Extract data */
9226 xpath_ctx = xmlXPathNewContext(*response);
9227 if (!xpath_ctx) {
9228 err = IE_ERROR;
9229 goto leave;
9231 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9232 err = IE_ERROR;
9233 goto leave;
9235 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9236 if (!result) {
9237 err = IE_ERROR;
9238 goto leave;
9240 /* Empty response */
9241 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9242 char *message_id_locale = _isds_utf82locale((char*) message_id);
9243 isds_printf_message(context,
9244 _("Server did not return any signed data for message ID `%s' "
9245 "on %s request"),
9246 message_id_locale, request_name);
9247 free(message_id_locale);
9248 err = IE_ISDS;
9249 goto leave;
9251 /* More responses */
9252 if (result->nodesetval->nodeNr > 1) {
9253 char *message_id_locale = _isds_utf82locale((char*) message_id);
9254 isds_printf_message(context,
9255 _("Server did return more signed data for message ID `%s' "
9256 "on %s request"),
9257 message_id_locale, request_name);
9258 free(message_id_locale);
9259 err = IE_ISDS;
9260 goto leave;
9262 /* One response */
9263 xpath_ctx->node = result->nodesetval->nodeTab[0];
9265 /* Extract PKCS#7 structure */
9266 EXTRACT_STRING(".", encoded_structure);
9267 if (!encoded_structure) {
9268 isds_log_message(context, _("dmSignature element is empty"));
9271 /* Here we have delivery info as standalone CMS in encoded_structure.
9272 * We don't need any other data, free them: */
9273 xmlXPathFreeObject(result); result = NULL;
9274 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9275 xmlFreeDoc(*response); *response = NULL;
9278 /* Decode PKCS#7 to DER format */
9279 *raw_length = _isds_b64decode(encoded_structure, raw);
9280 if (*raw_length == (size_t) -1) {
9281 isds_log_message(context,
9282 _("Error while Base64-decoding PKCS#7 structure"));
9283 err = IE_ERROR;
9284 goto leave;
9287 leave:
9288 if (err) {
9289 zfree(*raw);
9290 raw_length = 0;
9293 free(encoded_structure);
9294 xmlXPathFreeObject(result);
9295 xmlXPathFreeContext(xpath_ctx);
9296 free(xpath_expression);
9298 return err;
9300 #endif /* HAVE_LIBCURL */
9303 /* Download incoming message envelope identified by ID.
9304 * @context is session context
9305 * @message_id is message identifier (you can get them from
9306 * isds_get_list_of_received_messages())
9307 * @message is automatically reallocated message retrieved from ISDS.
9308 * It will miss documents per se. Use isds_get_received_message(), if you are
9309 * interested in documents (content) too.
9310 * Returned hash and timestamp require documents to be verifiable. */
9311 isds_error isds_get_received_envelope(struct isds_ctx *context,
9312 const char *message_id, struct isds_message **message) {
9314 isds_error err = IE_SUCCESS;
9315 #if HAVE_LIBCURL
9316 xmlDocPtr response = NULL;
9317 xmlChar *code = NULL, *status_message = NULL;
9318 xmlXPathContextPtr xpath_ctx = NULL;
9319 xmlXPathObjectPtr result = NULL;
9320 #endif
9322 if (!context) return IE_INVALID_CONTEXT;
9323 zfree(context->long_message);
9325 /* Free former message if any */
9326 if (!message) return IE_INVAL;
9327 isds_message_free(message);
9329 #if HAVE_LIBCURL
9330 /* Do request and check for success */
9331 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9332 BAD_CAST "MessageEnvelopeDownload", message_id,
9333 &response, NULL, NULL, &code, &status_message);
9334 if (err) goto leave;
9336 /* Extract data */
9337 xpath_ctx = xmlXPathNewContext(response);
9338 if (!xpath_ctx) {
9339 err = IE_ERROR;
9340 goto leave;
9342 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9343 err = IE_ERROR;
9344 goto leave;
9346 result = xmlXPathEvalExpression(
9347 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9348 "isds:dmReturnedMessageEnvelope",
9349 xpath_ctx);
9350 if (!result) {
9351 err = IE_ERROR;
9352 goto leave;
9354 /* Empty response */
9355 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9356 char *message_id_locale = _isds_utf82locale((char*) message_id);
9357 isds_printf_message(context,
9358 _("Server did not return any envelope for ID `%s' "
9359 "on MessageEnvelopeDownload request"), message_id_locale);
9360 free(message_id_locale);
9361 err = IE_ISDS;
9362 goto leave;
9364 /* More envelops */
9365 if (result->nodesetval->nodeNr > 1) {
9366 char *message_id_locale = _isds_utf82locale((char*) message_id);
9367 isds_printf_message(context,
9368 _("Server did return more envelopes for ID `%s' "
9369 "on MessageEnvelopeDownload request"), message_id_locale);
9370 free(message_id_locale);
9371 err = IE_ISDS;
9372 goto leave;
9374 /* One message */
9375 xpath_ctx->node = result->nodesetval->nodeTab[0];
9377 /* Extract the envelope (= message without documents, hence 0) */
9378 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9379 if (err) goto leave;
9381 /* Save XML blob */
9382 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9383 &(*message)->raw_length);
9385 leave:
9386 if (err) {
9387 isds_message_free(message);
9390 xmlXPathFreeObject(result);
9391 xmlXPathFreeContext(xpath_ctx);
9393 free(code);
9394 free(status_message);
9395 if (!*message || !(*message)->xml) {
9396 xmlFreeDoc(response);
9399 if (!err)
9400 isds_log(ILF_ISDS, ILL_DEBUG,
9401 _("MessageEnvelopeDownload request processed by server "
9402 "successfully.\n")
9404 #else /* not HAVE_LIBCURL */
9405 err = IE_NOTSUP;
9406 #endif
9407 return err;
9411 /* Load delivery info of any format from buffer.
9412 * @context is session context
9413 * @raw_type advertises format of @buffer content. Only delivery info types
9414 * are accepted.
9415 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9416 * retrieve such data from message->raw after calling
9417 * isds_get_signed_delivery_info().
9418 * @length is length of buffer in bytes.
9419 * @message is automatically reallocated message parsed from @buffer.
9420 * @strategy selects how buffer will be attached into raw isds_message member.
9421 * */
9422 isds_error isds_load_delivery_info(struct isds_ctx *context,
9423 const isds_raw_type raw_type,
9424 const void *buffer, const size_t length,
9425 struct isds_message **message, const isds_buffer_strategy strategy) {
9427 isds_error err = IE_SUCCESS;
9428 message_ns_type message_ns;
9429 xmlDocPtr message_doc = NULL;
9430 xmlXPathContextPtr xpath_ctx = NULL;
9431 xmlXPathObjectPtr result = NULL;
9432 void *xml_stream = NULL;
9433 size_t xml_stream_length = 0;
9435 if (!context) return IE_INVALID_CONTEXT;
9436 zfree(context->long_message);
9437 if (!message) return IE_INVAL;
9438 isds_message_free(message);
9439 if (!buffer) return IE_INVAL;
9442 /* Select buffer format and extract XML from CMS*/
9443 switch (raw_type) {
9444 case RAWTYPE_DELIVERYINFO:
9445 message_ns = MESSAGE_NS_UNSIGNED;
9446 xml_stream = (void *) buffer;
9447 xml_stream_length = length;
9448 break;
9450 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
9451 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9452 xml_stream = (void *) buffer;
9453 xml_stream_length = length;
9454 break;
9456 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
9457 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9458 err = _isds_extract_cms_data(context, buffer, length,
9459 &xml_stream, &xml_stream_length);
9460 if (err) goto leave;
9461 break;
9463 default:
9464 isds_log_message(context, _("Bad raw delivery representation type"));
9465 return IE_INVAL;
9466 break;
9469 isds_log(ILF_ISDS, ILL_DEBUG,
9470 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
9471 xml_stream_length, xml_stream);
9473 /* Convert delivery info XML stream into XPath context */
9474 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9475 if (!message_doc) {
9476 err = IE_XML;
9477 goto leave;
9479 xpath_ctx = xmlXPathNewContext(message_doc);
9480 if (!xpath_ctx) {
9481 err = IE_ERROR;
9482 goto leave;
9484 /* XXX: Name spaces mangled for signed delivery info:
9485 * http://isds.czechpoint.cz/v20/delivery:
9487 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
9488 * <q:dmDelivery>
9489 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9490 * <p:dmID>170272</p:dmID>
9491 * ...
9492 * </p:dmDm>
9493 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9494 * ...
9495 * </q:dmEvents>...</q:dmEvents>
9496 * </q:dmDelivery>
9497 * </q:GetDeliveryInfoResponse>
9498 * */
9499 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
9500 err = IE_ERROR;
9501 goto leave;
9503 result = xmlXPathEvalExpression(
9504 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
9505 xpath_ctx);
9506 if (!result) {
9507 err = IE_ERROR;
9508 goto leave;
9510 /* Empty delivery info */
9511 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9512 isds_printf_message(context,
9513 _("XML document is not sisds:dmDelivery document"));
9514 err = IE_ISDS;
9515 goto leave;
9517 /* More delivery info's */
9518 if (result->nodesetval->nodeNr > 1) {
9519 isds_printf_message(context,
9520 _("XML document has more sisds:dmDelivery elements"));
9521 err = IE_ISDS;
9522 goto leave;
9524 /* One delivery info */
9525 xpath_ctx->node = result->nodesetval->nodeTab[0];
9527 /* Extract the envelope (= message without documents, hence 0).
9528 * XXX: extract_TReturnedMessage() can obtain attachments size,
9529 * but delivery info carries none. It's coded as option elements,
9530 * so it should work. */
9531 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9532 if (err) goto leave;
9534 /* Extract events */
9535 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
9536 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
9537 if (err) { err = IE_ERROR; goto leave; }
9538 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
9539 if (err) goto leave;
9541 /* Append raw CMS structure into message */
9542 (*message)->raw_type = raw_type;
9543 switch (strategy) {
9544 case BUFFER_DONT_STORE:
9545 break;
9546 case BUFFER_COPY:
9547 (*message)->raw = malloc(length);
9548 if (!(*message)->raw) {
9549 err = IE_NOMEM;
9550 goto leave;
9552 memcpy((*message)->raw, buffer, length);
9553 (*message)->raw_length = length;
9554 break;
9555 case BUFFER_MOVE:
9556 (*message)->raw = (void *) buffer;
9557 (*message)->raw_length = length;
9558 break;
9559 default:
9560 err = IE_ENUM;
9561 goto leave;
9564 leave:
9565 if (err) {
9566 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
9567 isds_message_free(message);
9570 xmlXPathFreeObject(result);
9571 xmlXPathFreeContext(xpath_ctx);
9572 if (!*message || !(*message)->xml) {
9573 xmlFreeDoc(message_doc);
9575 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9577 if (!err)
9578 isds_log(ILF_ISDS, ILL_DEBUG,
9579 _("Delivery info loaded successfully.\n"));
9580 return err;
9584 /* Download signed delivery info-sheet of given message identified by ID.
9585 * @context is session context
9586 * @message_id is message identifier (you can get them from
9587 * isds_get_list_of_{sent,received}_messages())
9588 * @message is automatically reallocated message retrieved from ISDS.
9589 * It will miss documents per se. Use isds_get_signed_received_message(),
9590 * if you are interested in documents (content). OTOH, only this function
9591 * can get list events message has gone through. */
9592 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
9593 const char *message_id, struct isds_message **message) {
9595 isds_error err = IE_SUCCESS;
9596 #if HAVE_LIBCURL
9597 xmlDocPtr response = NULL;
9598 xmlChar *code = NULL, *status_message = NULL;
9599 void *raw = NULL;
9600 size_t raw_length = 0;
9601 #endif
9603 if (!context) return IE_INVALID_CONTEXT;
9604 zfree(context->long_message);
9606 /* Free former message if any */
9607 if (!message) return IE_INVAL;
9608 isds_message_free(message);
9610 #if HAVE_LIBCURL
9611 /* Do request and check for success */
9612 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9613 BAD_CAST "GetSignedDeliveryInfo", message_id,
9614 &response, NULL, NULL, &code, &status_message);
9615 if (err) goto leave;
9617 /* Find signed delivery info, extract it into raw and maybe free
9618 * response */
9619 err = find_extract_signed_data_free_response(context,
9620 (xmlChar *)message_id, &response,
9621 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
9622 if (err) goto leave;
9624 /* Parse delivery info */
9625 err = isds_load_delivery_info(context,
9626 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
9627 message, BUFFER_MOVE);
9628 if (err) goto leave;
9630 raw = NULL;
9632 leave:
9633 if (err) {
9634 isds_message_free(message);
9637 free(raw);
9638 free(code);
9639 free(status_message);
9640 xmlFreeDoc(response);
9642 if (!err)
9643 isds_log(ILF_ISDS, ILL_DEBUG,
9644 _("GetSignedDeliveryInfo request processed by server "
9645 "successfully.\n")
9647 #else /* not HAVE_LIBCURL */
9648 err = IE_NOTSUP;
9649 #endif
9650 return err;
9654 /* Download delivery info-sheet of given message identified by ID.
9655 * @context is session context
9656 * @message_id is message identifier (you can get them from
9657 * isds_get_list_of_{sent,received}_messages())
9658 * @message is automatically reallocated message retrieved from ISDS.
9659 * It will miss documents per se. Use isds_get_received_message(), if you are
9660 * interested in documents (content). OTOH, only this function can get list
9661 * of events message has gone through. */
9662 isds_error isds_get_delivery_info(struct isds_ctx *context,
9663 const char *message_id, struct isds_message **message) {
9665 isds_error err = IE_SUCCESS;
9666 #if HAVE_LIBCURL
9667 xmlDocPtr response = NULL;
9668 xmlChar *code = NULL, *status_message = NULL;
9669 xmlNodePtr delivery_node = NULL;
9670 void *raw = NULL;
9671 size_t raw_length = 0;
9672 #endif
9674 if (!context) return IE_INVALID_CONTEXT;
9675 zfree(context->long_message);
9677 /* Free former message if any */
9678 if (!message) return IE_INVAL;
9679 isds_message_free(message);
9681 #if HAVE_LIBCURL
9682 /* Do request and check for success */
9683 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9684 BAD_CAST "GetDeliveryInfo", message_id,
9685 &response, NULL, NULL, &code, &status_message);
9686 if (err) goto leave;
9689 /* Serialize delivery info */
9690 delivery_node = xmlDocGetRootElement(response);
9691 if (!delivery_node) {
9692 char *message_id_locale = _isds_utf82locale((char*) message_id);
9693 isds_printf_message(context,
9694 _("Server did not return any delivery info for ID `%s' "
9695 "on GetDeliveryInfo request"), message_id_locale);
9696 free(message_id_locale);
9697 err = IE_ISDS;
9698 goto leave;
9700 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
9701 if (err) goto leave;
9703 /* Parse delivery info */
9704 /* TODO: Here we parse the response second time. We could single delivery
9705 * parser from isds_load_delivery_info() to make things faster. */
9706 err = isds_load_delivery_info(context,
9707 RAWTYPE_DELIVERYINFO, raw, raw_length,
9708 message, BUFFER_MOVE);
9709 if (err) goto leave;
9711 raw = NULL;
9714 leave:
9715 if (err) {
9716 isds_message_free(message);
9719 free(raw);
9720 free(code);
9721 free(status_message);
9722 xmlFreeDoc(response);
9724 if (!err)
9725 isds_log(ILF_ISDS, ILL_DEBUG,
9726 _("GetDeliveryInfo request processed by server "
9727 "successfully.\n")
9729 #else /* not HAVE_LIBCURL */
9730 err = IE_NOTSUP;
9731 #endif
9732 return err;
9736 /* Download incoming message identified by ID.
9737 * @context is session context
9738 * @message_id is message identifier (you can get them from
9739 * isds_get_list_of_received_messages())
9740 * @message is automatically reallocated message retrieved from ISDS */
9741 isds_error isds_get_received_message(struct isds_ctx *context,
9742 const char *message_id, struct isds_message **message) {
9744 isds_error err = IE_SUCCESS;
9745 #if HAVE_LIBCURL
9746 xmlDocPtr response = NULL;
9747 void *xml_stream = NULL;
9748 size_t xml_stream_length;
9749 xmlChar *code = NULL, *status_message = NULL;
9750 xmlXPathContextPtr xpath_ctx = NULL;
9751 xmlXPathObjectPtr result = NULL;
9752 char *phys_path = NULL;
9753 size_t phys_start, phys_end;
9754 #endif
9756 if (!context) return IE_INVALID_CONTEXT;
9757 zfree(context->long_message);
9759 /* Free former message if any */
9760 if (NULL == message) return IE_INVAL;
9761 if (message) isds_message_free(message);
9763 #if HAVE_LIBCURL
9764 /* Do request and check for success */
9765 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
9766 BAD_CAST "MessageDownload", message_id,
9767 &response, &xml_stream, &xml_stream_length,
9768 &code, &status_message);
9769 if (err) goto leave;
9771 /* Extract data */
9772 xpath_ctx = xmlXPathNewContext(response);
9773 if (!xpath_ctx) {
9774 err = IE_ERROR;
9775 goto leave;
9777 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9778 err = IE_ERROR;
9779 goto leave;
9781 result = xmlXPathEvalExpression(
9782 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
9783 xpath_ctx);
9784 if (!result) {
9785 err = IE_ERROR;
9786 goto leave;
9788 /* Empty response */
9789 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9790 char *message_id_locale = _isds_utf82locale((char*) message_id);
9791 isds_printf_message(context,
9792 _("Server did not return any message for ID `%s' "
9793 "on MessageDownload request"), message_id_locale);
9794 free(message_id_locale);
9795 err = IE_ISDS;
9796 goto leave;
9798 /* More messages */
9799 if (result->nodesetval->nodeNr > 1) {
9800 char *message_id_locale = _isds_utf82locale((char*) message_id);
9801 isds_printf_message(context,
9802 _("Server did return more messages for ID `%s' "
9803 "on MessageDownload request"), message_id_locale);
9804 free(message_id_locale);
9805 err = IE_ISDS;
9806 goto leave;
9808 /* One message */
9809 xpath_ctx->node = result->nodesetval->nodeTab[0];
9811 /* Extract the message */
9812 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
9813 if (err) goto leave;
9815 /* Locate raw XML blob */
9816 phys_path = strdup(
9817 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
9818 PHYSXML_ELEMENT_SEPARATOR
9819 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
9820 PHYSXML_ELEMENT_SEPARATOR
9821 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
9823 if (!phys_path) {
9824 err = IE_NOMEM;
9825 goto leave;
9827 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
9828 phys_path, &phys_start, &phys_end);
9829 zfree(phys_path);
9830 if (err) {
9831 isds_log_message(context,
9832 _("Substring with isds:MessageDownloadResponse element "
9833 "could not be located in raw SOAP message"));
9834 goto leave;
9836 /* Save XML blob */
9837 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9838 &(*message)->raw_length);*/
9839 /* TODO: Store name space declarations from ancestors */
9840 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
9841 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
9842 (*message)->raw_length = phys_end - phys_start + 1;
9843 (*message)->raw = malloc((*message)->raw_length);
9844 if (!(*message)->raw) {
9845 err = IE_NOMEM;
9846 goto leave;
9848 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
9851 leave:
9852 if (err) {
9853 isds_message_free(message);
9856 free(phys_path);
9858 xmlXPathFreeObject(result);
9859 xmlXPathFreeContext(xpath_ctx);
9861 free(code);
9862 free(status_message);
9863 free(xml_stream);
9864 if (!*message || !(*message)->xml) {
9865 xmlFreeDoc(response);
9868 if (!err)
9869 isds_log(ILF_ISDS, ILL_DEBUG,
9870 _("MessageDownload request processed by server "
9871 "successfully.\n")
9873 #else /* not HAVE_LIBCURL */
9874 err = IE_NOTSUP;
9875 #endif
9876 return err;
9880 /* Load message of any type from buffer.
9881 * @context is session context
9882 * @raw_type defines content type of @buffer. Only message types are allowed.
9883 * @buffer is message raw representation. Format (CMS, plain signed,
9884 * message direction) is defined in @raw_type. You can retrieve such data
9885 * from message->raw after calling isds_get_[signed]{received,sent}_message().
9886 * @length is length of buffer in bytes.
9887 * @message is automatically reallocated message parsed from @buffer.
9888 * @strategy selects how buffer will be attached into raw isds_message member.
9889 * */
9890 isds_error isds_load_message(struct isds_ctx *context,
9891 const isds_raw_type raw_type, const void *buffer, const size_t length,
9892 struct isds_message **message, const isds_buffer_strategy strategy) {
9894 isds_error err = IE_SUCCESS;
9895 void *xml_stream = NULL;
9896 size_t xml_stream_length = 0;
9897 message_ns_type message_ns;
9898 xmlDocPtr message_doc = NULL;
9899 xmlXPathContextPtr xpath_ctx = NULL;
9900 xmlXPathObjectPtr result = NULL;
9902 if (!context) return IE_INVALID_CONTEXT;
9903 zfree(context->long_message);
9904 if (!message) return IE_INVAL;
9905 isds_message_free(message);
9906 if (!buffer) return IE_INVAL;
9909 /* Select buffer format and extract XML from CMS*/
9910 switch (raw_type) {
9911 case RAWTYPE_INCOMING_MESSAGE:
9912 message_ns = MESSAGE_NS_UNSIGNED;
9913 xml_stream = (void *) buffer;
9914 xml_stream_length = length;
9915 break;
9917 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
9918 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9919 xml_stream = (void *) buffer;
9920 xml_stream_length = length;
9921 break;
9923 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
9924 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9925 err = _isds_extract_cms_data(context, buffer, length,
9926 &xml_stream, &xml_stream_length);
9927 if (err) goto leave;
9928 break;
9930 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
9931 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9932 xml_stream = (void *) buffer;
9933 xml_stream_length = length;
9934 break;
9936 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
9937 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9938 err = _isds_extract_cms_data(context, buffer, length,
9939 &xml_stream, &xml_stream_length);
9940 if (err) goto leave;
9941 break;
9943 default:
9944 isds_log_message(context, _("Bad raw message representation type"));
9945 return IE_INVAL;
9946 break;
9949 isds_log(ILF_ISDS, ILL_DEBUG,
9950 _("Loading message:\n%.*s\nEnd of message\n"),
9951 xml_stream_length, xml_stream);
9953 /* Convert messages XML stream into XPath context */
9954 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9955 if (!message_doc) {
9956 err = IE_XML;
9957 goto leave;
9959 xpath_ctx = xmlXPathNewContext(message_doc);
9960 if (!xpath_ctx) {
9961 err = IE_ERROR;
9962 goto leave;
9964 /* XXX: Standard name space for unsigned incoming direction:
9965 * http://isds.czechpoint.cz/v20/
9967 * XXX: Name spaces mangled for signed outgoing direction:
9968 * http://isds.czechpoint.cz/v20/SentMessage:
9970 * <q:MessageDownloadResponse
9971 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
9972 * <q:dmReturnedMessage>
9973 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9974 * <p:dmID>151916</p:dmID>
9975 * ...
9976 * </p:dmDm>
9977 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9978 * ...
9979 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9980 * </q:dmReturnedMessage>
9981 * </q:MessageDownloadResponse>
9983 * XXX: Name spaces mangled for signed incoming direction:
9984 * http://isds.czechpoint.cz/v20/message:
9986 * <q:MessageDownloadResponse
9987 * xmlns:q="http://isds.czechpoint.cz/v20/message">
9988 * <q:dmReturnedMessage>
9989 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9990 * <p:dmID>151916</p:dmID>
9991 * ...
9992 * </p:dmDm>
9993 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9994 * ...
9995 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9996 * </q:dmReturnedMessage>
9997 * </q:MessageDownloadResponse>
9999 * Stupidity of ISDS developers is unlimited */
10000 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10001 err = IE_ERROR;
10002 goto leave;
10004 result = xmlXPathEvalExpression(
10005 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10006 xpath_ctx);
10007 if (!result) {
10008 err = IE_ERROR;
10009 goto leave;
10011 /* Empty message */
10012 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10013 isds_printf_message(context,
10014 _("XML document does not contain "
10015 "sisds:dmReturnedMessage element"));
10016 err = IE_ISDS;
10017 goto leave;
10019 /* More messages */
10020 if (result->nodesetval->nodeNr > 1) {
10021 isds_printf_message(context,
10022 _("XML document has more sisds:dmReturnedMessage elements"));
10023 err = IE_ISDS;
10024 goto leave;
10026 /* One message */
10027 xpath_ctx->node = result->nodesetval->nodeTab[0];
10029 /* Extract the message */
10030 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10031 if (err) goto leave;
10033 /* Append raw buffer into message */
10034 (*message)->raw_type = raw_type;
10035 switch (strategy) {
10036 case BUFFER_DONT_STORE:
10037 break;
10038 case BUFFER_COPY:
10039 (*message)->raw = malloc(length);
10040 if (!(*message)->raw) {
10041 err = IE_NOMEM;
10042 goto leave;
10044 memcpy((*message)->raw, buffer, length);
10045 (*message)->raw_length = length;
10046 break;
10047 case BUFFER_MOVE:
10048 (*message)->raw = (void *) buffer;
10049 (*message)->raw_length = length;
10050 break;
10051 default:
10052 err = IE_ENUM;
10053 goto leave;
10057 leave:
10058 if (err) {
10059 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10060 isds_message_free(message);
10063 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10064 xmlXPathFreeObject(result);
10065 xmlXPathFreeContext(xpath_ctx);
10066 if (!*message || !(*message)->xml) {
10067 xmlFreeDoc(message_doc);
10070 if (!err)
10071 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10072 return err;
10076 /* Determine type of raw message or delivery info according some heuristics.
10077 * It does not validate the raw blob.
10078 * @context is session context
10079 * @raw_type returns content type of @buffer. Valid only if exit code of this
10080 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10081 * reallocated memory.
10082 * @buffer is message raw representation.
10083 * @length is length of buffer in bytes. */
10084 isds_error isds_guess_raw_type(struct isds_ctx *context,
10085 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10086 isds_error err;
10087 void *xml_stream = NULL;
10088 size_t xml_stream_length = 0;
10089 xmlDocPtr document = NULL;
10090 xmlNodePtr root = NULL;
10092 if (!context) return IE_INVALID_CONTEXT;
10093 zfree(context->long_message);
10094 if (length == 0 || !buffer) return IE_INVAL;
10095 if (!raw_type) return IE_INVAL;
10097 /* Try CMS */
10098 err = _isds_extract_cms_data(context, buffer, length,
10099 &xml_stream, &xml_stream_length);
10100 if (err) {
10101 xml_stream = (void *) buffer;
10102 xml_stream_length = (size_t) length;
10103 err = IE_SUCCESS;
10106 /* Try XML */
10107 document = xmlParseMemory(xml_stream, xml_stream_length);
10108 if (!document) {
10109 isds_printf_message(context,
10110 _("Could not parse data as XML document"));
10111 err = IE_NOTSUP;
10112 goto leave;
10115 /* Get root element */
10116 root = xmlDocGetRootElement(document);
10117 if (!root) {
10118 isds_printf_message(context,
10119 _("XML document is missing root element"));
10120 err = IE_XML;
10121 goto leave;
10124 if (!root->ns || !root->ns->href) {
10125 isds_printf_message(context,
10126 _("Root element does not belong to any name space"));
10127 err = IE_NOTSUP;
10128 goto leave;
10131 /* Test name space */
10132 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10133 if (xml_stream == buffer)
10134 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10135 else
10136 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10137 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10138 if (xml_stream == buffer)
10139 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10140 else
10141 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10142 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10143 if (xml_stream == buffer)
10144 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10145 else
10146 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10147 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10148 if (xml_stream != buffer) {
10149 isds_printf_message(context,
10150 _("Document in ISDS name space is encapsulated into CMS" ));
10151 err = IE_NOTSUP;
10152 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10153 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10154 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10155 *raw_type = RAWTYPE_DELIVERYINFO;
10156 else {
10157 isds_printf_message(context,
10158 _("Unknown root element in ISDS name space"));
10159 err = IE_NOTSUP;
10161 } else {
10162 isds_printf_message(context,
10163 _("Unknown name space"));
10164 err = IE_NOTSUP;
10167 leave:
10168 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10169 xmlFreeDoc(document);
10170 return err;
10174 /* Download signed incoming/outgoing message identified by ID.
10175 * @context is session context
10176 * @output is true for outgoing message, false for incoming message
10177 * @message_id is message identifier (you can get them from
10178 * isds_get_list_of_{sent,received}_messages())
10179 * @message is automatically reallocated message retrieved from ISDS. The raw
10180 * member will be filled with PKCS#7 structure in DER format. */
10181 static isds_error isds_get_signed_message(struct isds_ctx *context,
10182 const _Bool outgoing, const char *message_id,
10183 struct isds_message **message) {
10185 isds_error err = IE_SUCCESS;
10186 #if HAVE_LIBCURL
10187 xmlDocPtr response = NULL;
10188 xmlChar *code = NULL, *status_message = NULL;
10189 xmlXPathContextPtr xpath_ctx = NULL;
10190 xmlXPathObjectPtr result = NULL;
10191 char *encoded_structure = NULL;
10192 void *raw = NULL;
10193 size_t raw_length = 0;
10194 #endif
10196 if (!context) return IE_INVALID_CONTEXT;
10197 zfree(context->long_message);
10198 if (!message) return IE_INVAL;
10199 isds_message_free(message);
10201 #if HAVE_LIBCURL
10202 /* Do request and check for success */
10203 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10204 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10205 BAD_CAST "SignedMessageDownload",
10206 message_id, &response, NULL, NULL, &code, &status_message);
10207 if (err) goto leave;
10209 /* Find signed message, extract it into raw and maybe free
10210 * response */
10211 err = find_extract_signed_data_free_response(context,
10212 (xmlChar *)message_id, &response,
10213 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10214 BAD_CAST "SignedMessageDownload",
10215 &raw, &raw_length);
10216 if (err) goto leave;
10218 /* Parse message */
10219 err = isds_load_message(context,
10220 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10221 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10222 raw, raw_length, message, BUFFER_MOVE);
10223 if (err) goto leave;
10225 raw = NULL;
10227 leave:
10228 if (err) {
10229 isds_message_free(message);
10232 free(encoded_structure);
10233 xmlXPathFreeObject(result);
10234 xmlXPathFreeContext(xpath_ctx);
10235 free(raw);
10237 free(code);
10238 free(status_message);
10239 xmlFreeDoc(response);
10241 if (!err)
10242 isds_log(ILF_ISDS, ILL_DEBUG,
10243 (outgoing) ?
10244 _("SignedSentMessageDownload request processed by server "
10245 "successfully.\n") :
10246 _("SignedMessageDownload request processed by server "
10247 "successfully.\n")
10249 #else /* not HAVE_LIBCURL */
10250 err = IE_NOTSUP;
10251 #endif
10252 return err;
10256 /* Download signed incoming message identified by ID.
10257 * @context is session context
10258 * @message_id is message identifier (you can get them from
10259 * isds_get_list_of_received_messages())
10260 * @message is automatically reallocated message retrieved from ISDS. The raw
10261 * member will be filled with PKCS#7 structure in DER format. */
10262 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10263 const char *message_id, struct isds_message **message) {
10264 return isds_get_signed_message(context, 0, message_id, message);
10268 /* Download signed outgoing message identified by ID.
10269 * @context is session context
10270 * @message_id is message identifier (you can get them from
10271 * isds_get_list_of_sent_messages())
10272 * @message is automatically reallocated message retrieved from ISDS. The raw
10273 * member will be filled with PKCS#7 structure in DER format. */
10274 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10275 const char *message_id, struct isds_message **message) {
10276 return isds_get_signed_message(context, 1, message_id, message);
10280 /* Get type and name of user who sent a message identified by ID.
10281 * @context is session context
10282 * @message_id is message identifier
10283 * @sender_type is pointer to automatically allocated type of sender detected
10284 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10285 * library or to the server, NULL will be returned. Pass NULL if you don't
10286 * care about it.
10287 * @raw_sender_type is automatically reallocated UTF-8 string describing
10288 * sender type or NULL if not known to server. Pass NULL if you don't care.
10289 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10290 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10291 isds_error isds_get_message_sender(struct isds_ctx *context,
10292 const char *message_id, isds_sender_type **sender_type,
10293 char **raw_sender_type, char **sender_name) {
10294 isds_error err = IE_SUCCESS;
10295 #if HAVE_LIBCURL
10296 xmlDocPtr response = NULL;
10297 xmlChar *code = NULL, *status_message = NULL;
10298 xmlXPathContextPtr xpath_ctx = NULL;
10299 xmlXPathObjectPtr result = NULL;
10300 char *type_string = NULL;
10301 #endif
10303 if (!context) return IE_INVALID_CONTEXT;
10304 zfree(context->long_message);
10305 if (sender_type) zfree(*sender_type);
10306 if (raw_sender_type) zfree(*raw_sender_type);
10307 if (sender_name) zfree(*sender_name);
10308 if (!message_id) return IE_INVAL;
10310 #if HAVE_LIBCURL
10311 /* Do request and check for success */
10312 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10313 BAD_CAST "GetMessageAuthor",
10314 message_id, &response, NULL, NULL, &code, &status_message);
10315 if (err) goto leave;
10317 /* Extract data */
10318 xpath_ctx = xmlXPathNewContext(response);
10319 if (!xpath_ctx) {
10320 err = IE_ERROR;
10321 goto leave;
10323 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10324 err = IE_ERROR;
10325 goto leave;
10327 result = xmlXPathEvalExpression(
10328 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10329 if (!result) {
10330 err = IE_ERROR;
10331 goto leave;
10333 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10334 isds_log_message(context,
10335 _("Missing GetMessageAuthorResponse element"));
10336 err = IE_ISDS;
10337 goto leave;
10339 if (result->nodesetval->nodeNr > 1) {
10340 isds_log_message(context,
10341 _("Multiple GetMessageAuthorResponse element"));
10342 err = IE_ISDS;
10343 goto leave;
10345 xpath_ctx->node = result->nodesetval->nodeTab[0];
10346 xmlXPathFreeObject(result); result = NULL;
10348 /* Fill output arguments in */
10349 EXTRACT_STRING("isds:userType", type_string);
10350 if (NULL != type_string) {
10351 if (NULL != sender_type) {
10352 *sender_type = calloc(1, sizeof(**sender_type));
10353 if (NULL == *sender_type) {
10354 err = IE_NOMEM;
10355 goto leave;
10358 err = string2isds_sender_type((xmlChar *)type_string,
10359 *sender_type);
10360 if (err) {
10361 zfree(*sender_type);
10362 if (err == IE_ENUM) {
10363 err = IE_SUCCESS;
10364 char *type_string_locale = _isds_utf82locale(type_string);
10365 isds_log(ILF_ISDS, ILL_WARNING,
10366 _("Unknown isds:userType value: %s"),
10367 type_string_locale);
10368 free(type_string_locale);
10373 if (NULL != sender_name)
10374 EXTRACT_STRING("isds:authorName", *sender_name);
10376 leave:
10377 if (err) {
10378 if (NULL != sender_type) zfree(*sender_type);
10379 zfree(type_string);
10380 if (NULL != sender_name) zfree(*sender_name);
10382 if (NULL != raw_sender_type) *raw_sender_type = type_string;
10384 xmlXPathFreeObject(result);
10385 xmlXPathFreeContext(xpath_ctx);
10387 free(code);
10388 free(status_message);
10389 xmlFreeDoc(response);
10391 if (!err)
10392 isds_log(ILF_ISDS, ILL_DEBUG,
10393 _("GetMessageAuthor request processed by server "
10394 "successfully.\n"));
10395 #else /* not HAVE_LIBCURL */
10396 err = IE_NOTSUP;
10397 #endif
10398 return err;
10402 /* Retrieve hash of message identified by ID stored in ISDS.
10403 * @context is session context
10404 * @message_id is message identifier
10405 * @hash is automatically reallocated message hash downloaded from ISDS.
10406 * Message must exist in system and must not be deleted. */
10407 isds_error isds_download_message_hash(struct isds_ctx *context,
10408 const char *message_id, struct isds_hash **hash) {
10410 isds_error err = IE_SUCCESS;
10411 #if HAVE_LIBCURL
10412 xmlDocPtr response = NULL;
10413 xmlChar *code = NULL, *status_message = NULL;
10414 xmlXPathContextPtr xpath_ctx = NULL;
10415 xmlXPathObjectPtr result = NULL;
10416 #endif
10418 if (!context) return IE_INVALID_CONTEXT;
10419 zfree(context->long_message);
10421 isds_hash_free(hash);
10423 #if HAVE_LIBCURL
10424 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10425 BAD_CAST "VerifyMessage", message_id,
10426 &response, NULL, NULL, &code, &status_message);
10427 if (err) goto leave;
10430 /* Extract data */
10431 xpath_ctx = xmlXPathNewContext(response);
10432 if (!xpath_ctx) {
10433 err = IE_ERROR;
10434 goto leave;
10436 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10437 err = IE_ERROR;
10438 goto leave;
10440 result = xmlXPathEvalExpression(
10441 BAD_CAST "/isds:VerifyMessageResponse",
10442 xpath_ctx);
10443 if (!result) {
10444 err = IE_ERROR;
10445 goto leave;
10447 /* Empty response */
10448 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10449 char *message_id_locale = _isds_utf82locale((char*) message_id);
10450 isds_printf_message(context,
10451 _("Server did not return any response for ID `%s' "
10452 "on VerifyMessage request"), message_id_locale);
10453 free(message_id_locale);
10454 err = IE_ISDS;
10455 goto leave;
10457 /* More responses */
10458 if (result->nodesetval->nodeNr > 1) {
10459 char *message_id_locale = _isds_utf82locale((char*) message_id);
10460 isds_printf_message(context,
10461 _("Server did return more responses for ID `%s' "
10462 "on VerifyMessage request"), message_id_locale);
10463 free(message_id_locale);
10464 err = IE_ISDS;
10465 goto leave;
10467 /* One response */
10468 xpath_ctx->node = result->nodesetval->nodeTab[0];
10470 /* Extract the hash */
10471 err = find_and_extract_DmHash(context, hash, xpath_ctx);
10473 leave:
10474 if (err) {
10475 isds_hash_free(hash);
10478 xmlXPathFreeObject(result);
10479 xmlXPathFreeContext(xpath_ctx);
10481 free(code);
10482 free(status_message);
10483 xmlFreeDoc(response);
10485 if (!err)
10486 isds_log(ILF_ISDS, ILL_DEBUG,
10487 _("VerifyMessage request processed by server "
10488 "successfully.\n")
10490 #else /* not HAVE_LIBCURL */
10491 err = IE_NOTSUP;
10492 #endif
10493 return err;
10497 /* Erase message specified by @message_id from long term storage. Other
10498 * message cannot be erased on user request.
10499 * @context is session context
10500 * @message_id is message identifier.
10501 * @incoming is true for incoming message, false for outgoing message.
10502 * @return
10503 * IE_SUCCESS if message has ben removed
10504 * IE_INVAL if message does not exist in long term storage or message
10505 * belongs to different box
10506 * TODO: IE_NOEPRM if user has no permission to erase a message */
10507 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
10508 const char *message_id, _Bool incoming) {
10509 isds_error err = IE_SUCCESS;
10510 #if HAVE_LIBCURL
10511 xmlNodePtr request = NULL, node;
10512 xmlNsPtr isds_ns = NULL;
10513 xmlDocPtr response = NULL;
10514 xmlChar *code = NULL, *status_message = NULL;
10515 #endif
10517 if (!context) return IE_INVALID_CONTEXT;
10518 zfree(context->long_message);
10519 if (NULL == message_id) return IE_INVAL;
10521 /* Check if connection is established
10522 * TODO: This check should be done downstairs. */
10523 if (!context->curl) return IE_CONNECTION_CLOSED;
10525 #if HAVE_LIBCURL
10526 /* Build request */
10527 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
10528 if (!request) {
10529 isds_log_message(context,
10530 _("Could build EraseMessage request"));
10531 return IE_ERROR;
10533 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10534 if(!isds_ns) {
10535 isds_log_message(context, _("Could not create ISDS name space"));
10536 xmlFreeNode(request);
10537 return IE_ERROR;
10539 xmlSetNs(request, isds_ns);
10541 err = validate_message_id_length(context, (xmlChar *) message_id);
10542 if (err) goto leave;
10543 INSERT_STRING(request, "dmID", message_id);
10545 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
10548 /* Send request */
10549 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
10550 "message ID %s to ISDS\n"), message_id);
10551 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
10552 xmlFreeNode(request); request = NULL;
10554 if (err) {
10555 isds_log(ILF_ISDS, ILL_DEBUG,
10556 _("Processing ISDS response on EraseMessage request "
10557 "failed\n"));
10558 goto leave;
10561 /* Check for response status */
10562 err = isds_response_status(context, SERVICE_DM_INFO, response,
10563 &code, &status_message, NULL);
10564 if (err) {
10565 isds_log(ILF_ISDS, ILL_DEBUG,
10566 _("ISDS response on EraseMessage request is missing "
10567 "status\n"));
10568 goto leave;
10571 /* Check server status code */
10572 if (!xmlStrcmp(code, BAD_CAST "1211")) {
10573 isds_log_message(context, _("Message to erase belongs to other box"));
10574 err = IE_INVAL;
10575 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
10576 isds_log_message(context, _("Message to erase is not saved in "
10577 "long term storage or the direction does not match"));
10578 err = IE_INVAL;
10579 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
10580 char *code_locale = _isds_utf82locale((char*) code);
10581 char *message_locale = _isds_utf82locale((char*) status_message);
10582 isds_log(ILF_ISDS, ILL_DEBUG,
10583 _("Server refused EraseMessage request "
10584 "(code=%s, message=%s)\n"),
10585 code_locale, message_locale);
10586 isds_log_message(context, message_locale);
10587 free(code_locale);
10588 free(message_locale);
10589 err = IE_ISDS;
10590 goto leave;
10593 leave:
10594 free(code);
10595 free(status_message);
10596 xmlFreeDoc(response);
10597 xmlFreeNode(request);
10599 if (!err)
10600 isds_log(ILF_ISDS, ILL_DEBUG,
10601 _("EraseMessage request processed by server "
10602 "successfully.\n")
10604 #else /* not HAVE_LIBCURL */
10605 err = IE_NOTSUP;
10606 #endif
10607 return err;
10611 /* Mark message as read. This is a transactional commit function to acknowledge
10612 * to ISDS the message has been downloaded and processed by client properly.
10613 * @context is session context
10614 * @message_id is message identifier. */
10615 isds_error isds_mark_message_read(struct isds_ctx *context,
10616 const char *message_id) {
10618 isds_error err = IE_SUCCESS;
10619 #if HAVE_LIBCURL
10620 xmlDocPtr response = NULL;
10621 xmlChar *code = NULL, *status_message = NULL;
10622 #endif
10624 if (!context) return IE_INVALID_CONTEXT;
10625 zfree(context->long_message);
10627 #if HAVE_LIBCURL
10628 /* Do request and check for success */
10629 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10630 BAD_CAST "MarkMessageAsDownloaded", message_id,
10631 &response, NULL, NULL, &code, &status_message);
10633 free(code);
10634 free(status_message);
10635 xmlFreeDoc(response);
10637 if (!err)
10638 isds_log(ILF_ISDS, ILL_DEBUG,
10639 _("MarkMessageAsDownloaded request processed by server "
10640 "successfully.\n")
10642 #else /* not HAVE_LIBCURL */
10643 err = IE_NOTSUP;
10644 #endif
10645 return err;
10649 /* Mark message as received by recipient. This is applicable only to
10650 * commercial message. Use envelope->dmType message member to distinguish
10651 * commercial message from government message. Government message is
10652 * received automatically (by law), commercial message on recipient request.
10653 * @context is session context
10654 * @message_id is message identifier. */
10655 isds_error isds_mark_message_received(struct isds_ctx *context,
10656 const char *message_id) {
10658 isds_error err = IE_SUCCESS;
10659 #if HAVE_LIBCURL
10660 xmlDocPtr response = NULL;
10661 xmlChar *code = NULL, *status_message = NULL;
10662 #endif
10664 if (!context) return IE_INVALID_CONTEXT;
10665 zfree(context->long_message);
10667 #if HAVE_LIBCURL
10668 /* Do request and check for success */
10669 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10670 BAD_CAST "ConfirmDelivery", message_id,
10671 &response, NULL, NULL, &code, &status_message);
10673 free(code);
10674 free(status_message);
10675 xmlFreeDoc(response);
10677 if (!err)
10678 isds_log(ILF_ISDS, ILL_DEBUG,
10679 _("ConfirmDelivery request processed by server "
10680 "successfully.\n")
10682 #else /* not HAVE_LIBCURL */
10683 err = IE_NOTSUP;
10684 #endif
10685 return err;
10689 /* Send document for authorized conversion into Czech POINT system.
10690 * This is public anonymous service, no log-in necessary. Special context is
10691 * used to reuse keep-a-live HTTPS connection.
10692 * @context is Czech POINT session context. DO NOT use context connected to
10693 * ISDS server. Use new context or context used by this function previously.
10694 * @document is document to convert. Only data, data_length, dmFileDescr and
10695 * is_xml members are significant. Be ware that not all document formats can be
10696 * converted (signed PDF 1.3 and higher only (2010-02 state)).
10697 * @id is reallocated identifier assigned by Czech POINT system to
10698 * your document on submit. Use is to tell it to Czech POINT officer.
10699 * @date is reallocated document submit date (submitted documents
10700 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
10701 * value. */
10702 isds_error czp_convert_document(struct isds_ctx *context,
10703 const struct isds_document *document,
10704 char **id, struct tm **date) {
10705 isds_error err = IE_SUCCESS;
10706 #if HAVE_LIBCURL
10707 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
10708 xmlNodePtr request = NULL, node;
10709 xmlDocPtr response = NULL;
10711 xmlXPathContextPtr xpath_ctx = NULL;
10712 xmlXPathObjectPtr result = NULL;
10713 long int status = -1;
10714 long int *status_ptr = &status;
10715 char *string = NULL;
10716 #endif
10719 if (!context) return IE_INVALID_CONTEXT;
10720 zfree(context->long_message);
10721 if (!document || !id || !date) return IE_INVAL;
10723 if (document->is_xml) {
10724 isds_log_message(context,
10725 _("XML documents cannot be submitted to conversion"));
10726 return IE_NOTSUP;
10729 /* Free output arguments */
10730 zfree(*id);
10731 zfree(*date);
10733 #if HAVE_LIBCURL
10734 /* Store configuration */
10735 context->type = CTX_TYPE_CZP;
10736 free(context->url);
10737 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
10738 if (!(context->url))
10739 return IE_NOMEM;
10741 /* Prepare CURL handle if not yet connected */
10742 if (!context->curl) {
10743 context->curl = curl_easy_init();
10744 if (!(context->curl))
10745 return IE_ERROR;
10748 /* Build conversion request */
10749 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
10750 if (!request) {
10751 isds_log_message(context,
10752 _("Could not build Czech POINT conversion request"));
10753 return IE_ERROR;
10755 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
10756 if(!deposit_ns) {
10757 isds_log_message(context,
10758 _("Could not create Czech POINT deposit name space"));
10759 xmlFreeNode(request);
10760 return IE_ERROR;
10762 xmlSetNs(request, deposit_ns);
10764 /* Insert children. They are in empty namespace! */
10765 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
10766 if(!empty_ns) {
10767 isds_log_message(context, _("Could not create empty name space"));
10768 err = IE_ERROR;
10769 goto leave;
10771 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
10772 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
10773 document->dmFileDescr);
10775 /* Document encoded in Base64 */
10776 err = insert_base64_encoded_string(context, request, empty_ns, "document",
10777 document->data, document->data_length);
10778 if (err) goto leave;
10780 isds_log(ILF_ISDS, ILL_DEBUG,
10781 _("Submitting document for conversion into Czech POINT deposit"));
10783 /* Send conversion request */
10784 err = _czp_czpdeposit(context, request, &response);
10785 xmlFreeNode(request); request = NULL;
10787 if (err) {
10788 czp_do_close_connection(context);
10789 goto leave;
10793 /* Extract response */
10794 xpath_ctx = xmlXPathNewContext(response);
10795 if (!xpath_ctx) {
10796 err = IE_ERROR;
10797 goto leave;
10799 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10800 err = IE_ERROR;
10801 goto leave;
10803 result = xmlXPathEvalExpression(
10804 BAD_CAST "/deposit:saveDocumentResponse/return",
10805 xpath_ctx);
10806 if (!result) {
10807 err = IE_ERROR;
10808 goto leave;
10810 /* Empty response */
10811 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10812 isds_printf_message(context,
10813 _("Missing `return' element in Czech POINT deposit response"));
10814 err = IE_ISDS;
10815 goto leave;
10817 /* More responses */
10818 if (result->nodesetval->nodeNr > 1) {
10819 isds_printf_message(context,
10820 _("Multiple `return' element in Czech POINT deposit response"));
10821 err = IE_ISDS;
10822 goto leave;
10824 /* One response */
10825 xpath_ctx->node = result->nodesetval->nodeTab[0];
10827 /* Get status */
10828 EXTRACT_LONGINT("status", status_ptr, 1);
10829 if (status) {
10830 EXTRACT_STRING("statusMsg", string);
10831 char *string_locale = _isds_utf82locale(string);
10832 isds_printf_message(context,
10833 _("Czech POINT deposit refused document for conversion "
10834 "(code=%ld, message=%s)"),
10835 status, string_locale);
10836 free(string_locale);
10837 err = IE_ISDS;
10838 goto leave;
10841 /* Get document ID */
10842 EXTRACT_STRING("documentID", *id);
10844 /* Get submit date */
10845 EXTRACT_STRING("dateInserted", string);
10846 if (string) {
10847 *date = calloc(1, sizeof(**date));
10848 if (!*date) {
10849 err = IE_NOMEM;
10850 goto leave;
10852 err = _isds_datestring2tm((xmlChar *)string, *date);
10853 if (err) {
10854 if (err == IE_NOTSUP) {
10855 err = IE_ISDS;
10856 char *string_locale = _isds_utf82locale(string);
10857 isds_printf_message(context,
10858 _("Invalid dateInserted value: %s"), string_locale);
10859 free(string_locale);
10861 goto leave;
10865 leave:
10866 free(string);
10867 xmlXPathFreeObject(result);
10868 xmlXPathFreeContext(xpath_ctx);
10870 xmlFreeDoc(response);
10871 xmlFreeNode(request);
10873 if (!err) {
10874 char *id_locale = _isds_utf82locale((char *) *id);
10875 isds_log(ILF_ISDS, ILL_DEBUG,
10876 _("Document %s has been submitted for conversion "
10877 "to server successfully\n"), id_locale);
10878 free(id_locale);
10880 #else /* not HAVE_LIBCURL */
10881 err = IE_NOTSUP;
10882 #endif
10883 return err;
10887 /* Close possibly opened connection to Czech POINT document deposit.
10888 * @context is Czech POINT session context. */
10889 isds_error czp_close_connection(struct isds_ctx *context) {
10890 if (!context) return IE_INVALID_CONTEXT;
10891 zfree(context->long_message);
10892 #if HAVE_LIBCURL
10893 return czp_do_close_connection(context);
10894 #else
10895 return IE_NOTSUP;
10896 #endif
10900 /* Send request for new box creation in testing ISDS instance.
10901 * It's not possible to request for a production box currently, as it
10902 * communicates via e-mail.
10903 * XXX: This function does not work either. Server complains about invalid
10904 * e-mail address.
10905 * XXX: Remove context->type hacks in isds.c and validator.c when removing
10906 * this function
10907 * @context is special session context for box creation request. DO NOT use
10908 * standard context as it could reveal your password. Use fresh new context or
10909 * context previously used by this function.
10910 * @box is box description to create including single primary user (in case of
10911 * FO box type). It outputs box ID assigned by ISDS in dbID element.
10912 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
10913 * box, or contact address of PFO box owner). The email member is mandatory as
10914 * it will be used to deliver credentials.
10915 * @former_names is former name of box owner. Pass NULL if you don't care.
10916 * @approval is optional external approval of box manipulation
10917 * @refnumber is reallocated serial number of request assigned by ISDS. Use
10918 * NULL, if you don't care.*/
10919 isds_error isds_request_new_testing_box(struct isds_ctx *context,
10920 struct isds_DbOwnerInfo *box, const struct isds_list *users,
10921 const char *former_names, const struct isds_approval *approval,
10922 char **refnumber) {
10923 isds_error err = IE_SUCCESS;
10924 #if HAVE_LIBCURL
10925 xmlNodePtr request = NULL;
10926 xmlDocPtr response = NULL;
10927 xmlXPathContextPtr xpath_ctx = NULL;
10928 xmlXPathObjectPtr result = NULL;
10929 #endif
10932 if (!context) return IE_INVALID_CONTEXT;
10933 zfree(context->long_message);
10934 if (!box) return IE_INVAL;
10936 #if HAVE_LIBCURL
10937 if (!box->email || box->email[0] == '\0') {
10938 isds_log_message(context, _("E-mail field is mandatory"));
10939 return IE_INVAL;
10942 /* Scratch box ID */
10943 zfree(box->dbID);
10945 /* Store configuration */
10946 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
10947 free(context->url);
10948 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
10949 if (!(context->url))
10950 return IE_NOMEM;
10952 /* Prepare CURL handle if not yet connected */
10953 if (!context->curl) {
10954 context->curl = curl_easy_init();
10955 if (!(context->curl))
10956 return IE_ERROR;
10959 /* Build CreateDataBox request */
10960 err = build_CreateDBInput_request(context,
10961 &request, BAD_CAST "CreateDataBox",
10962 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
10963 if (err) goto leave;
10965 /* Send it to server and process response */
10966 err = send_destroy_request_check_response(context,
10967 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
10968 &response, (xmlChar **) refnumber, NULL);
10969 if (err) goto leave;
10971 /* Extract box ID */
10972 xpath_ctx = xmlXPathNewContext(response);
10973 if (!xpath_ctx) {
10974 err = IE_ERROR;
10975 goto leave;
10977 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10978 err = IE_ERROR;
10979 goto leave;
10981 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
10983 leave:
10984 xmlXPathFreeObject(result);
10985 xmlXPathFreeContext(xpath_ctx);
10986 xmlFreeDoc(response);
10987 xmlFreeNode(request);
10989 if (!err) {
10990 isds_log(ILF_ISDS, ILL_DEBUG,
10991 _("CreateDataBox request processed by server successfully.\n"));
10993 #else /* not HAVE_LIBCURL */
10994 err = IE_NOTSUP;
10995 #endif
10997 return err;
11001 /* Submit CMS signed message to ISDS to verify its originality. This is
11002 * stronger form of isds_verify_message_hash() because ISDS does more checks
11003 * than simple one (potentialy old weak) hash comparison.
11004 * @context is session context
11005 * @message is memory with raw CMS signed message bit stream
11006 * @length is @message size in bytes
11007 * @return
11008 * IE_SUCCESS if message originates in ISDS
11009 * IE_NOTEQUAL if message is unknown to ISDS
11010 * other code for other errors */
11011 isds_error isds_authenticate_message(struct isds_ctx *context,
11012 const void *message, size_t length) {
11013 isds_error err = IE_SUCCESS;
11014 #if HAVE_LIBCURL
11015 xmlNsPtr isds_ns = NULL;
11016 xmlNodePtr request = NULL;
11017 xmlDocPtr response = NULL;
11018 xmlXPathContextPtr xpath_ctx = NULL;
11019 xmlXPathObjectPtr result = NULL;
11020 _Bool *authentic = NULL;
11021 #endif
11023 if (!context) return IE_INVALID_CONTEXT;
11024 zfree(context->long_message);
11025 if (!message || length == 0) return IE_INVAL;
11027 #if HAVE_LIBCURL
11028 /* Check if connection is established
11029 * TODO: This check should be done downstairs. */
11030 if (!context->curl) return IE_CONNECTION_CLOSED;
11033 /* Build AuthenticateMessage request */
11034 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11035 if (!request) {
11036 isds_log_message(context,
11037 _("Could not build AuthenticateMessage request"));
11038 return IE_ERROR;
11040 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11041 if(!isds_ns) {
11042 isds_log_message(context, _("Could not create ISDS name space"));
11043 xmlFreeNode(request);
11044 return IE_ERROR;
11046 xmlSetNs(request, isds_ns);
11048 /* Insert Base64 encoded message */
11049 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11050 message, length);
11051 if (err) goto leave;
11053 /* Send request to server and process response */
11054 err = send_destroy_request_check_response(context,
11055 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11056 &response, NULL, NULL);
11057 if (err) goto leave;
11060 /* ISDS has decided */
11061 xpath_ctx = xmlXPathNewContext(response);
11062 if (!xpath_ctx) {
11063 err = IE_ERROR;
11064 goto leave;
11066 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11067 err = IE_ERROR;
11068 goto leave;
11071 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11073 if (!authentic) {
11074 isds_log_message(context,
11075 _("Server did not return any response on "
11076 "AuthenticateMessage request"));
11077 err = IE_ISDS;
11078 goto leave;
11080 if (*authentic) {
11081 isds_log(ILF_ISDS, ILL_DEBUG,
11082 _("ISDS authenticated the message successfully\n"));
11083 } else {
11084 isds_log_message(context, _("ISDS does not know the message"));
11085 err = IE_NOTEQUAL;
11089 leave:
11090 free(authentic);
11091 xmlXPathFreeObject(result);
11092 xmlXPathFreeContext(xpath_ctx);
11094 xmlFreeDoc(response);
11095 xmlFreeNode(request);
11096 #else /* not HAVE_LIBCURL */
11097 err = IE_NOTSUP;
11098 #endif
11100 return err;
11104 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11105 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11106 * be re-signed.
11107 * @context is session context
11108 * @input_data is memory with raw CMS signed message or delivery info bit
11109 * stream to re-sign
11110 * @input_length is @input_data size in bytes
11111 * @output_data is pointer to auto-allocated memory where to store re-signed
11112 * input data blob. Caller must free it.
11113 * @output_data is pointer where to store @output_data size in bytes
11114 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11115 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11116 * @return
11117 * IE_SUCCESS if CMS blob has been re-signed successfully
11118 * other code for other errors */
11119 isds_error isds_resign_message(struct isds_ctx *context,
11120 const void *input_data, size_t input_length,
11121 void **output_data, size_t *output_length, struct tm **valid_to) {
11122 isds_error err = IE_SUCCESS;
11123 #if HAVE_LIBCURL
11124 xmlNsPtr isds_ns = NULL;
11125 xmlNodePtr request = NULL;
11126 xmlDocPtr response = NULL;
11127 xmlXPathContextPtr xpath_ctx = NULL;
11128 xmlXPathObjectPtr result = NULL;
11129 char *string = NULL;
11130 const xmlChar *codes[] = {
11131 BAD_CAST "2200",
11132 BAD_CAST "2201",
11133 BAD_CAST "2204",
11134 BAD_CAST "2207",
11135 NULL
11137 const char *meanings[] = {
11138 "Message is bad",
11139 "Message is not original",
11140 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11141 "Time stamp could not been generated in time"
11143 const isds_error errors[] = {
11144 IE_INVAL,
11145 IE_NOTUNIQ,
11146 IE_INVAL,
11147 IE_ISDS,
11149 struct code_map_isds_error map = {
11150 .codes = codes,
11151 .meanings = meanings,
11152 .errors = errors
11154 #endif
11156 if (NULL != output_data) *output_data = NULL;
11157 if (NULL != output_length) *output_length = 0;
11158 if (NULL != valid_to) *valid_to = NULL;
11160 if (NULL == context) return IE_INVALID_CONTEXT;
11161 zfree(context->long_message);
11162 if (NULL == input_data || 0 == input_length) {
11163 isds_log_message(context, _("Empty CMS blob on input"));
11164 return IE_INVAL;
11166 if (NULL == output_data || NULL == output_length) {
11167 isds_log_message(context,
11168 _("NULL pointer provided for output CMS blob"));
11169 return IE_INVAL;
11172 #if HAVE_LIBCURL
11173 /* Check if connection is established
11174 * TODO: This check should be done downstairs. */
11175 if (!context->curl) return IE_CONNECTION_CLOSED;
11178 /* Build Re-signISDSDocument request */
11179 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11180 if (!request) {
11181 isds_log_message(context,
11182 _("Could not build Re-signISDSDocument request"));
11183 return IE_ERROR;
11185 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11186 if(!isds_ns) {
11187 isds_log_message(context, _("Could not create ISDS name space"));
11188 xmlFreeNode(request);
11189 return IE_ERROR;
11191 xmlSetNs(request, isds_ns);
11193 /* Insert Base64 encoded CMS blob */
11194 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11195 input_data, input_length);
11196 if (err) goto leave;
11198 /* Send request to server and process response */
11199 err = send_destroy_request_check_response(context,
11200 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11201 &response, NULL, &map);
11202 if (err) goto leave;
11205 /* Extract re-signed data */
11206 xpath_ctx = xmlXPathNewContext(response);
11207 if (!xpath_ctx) {
11208 err = IE_ERROR;
11209 goto leave;
11211 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11212 err = IE_ERROR;
11213 goto leave;
11215 result = xmlXPathEvalExpression(
11216 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11217 if (!result) {
11218 err = IE_ERROR;
11219 goto leave;
11221 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11222 isds_log_message(context,
11223 _("Missing Re-signISDSDocumentResponse element"));
11224 err = IE_ISDS;
11225 goto leave;
11227 if (result->nodesetval->nodeNr > 1) {
11228 isds_log_message(context,
11229 _("Multiple Re-signISDSDocumentResponse element"));
11230 err = IE_ISDS;
11231 goto leave;
11233 xpath_ctx->node = result->nodesetval->nodeTab[0];
11234 xmlXPathFreeObject(result); result = NULL;
11236 EXTRACT_STRING("isds:dmResultDoc", string);
11237 /* Decode non-empty data */
11238 if (NULL != string && string[0] != '\0') {
11239 *output_length = _isds_b64decode(string, output_data);
11240 if (*output_length == (size_t) -1) {
11241 isds_log_message(context,
11242 _("Error while Base64-decoding re-signed data"));
11243 err = IE_ERROR;
11244 goto leave;
11246 } else {
11247 isds_log_message(context, _("Server did not send re-signed data"));
11248 err = IE_ISDS;
11249 goto leave;
11251 zfree(string);
11253 if (NULL != valid_to) {
11254 /* Get time stamp expiration date */
11255 EXTRACT_STRING("isds:dmValidTo", string);
11256 if (NULL != string) {
11257 *valid_to = calloc(1, sizeof(**valid_to));
11258 if (!*valid_to) {
11259 err = IE_NOMEM;
11260 goto leave;
11262 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11263 if (err) {
11264 if (err == IE_NOTSUP) {
11265 err = IE_ISDS;
11266 char *string_locale = _isds_utf82locale(string);
11267 isds_printf_message(context,
11268 _("Invalid dmValidTo value: %s"), string_locale);
11269 free(string_locale);
11271 goto leave;
11276 leave:
11277 free(string);
11279 xmlXPathFreeObject(result);
11280 xmlXPathFreeContext(xpath_ctx);
11282 xmlFreeDoc(response);
11283 xmlFreeNode(request);
11284 #else /* not HAVE_LIBCURL */
11285 err = IE_NOTSUP;
11286 #endif
11288 return err;
11291 #undef INSERT_ELEMENT
11292 #undef CHECK_FOR_STRING_LENGTH
11293 #undef INSERT_STRING_ATTRIBUTE
11294 #undef INSERT_ULONGINTNOPTR
11295 #undef INSERT_ULONGINT
11296 #undef INSERT_LONGINT
11297 #undef INSERT_BOOLEAN
11298 #undef INSERT_SCALAR_BOOLEAN
11299 #undef INSERT_STRING
11300 #undef INSERT_STRING_WITH_NS
11301 #undef EXTRACT_STRING_ATTRIBUTE
11302 #undef EXTRACT_ULONGINT
11303 #undef EXTRACT_LONGINT
11304 #undef EXTRACT_BOOLEAN
11305 #undef EXTRACT_STRING
11308 /* Compute hash of message from raw representation and store it into envelope.
11309 * Original hash structure will be destroyed in envelope.
11310 * @context is session context
11311 * @message is message carrying raw XML message blob
11312 * @algorithm is desired hash algorithm to use */
11313 isds_error isds_compute_message_hash(struct isds_ctx *context,
11314 struct isds_message *message, const isds_hash_algorithm algorithm) {
11315 isds_error err = IE_SUCCESS;
11316 const char *nsuri;
11317 void *xml_stream = NULL;
11318 size_t xml_stream_length;
11319 size_t phys_start, phys_end;
11320 char *phys_path = NULL;
11321 struct isds_hash *new_hash = NULL;
11324 if (!context) return IE_INVALID_CONTEXT;
11325 zfree(context->long_message);
11326 if (!message) return IE_INVAL;
11328 if (!message->raw) {
11329 isds_log_message(context,
11330 _("Message does not carry raw representation"));
11331 return IE_INVAL;
11334 switch (message->raw_type) {
11335 case RAWTYPE_INCOMING_MESSAGE:
11336 nsuri = ISDS_NS;
11337 xml_stream = message->raw;
11338 xml_stream_length = message->raw_length;
11339 break;
11341 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11342 nsuri = SISDS_INCOMING_NS;
11343 xml_stream = message->raw;
11344 xml_stream_length = message->raw_length;
11345 break;
11347 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11348 nsuri = SISDS_INCOMING_NS;
11349 err = _isds_extract_cms_data(context,
11350 message->raw, message->raw_length,
11351 &xml_stream, &xml_stream_length);
11352 if (err) goto leave;
11353 break;
11355 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11356 nsuri = SISDS_OUTGOING_NS;
11357 xml_stream = message->raw;
11358 xml_stream_length = message->raw_length;
11359 break;
11361 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11362 nsuri = SISDS_OUTGOING_NS;
11363 err = _isds_extract_cms_data(context,
11364 message->raw, message->raw_length,
11365 &xml_stream, &xml_stream_length);
11366 if (err) goto leave;
11367 break;
11369 default:
11370 isds_log_message(context, _("Bad raw representation type"));
11371 return IE_INVAL;
11372 break;
11376 /* XXX: Hash is computed from original string representing isds:dmDm
11377 * subtree. That means no encoding, white space, xmlns attributes changes.
11378 * In other words, input for hash can be invalid XML stream. */
11379 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
11380 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11381 PHYSXML_ELEMENT_SEPARATOR,
11382 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
11383 PHYSXML_ELEMENT_SEPARATOR
11384 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
11385 err = IE_NOMEM;
11386 goto leave;
11388 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11389 phys_path, &phys_start, &phys_end);
11390 zfree(phys_path);
11391 if (err) {
11392 isds_log_message(context,
11393 _("Substring with isds:dmDM element could not be located "
11394 "in raw message"));
11395 goto leave;
11399 /* Compute hash */
11400 new_hash = calloc(1, sizeof(*new_hash));
11401 if (!new_hash) {
11402 err = IE_NOMEM;
11403 goto leave;
11405 new_hash->algorithm = algorithm;
11406 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
11407 new_hash);
11408 if (err) {
11409 isds_log_message(context, _("Could not compute message hash"));
11410 goto leave;
11413 /* Save computed hash */
11414 if (!message->envelope) {
11415 message->envelope = calloc(1, sizeof(*message->envelope));
11416 if (!message->envelope) {
11417 err = IE_NOMEM;
11418 goto leave;
11421 isds_hash_free(&message->envelope->hash);
11422 message->envelope->hash = new_hash;
11424 leave:
11425 if (err) {
11426 isds_hash_free(&new_hash);
11429 free(phys_path);
11430 if (xml_stream != message->raw) free(xml_stream);
11431 return err;
11435 /* Compare two hashes.
11436 * @h1 is first hash
11437 * @h2 is another hash
11438 * @return
11439 * IE_SUCCESS if hashes equal
11440 * IE_NOTUNIQ if hashes are comparable, but they don't equal
11441 * IE_ENUM if not comparable, but both structures defined
11442 * IE_INVAL if some of the structures are undefined (NULL)
11443 * IE_ERROR if internal error occurs */
11444 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
11445 if (h1 == NULL || h2 == NULL) return IE_INVAL;
11446 if (h1->algorithm != h2->algorithm) return IE_ENUM;
11447 if (h1->length != h2->length) return IE_ERROR;
11448 if (h1->length > 0 && !h1->value) return IE_ERROR;
11449 if (h2->length > 0 && !h2->value) return IE_ERROR;
11451 for (int i = 0; i < h1->length; i++) {
11452 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
11453 return IE_NOTEQUAL;
11455 return IE_SUCCESS;
11459 /* Check message has gone through ISDS by comparing message hash stored in
11460 * ISDS and locally computed hash. You must provide message with valid raw
11461 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
11462 * This is convenient wrapper for isds_download_message_hash(),
11463 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
11464 * @context is session context
11465 * @message is message with valid raw and envelope member; envelope->hash
11466 * member will be changed during function run. Use envelope on heap only.
11467 * @return
11468 * IE_SUCCESS if message originates in ISDS
11469 * IE_NOTEQUAL if message is unknown to ISDS
11470 * other code for other errors */
11471 isds_error isds_verify_message_hash(struct isds_ctx *context,
11472 struct isds_message *message) {
11473 isds_error err = IE_SUCCESS;
11474 struct isds_hash *downloaded_hash = NULL;
11476 if (!context) return IE_INVALID_CONTEXT;
11477 zfree(context->long_message);
11478 if (!message) return IE_INVAL;
11480 if (!message->envelope) {
11481 isds_log_message(context,
11482 _("Given message structure is missing envelope"));
11483 return IE_INVAL;
11485 if (!message->raw) {
11486 isds_log_message(context,
11487 _("Given message structure is missing raw representation"));
11488 return IE_INVAL;
11491 err = isds_download_message_hash(context, message->envelope->dmID,
11492 &downloaded_hash);
11493 if (err) goto leave;
11495 err = isds_compute_message_hash(context, message,
11496 downloaded_hash->algorithm);
11497 if (err) goto leave;
11499 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
11501 leave:
11502 isds_hash_free(&downloaded_hash);
11503 return err;
11507 /* Search for document by document ID in list of documents. IDs are compared
11508 * as UTF-8 string.
11509 * @documents is list of isds_documents
11510 * @id is document identifier
11511 * @return first matching document or NULL. */
11512 const struct isds_document *isds_find_document_by_id(
11513 const struct isds_list *documents, const char *id) {
11514 const struct isds_list *item;
11515 const struct isds_document *document;
11517 for (item = documents; item; item = item->next) {
11518 document = (struct isds_document *) item->data;
11519 if (!document) continue;
11521 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
11522 return document;
11525 return NULL;
11529 /* Normalize @mime_type to be proper MIME type.
11530 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
11531 * guess regular MIME type (e.g. "application/pdf").
11532 * @mime_type is UTF-8 encoded MIME type to fix
11533 * @return original @mime_type if no better interpretation exists, or
11534 * constant static UTF-8 encoded string with proper MIME type. */
11535 const char *isds_normalize_mime_type(const char *mime_type) {
11536 if (!mime_type) return NULL;
11538 for (int offset = 0;
11539 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
11540 offset += 2) {
11541 if (!xmlStrcasecmp((const xmlChar*) mime_type,
11542 extension_map_mime[offset]))
11543 return (const char *) extension_map_mime[offset + 1];
11546 return mime_type;
11550 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
11551 struct isds_message **message);
11552 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
11553 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
11554 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
11555 struct isds_address **address);
11557 int isds_message_free(struct isds_message **message);
11558 int isds_address_free(struct isds_address **address);
11562 /* Makes known all relevant namespaces to given XPath context
11563 * @xpath_ctx is XPath context
11564 * @message_ns selects proper message name space. Unsigned and signed
11565 * messages and delivery info's differ in prefix and URI. */
11566 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
11567 const message_ns_type message_ns) {
11568 const xmlChar *message_namespace = NULL;
11570 if (!xpath_ctx) return IE_ERROR;
11572 switch(message_ns) {
11573 case MESSAGE_NS_1:
11574 message_namespace = BAD_CAST ISDS1_NS; break;
11575 case MESSAGE_NS_UNSIGNED:
11576 message_namespace = BAD_CAST ISDS_NS; break;
11577 case MESSAGE_NS_SIGNED_INCOMING:
11578 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
11579 case MESSAGE_NS_SIGNED_OUTGOING:
11580 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
11581 case MESSAGE_NS_SIGNED_DELIVERY:
11582 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
11583 default:
11584 return IE_ENUM;
11587 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
11588 return IE_ERROR;
11589 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
11590 return IE_ERROR;
11591 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
11592 return IE_ERROR;
11593 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
11594 return IE_ERROR;
11595 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
11596 return IE_ERROR;
11597 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
11598 return IE_ERROR;
11599 return IE_SUCCESS;