dates element are mandatory at DataBoxCreditInfo
[libisds.git] / src / isds.c
blobbd22c5eb239118af6e28fd248e5f0106377df0b4
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 <gpg-error.h> /* Because of ksba or gpgme */
16 #include "physxml.h"
17 #include "system.h"
19 /* Locators */
20 /* Base URL of production ISDS instance */
21 const char isds_locator[] = "https://ws1.mojedatovaschranka.cz/";
22 const char isds_cert_locator[] = "https://ws1c.mojedatovaschranka.cz/";
23 const char isds_otp_locator[] = "https://www.mojedatovaschranka.cz/";
25 /* Base URL of production ISDS instance */
26 const char isds_testing_locator[] = "https://ws1.czebox.cz/";
27 const char isds_cert_testing_locator[] = "https://ws1c.czebox.cz/";
28 const char isds_otp_testing_locator[] = "https://www.czebox.cz/";
30 /* Extension to MIME type map */
31 static const xmlChar *extension_map_mime[] = {
32 BAD_CAST "cer", BAD_CAST "application/x-x509-ca-cert",
33 BAD_CAST "crt", BAD_CAST "application/x-x509-ca-cert",
34 BAD_CAST "der", BAD_CAST "application/x-x509-ca-cert",
35 BAD_CAST "doc", BAD_CAST "application/msword",
36 BAD_CAST "docx", BAD_CAST "application/vnd.openxmlformats-officedocument."
37 "wordprocessingml.document",
38 BAD_CAST "dbf", BAD_CAST "application/octet-stream",
39 BAD_CAST "prj", BAD_CAST "application/octet-stream",
40 BAD_CAST "qix", BAD_CAST "application/octet-stream",
41 BAD_CAST "sbn", BAD_CAST "application/octet-stream",
42 BAD_CAST "sbx", BAD_CAST "application/octet-stream",
43 BAD_CAST "shp", BAD_CAST "application/octet-stream",
44 BAD_CAST "shx", BAD_CAST "application/octet-stream",
45 BAD_CAST "dgn", BAD_CAST "application/octet-stream",
46 BAD_CAST "dwg", BAD_CAST "image/vnd.dwg",
47 BAD_CAST "edi", BAD_CAST "application/edifact",
48 BAD_CAST "fo", BAD_CAST "application/vnd.software602.filler.form+xml",
49 BAD_CAST "gfs", BAD_CAST "application/xml",
50 BAD_CAST "gml", BAD_CAST "application/xml",
51 BAD_CAST "gif", BAD_CAST "image/gif",
52 BAD_CAST "htm", BAD_CAST "text/html",
53 BAD_CAST "html", BAD_CAST "text/html",
54 BAD_CAST "isdoc", BAD_CAST "text/isdoc",
55 BAD_CAST "isdocx", BAD_CAST "text/isdocx",
56 BAD_CAST "jfif", BAD_CAST "image/jpeg",
57 BAD_CAST "jpg", BAD_CAST "image/jpeg",
58 BAD_CAST "jpeg", BAD_CAST "image/jpeg",
59 BAD_CAST "mpeg", BAD_CAST "video/mpeg",
60 BAD_CAST "mpeg1", BAD_CAST "video/mpeg",
61 BAD_CAST "mpeg2", BAD_CAST "video/mpeg",
62 BAD_CAST "mpg", BAD_CAST "video/mpeg",
63 BAD_CAST "mp2", BAD_CAST "audio/mpeg",
64 BAD_CAST "mp3", BAD_CAST "audio/mpeg",
65 BAD_CAST "odp", BAD_CAST "application/vnd.oasis.opendocument.presentation",
66 BAD_CAST "ods", BAD_CAST "application/vnd.oasis.opendocument.spreadsheet",
67 BAD_CAST "odt", BAD_CAST "application/vnd.oasis.opendocument.text",
68 BAD_CAST "pdf", BAD_CAST "application/pdf",
69 BAD_CAST "p7b", BAD_CAST "application/pkcs7-certificates",
70 BAD_CAST "p7c", BAD_CAST "application/pkcs7-mime",
71 BAD_CAST "p7m", BAD_CAST "application/pkcs7-mime",
72 BAD_CAST "p7f", BAD_CAST "application/pkcs7-signature",
73 BAD_CAST "p7s", BAD_CAST "application/pkcs7-signature",
74 BAD_CAST "pk7", BAD_CAST "application/pkcs7-mime",
75 BAD_CAST "png", BAD_CAST "image/png",
76 BAD_CAST "ppt", BAD_CAST "application/vnd.ms-powerpoint",
77 BAD_CAST "pptx", BAD_CAST "application/vnd.openxmlformats-officedocument."
78 "presentationml.presentation",
79 BAD_CAST "rtf", BAD_CAST "application/rtf",
80 BAD_CAST "tif", BAD_CAST "image/tiff",
81 BAD_CAST "tiff", BAD_CAST "image/tiff",
82 BAD_CAST "tsr", BAD_CAST "application/timestamp-reply",
83 BAD_CAST "tst", BAD_CAST "application/timestamp-reply",
84 BAD_CAST "txt", BAD_CAST "text/plain",
85 BAD_CAST "wav", BAD_CAST "audio/wav",
86 BAD_CAST "xls", BAD_CAST "application/vnd.ms-excel",
87 BAD_CAST "xlsx", BAD_CAST "application/vnd.openxmlformats-officedocument."
88 "spreadsheetml.sheet",
89 BAD_CAST "xml", BAD_CAST "application/xml",
90 BAD_CAST "xsd", BAD_CAST "application/xml",
91 BAD_CAST "zfo", BAD_CAST "application/vnd.software602.filler.form-xml-zip"
94 /* Structure type to hold conversion table from status code to isds_error and
95 * long message */
96 struct code_map_isds_error {
97 const xmlChar **codes; /* NULL terminated array of status codes */
98 const char **meanings; /* Mapping to non-localized long messages */
99 const isds_error *errors; /* Mapping to isds_error code */
102 /* Deallocate structure isds_pki_credentials and NULL it.
103 * Pass-phrase is discarded.
104 * @pki credentials to to free */
105 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
106 if(!pki || !*pki) return;
108 free((*pki)->engine);
109 free((*pki)->certificate);
110 free((*pki)->key);
112 if ((*pki)->passphrase) {
113 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
114 free((*pki)->passphrase);
117 zfree((*pki));
121 /* Free isds_list with all member data.
122 * @list list to free, on return will be NULL */
123 void isds_list_free(struct isds_list **list) {
124 struct isds_list *item, *next_item;
126 if (!list || !*list) return;
128 for(item = *list; item; item = next_item) {
129 if (item->destructor) (item->destructor)(&(item->data));
130 next_item = item->next;
131 free(item);
134 *list = NULL;
138 /* Deallocate structure isds_hash and NULL it.
139 * @hash hash to to free */
140 void isds_hash_free(struct isds_hash **hash) {
141 if(!hash || !*hash) return;
142 free((*hash)->value);
143 zfree((*hash));
147 /* Deallocate structure isds_PersonName recursively and NULL it */
148 static void isds_PersonName_free(struct isds_PersonName **person_name) {
149 if (!person_name || !*person_name) return;
151 free((*person_name)->pnFirstName);
152 free((*person_name)->pnMiddleName);
153 free((*person_name)->pnLastName);
154 free((*person_name)->pnLastNameAtBirth);
156 free(*person_name);
157 *person_name = NULL;
161 /* Deallocate structure isds_BirthInfo recursively and NULL it */
162 static void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
163 if (!birth_info || !*birth_info) return;
165 free((*birth_info)->biDate);
166 free((*birth_info)->biCity);
167 free((*birth_info)->biCounty);
168 free((*birth_info)->biState);
170 free(*birth_info);
171 *birth_info = NULL;
175 /* Deallocate structure isds_Address recursively and NULL it */
176 static void isds_Address_free(struct isds_Address **address) {
177 if (!address || !*address) return;
179 free((*address)->adCity);
180 free((*address)->adStreet);
181 free((*address)->adNumberInStreet);
182 free((*address)->adNumberInMunicipality);
183 free((*address)->adZipCode);
184 free((*address)->adState);
186 free(*address);
187 *address = NULL;
191 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
192 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
193 if (!db_owner_info || !*db_owner_info) return;
195 free((*db_owner_info)->dbID);
196 free((*db_owner_info)->dbType);
197 free((*db_owner_info)->ic);
198 isds_PersonName_free(&((*db_owner_info)->personName));
199 free((*db_owner_info)->firmName);
200 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
201 isds_Address_free(&((*db_owner_info)->address));
202 free((*db_owner_info)->nationality);
203 free((*db_owner_info)->email);
204 free((*db_owner_info)->telNumber);
205 free((*db_owner_info)->identifier);
206 free((*db_owner_info)->registryCode);
207 free((*db_owner_info)->dbState);
208 free((*db_owner_info)->dbEffectiveOVM);
210 free(*db_owner_info);
211 *db_owner_info = NULL;
214 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
215 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
216 if (!db_user_info || !*db_user_info) return;
218 free((*db_user_info)->userID);
219 free((*db_user_info)->userType);
220 free((*db_user_info)->userPrivils);
221 isds_PersonName_free(&((*db_user_info)->personName));
222 isds_Address_free(&((*db_user_info)->address));
223 free((*db_user_info)->biDate);
224 free((*db_user_info)->ic);
225 free((*db_user_info)->firmName);
226 free((*db_user_info)->caStreet);
227 free((*db_user_info)->caCity);
228 free((*db_user_info)->caZipCode);
229 free((*db_user_info)->caState);
231 zfree(*db_user_info);
235 /* Deallocate struct isds_event recursively and NULL it */
236 void isds_event_free(struct isds_event **event) {
237 if (!event || !*event) return;
239 free((*event)->time);
240 free((*event)->type);
241 free((*event)->description);
242 zfree(*event);
246 /* Deallocate struct isds_envelope recursively and NULL it */
247 void isds_envelope_free(struct isds_envelope **envelope) {
248 if (!envelope || !*envelope) return;
250 free((*envelope)->dmID);
251 free((*envelope)->dbIDSender);
252 free((*envelope)->dmSender);
253 free((*envelope)->dmSenderAddress);
254 free((*envelope)->dmSenderType);
255 free((*envelope)->dmRecipient);
256 free((*envelope)->dmRecipientAddress);
257 free((*envelope)->dmAmbiguousRecipient);
258 free((*envelope)->dmType);
260 free((*envelope)->dmOrdinal);
261 free((*envelope)->dmMessageStatus);
262 free((*envelope)->dmDeliveryTime);
263 free((*envelope)->dmAcceptanceTime);
264 isds_hash_free(&(*envelope)->hash);
265 free((*envelope)->timestamp);
266 isds_list_free(&(*envelope)->events);
268 free((*envelope)->dmSenderOrgUnit);
269 free((*envelope)->dmSenderOrgUnitNum);
270 free((*envelope)->dbIDRecipient);
271 free((*envelope)->dmRecipientOrgUnit);
272 free((*envelope)->dmRecipientOrgUnitNum);
273 free((*envelope)->dmToHands);
274 free((*envelope)->dmAnnotation);
275 free((*envelope)->dmRecipientRefNumber);
276 free((*envelope)->dmSenderRefNumber);
277 free((*envelope)->dmRecipientIdent);
278 free((*envelope)->dmSenderIdent);
280 free((*envelope)->dmLegalTitleLaw);
281 free((*envelope)->dmLegalTitleYear);
282 free((*envelope)->dmLegalTitleSect);
283 free((*envelope)->dmLegalTitlePar);
284 free((*envelope)->dmLegalTitlePoint);
286 free((*envelope)->dmPersonalDelivery);
287 free((*envelope)->dmAllowSubstDelivery);
289 free((*envelope)->dmOVM);
290 free((*envelope)->dmPublishOwnID);
292 free(*envelope);
293 *envelope = NULL;
297 /* Deallocate struct isds_message recursively and NULL it */
298 void isds_message_free(struct isds_message **message) {
299 if (!message || !*message) return;
301 free((*message)->raw);
302 isds_envelope_free(&((*message)->envelope));
303 isds_list_free(&((*message)->documents));
304 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
306 free(*message);
307 *message = NULL;
311 /* Deallocate struct isds_document recursively and NULL it */
312 void isds_document_free(struct isds_document **document) {
313 if (!document || !*document) return;
315 if (!(*document)->is_xml) {
316 free((*document)->data);
318 free((*document)->dmMimeType);
319 free((*document)->dmFileGuid);
320 free((*document)->dmUpFileGuid);
321 free((*document)->dmFileDescr);
322 free((*document)->dmFormat);
324 free(*document);
325 *document = NULL;
329 /* Deallocate struct isds_message_copy recursively and NULL it */
330 void isds_message_copy_free(struct isds_message_copy **copy) {
331 if (!copy || !*copy) return;
333 free((*copy)->dbIDRecipient);
334 free((*copy)->dmRecipientOrgUnit);
335 free((*copy)->dmRecipientOrgUnitNum);
336 free((*copy)->dmToHands);
338 free((*copy)->dmStatus);
339 free((*copy)->dmID);
341 zfree(*copy);
345 /* Deallocate struct isds_message_status_change recursively and NULL it */
346 void isds_message_status_change_free(
347 struct isds_message_status_change **message_status_change) {
348 if (!message_status_change || !*message_status_change) return;
350 free((*message_status_change)->dmID);
351 free((*message_status_change)->time);
352 free((*message_status_change)->dmMessageStatus);
354 zfree(*message_status_change);
358 /* Deallocate struct isds_approval recursively and NULL it */
359 void isds_approval_free(struct isds_approval **approval) {
360 if (!approval || !*approval) return;
362 free((*approval)->refference);
364 zfree(*approval);
368 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
369 * The email string is deallocated too. */
370 void isds_credentials_delivery_free(
371 struct isds_credentials_delivery **credentials_delivery) {
372 if (!credentials_delivery || !*credentials_delivery) return;
374 free((*credentials_delivery)->email);
375 free((*credentials_delivery)->token);
376 free((*credentials_delivery)->new_user_name);
378 zfree(*credentials_delivery);
382 /* Deallocate struct isds_commercial_permission recursively and NULL it */
383 void isds_commercial_permission_free(
384 struct isds_commercial_permission **permission) {
385 if (NULL == permission || NULL == *permission) return;
387 free((*permission)->recipient);
388 free((*permission)->payer);
389 free((*permission)->expiration);
390 free((*permission)->count);
391 free((*permission)->reply_identifier);
393 zfree(*permission);
397 /* Deallocate struct isds_credit_event recursively and NULL it */
398 void isds_credit_event_free(struct isds_credit_event **event) {
399 if (NULL == event || NULL == *event) return;
401 free((*event)->time);
402 switch ((*event)->type) {
403 case ISDS_CREDIT_CHARGED:
404 free((*event)->details.charged.transaction);
405 break;
406 case ISDS_CREDIT_DISCHARGED:
407 free((*event)->details.discharged.transaction);
408 break;
409 case ISDS_CREDIT_MESSAGE_SENT:
410 free((*event)->details.message_sent.recipient);
411 free((*event)->details.message_sent.message_id);
412 break;
413 case ISDS_CREDIT_STORAGE_SET:
414 free((*event)->details.storage_set.new_valid_from);
415 free((*event)->details.storage_set.new_valid_to);
416 free((*event)->details.storage_set.old_capacity);
417 free((*event)->details.storage_set.old_valid_from);
418 free((*event)->details.storage_set.old_valid_to);
419 free((*event)->details.storage_set.initiator);
420 break;
421 case ISDS_CREDIT_EXPIRED:
422 break;
425 zfree(*event);
429 /* *DUP_OR_ERROR macros needs error label */
430 #define STRDUP_OR_ERROR(new, template) { \
431 if (!template) { \
432 (new) = NULL; \
433 } else { \
434 (new) = strdup(template); \
435 if (!new) goto error; \
439 #define FLATDUP_OR_ERROR(new, template) { \
440 if (!template) { \
441 (new) = NULL; \
442 } else { \
443 (new) = malloc(sizeof(*(new))); \
444 if (!new) goto error; \
445 memcpy((new), (template), sizeof(*(template))); \
449 /* Copy structure isds_pki_credentials recursively. */
450 struct isds_pki_credentials *isds_pki_credentials_duplicate(
451 const struct isds_pki_credentials *template) {
452 struct isds_pki_credentials *new = NULL;
454 if(!template) return NULL;
456 new = calloc(1, sizeof(*new));
457 if (!new) return NULL;
459 STRDUP_OR_ERROR(new->engine, template->engine);
460 new->certificate_format = template->certificate_format;
461 STRDUP_OR_ERROR(new->certificate, template->certificate);
462 new->key_format = template->key_format;
463 STRDUP_OR_ERROR(new->key, template->key);
464 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
466 return new;
468 error:
469 isds_pki_credentials_free(&new);
470 return NULL;
474 /* Copy structure isds_PersonName recursively */
475 struct isds_PersonName *isds_PersonName_duplicate(
476 const struct isds_PersonName *template) {
477 struct isds_PersonName *new = NULL;
479 if (!template) return NULL;
481 new = calloc(1, sizeof(*new));
482 if (!new) return NULL;
484 STRDUP_OR_ERROR(new->pnFirstName, template->pnFirstName);
485 STRDUP_OR_ERROR(new->pnMiddleName, template->pnMiddleName);
486 STRDUP_OR_ERROR(new->pnLastName, template->pnLastName);
487 STRDUP_OR_ERROR(new->pnLastNameAtBirth, template->pnLastNameAtBirth);
489 return new;
491 error:
492 isds_PersonName_free(&new);
493 return NULL;
497 /* Copy structure isds_BirthInfo recursively */
498 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
499 const struct isds_BirthInfo *template) {
500 struct isds_BirthInfo *new = NULL;
502 if (!template) return NULL;
504 new = calloc(1, sizeof(*new));
505 if (!new) return NULL;
507 FLATDUP_OR_ERROR(new->biDate, template->biDate);
508 STRDUP_OR_ERROR(new->biCity, template->biCity);
509 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
510 STRDUP_OR_ERROR(new->biState, template->biState);
512 return new;
514 error:
515 isds_BirthInfo_free(&new);
516 return NULL;
520 /* Copy structure isds_Address recursively */
521 struct isds_Address *isds_Address_duplicate(
522 const struct isds_Address *template) {
523 struct isds_Address *new = NULL;
525 if (!template) return NULL;
527 new = calloc(1, sizeof(*new));
528 if (!new) return NULL;
530 STRDUP_OR_ERROR(new->adCity, template->adCity);
531 STRDUP_OR_ERROR(new->adStreet, template->adStreet);
532 STRDUP_OR_ERROR(new->adNumberInStreet, template->adNumberInStreet);
533 STRDUP_OR_ERROR(new->adNumberInMunicipality,
534 template->adNumberInMunicipality);
535 STRDUP_OR_ERROR(new->adZipCode, template->adZipCode);
536 STRDUP_OR_ERROR(new->adState, template->adState);
538 return new;
540 error:
541 isds_Address_free(&new);
542 return NULL;
546 /* Copy structure isds_DbOwnerInfo recursively */
547 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
548 const struct isds_DbOwnerInfo *template) {
549 struct isds_DbOwnerInfo *new = NULL;
550 if (!template) return NULL;
552 new = calloc(1, sizeof(*new));
553 if (!new) return NULL;
555 STRDUP_OR_ERROR(new->dbID, template->dbID);
556 FLATDUP_OR_ERROR(new->dbType, template->dbType);
557 STRDUP_OR_ERROR(new->ic, template->ic);
559 if (template->personName) {
560 if (!(new->personName =
561 isds_PersonName_duplicate(template->personName)))
562 goto error;
565 STRDUP_OR_ERROR(new->firmName, template->firmName);
567 if (template->birthInfo) {
568 if (!(new->birthInfo =
569 isds_BirthInfo_duplicate(template->birthInfo)))
570 goto error;
573 if (template->address) {
574 if (!(new->address = isds_Address_duplicate(template->address)))
575 goto error;
578 STRDUP_OR_ERROR(new->nationality, template->nationality);
579 STRDUP_OR_ERROR(new->email, template->email);
580 STRDUP_OR_ERROR(new->telNumber, template->telNumber);
581 STRDUP_OR_ERROR(new->identifier, template->identifier);
582 STRDUP_OR_ERROR(new->registryCode, template->registryCode);
583 FLATDUP_OR_ERROR(new->dbState, template->dbState);
584 FLATDUP_OR_ERROR(new->dbEffectiveOVM, template->dbEffectiveOVM);
585 FLATDUP_OR_ERROR(new->dbOpenAddressing, template->dbOpenAddressing);
587 return new;
589 error:
590 isds_DbOwnerInfo_free(&new);
591 return NULL;
595 /* Copy structure isds_DbUserInfo recursively */
596 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
597 const struct isds_DbUserInfo *template) {
598 struct isds_DbUserInfo *new = NULL;
599 if (!template) return NULL;
601 new = calloc(1, sizeof(*new));
602 if (!new) return NULL;
604 STRDUP_OR_ERROR(new->userID, template->userID);
605 FLATDUP_OR_ERROR(new->userType, template->userType);
606 FLATDUP_OR_ERROR(new->userPrivils, template->userPrivils);
608 if (template->personName) {
609 if (!(new->personName =
610 isds_PersonName_duplicate(template->personName)))
611 goto error;
614 if (template->address) {
615 if (!(new->address = isds_Address_duplicate(template->address)))
616 goto error;
619 FLATDUP_OR_ERROR(new->biDate, template->biDate);
620 STRDUP_OR_ERROR(new->ic, template->ic);
621 STRDUP_OR_ERROR(new->firmName, template->firmName);
622 STRDUP_OR_ERROR(new->caStreet, template->caStreet);
623 STRDUP_OR_ERROR(new->caCity, template->caCity);
624 STRDUP_OR_ERROR(new->caZipCode, template->caZipCode);
625 STRDUP_OR_ERROR(new->caState, template->caState);
627 return new;
629 error:
630 isds_DbUserInfo_free(&new);
631 return NULL;
634 #undef FLATDUP_OR_ERROR
635 #undef STRDUP_OR_ERROR
638 /* Logs libxml2 errors. Should be registered to libxml2 library.
639 * @ctx is unused currently
640 * @msg is printf-like formated message from libxml2 (UTF-8?)
641 * @... are variadic arguments for @msg */
642 static void log_xml(void *ctx, const char *msg, ...) {
643 va_list ap;
644 char *text = NULL;
646 if (!msg) return;
648 va_start(ap, msg);
649 isds_vasprintf(&text, msg, ap);
650 va_end(ap);
652 if (text)
653 isds_log(ILF_XML, ILL_ERR, "%s", text);
654 free(text);
658 /* Initialize ISDS library.
659 * Global function, must be called before other functions.
660 * If it fails you can not use ISDS library and must call isds_cleanup() to
661 * free partially initialized global variables. */
662 isds_error isds_init(void) {
663 /* NULL global variables */
664 log_facilities = ILF_ALL;
665 log_level = ILL_WARNING;
666 log_callback = NULL;
667 log_callback_data = NULL;
669 #if ENABLE_NLS
670 /* Initialize gettext */
671 bindtextdomain(PACKAGE, LOCALEDIR);
672 #endif
674 #if HAVE_LIBCURL
675 /* Initialize CURL */
676 if (curl_global_init(CURL_GLOBAL_ALL)) {
677 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
678 return IE_ERROR;
680 #endif /* HAVE_LIBCURL */
682 /* Initialize gpg-error because of gpgme and ksba */
683 if (gpg_err_init()) {
684 isds_log(ILF_ISDS, ILL_CRIT,
685 _("gpg-error library initialization failed\n"));
686 return IE_ERROR;
689 /* Initialize GPGME */
690 if (_isds_init_gpgme(&version_gpgme)) {
691 isds_log(ILF_ISDS, ILL_CRIT,
692 _("GPGME library initialization failed\n"));
693 return IE_ERROR;
696 /* Initialize gcrypt */
697 if (_isds_init_gcrypt(&version_gcrypt)) {
698 isds_log(ILF_ISDS, ILL_CRIT,
699 _("gcrypt library initialization failed\n"));
700 return IE_ERROR;
703 /* This can _exit() current program. Find not so assertive check. */
704 LIBXML_TEST_VERSION;
705 xmlSetGenericErrorFunc(NULL, log_xml);
707 /* Check expat */
708 if (_isds_init_expat(&version_expat)) {
709 isds_log(ILF_ISDS, ILL_CRIT,
710 _("expat library initialization failed\n"));
711 return IE_ERROR;
714 /* Allocate global variables */
717 return IE_SUCCESS;
721 /* Deinitialize ISDS library.
722 * Global function, must be called as last library function. */
723 isds_error isds_cleanup(void) {
724 /* XML */
725 xmlCleanupParser();
727 #if HAVE_LIBCURL
728 /* Curl */
729 curl_global_cleanup();
730 #endif
732 return IE_SUCCESS;
736 /* Return version string of this library. Version of dependencies can be
737 * embedded. Do no try to parse it. You must free it. */
738 char *isds_version(void) {
739 char *buffer = NULL;
741 isds_asprintf(&buffer,
742 #if HAVE_LIBCURL
743 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
744 #else
745 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
746 #endif
747 PACKAGE_VERSION,
748 #if HAVE_LIBCURL
749 curl_version(),
750 #endif
751 version_gpgme, version_gcrypt,
752 version_expat, xmlParserVersion);
753 return buffer;
757 /* Return text description of ISDS error */
758 const char *isds_strerror(const isds_error error) {
759 switch (error) {
760 case IE_SUCCESS:
761 return(_("Success")); break;
762 case IE_ERROR:
763 return(_("Unspecified error")); break;
764 case IE_NOTSUP:
765 return(_("Not supported")); break;
766 case IE_INVAL:
767 return(_("Invalid value")); break;
768 case IE_INVALID_CONTEXT:
769 return(_("Invalid context")); break;
770 case IE_NOT_LOGGED_IN:
771 return(_("Not logged in")); break;
772 case IE_CONNECTION_CLOSED:
773 return(_("Connection closed")); break;
774 case IE_TIMED_OUT:
775 return(_("Timed out")); break;
776 case IE_NOEXIST:
777 return(_("Not exist")); break;
778 case IE_NOMEM:
779 return(_("Out of memory")); break;
780 case IE_NETWORK:
781 return(_("Network problem")); break;
782 case IE_HTTP:
783 return(_("HTTP problem")); break;
784 case IE_SOAP:
785 return(_("SOAP problem")); break;
786 case IE_XML:
787 return(_("XML problem")); break;
788 case IE_ISDS:
789 return(_("ISDS server problem")); break;
790 case IE_ENUM:
791 return(_("Invalid enum value")); break;
792 case IE_DATE:
793 return(_("Invalid date value")); break;
794 case IE_2BIG:
795 return(_("Too big")); break;
796 case IE_2SMALL:
797 return(_("Too small")); break;
798 case IE_NOTUNIQ:
799 return(_("Value not unique")); break;
800 case IE_NOTEQUAL:
801 return(_("Values not equal")); break;
802 case IE_PARTIAL_SUCCESS:
803 return(_("Some suboperations failed")); break;
804 case IE_ABORTED:
805 return(_("Operation aborted")); break;
806 case IE_SECURITY:
807 return(_("Security problem")); break;
808 default:
809 return(_("Unknown error"));
814 /* Create ISDS context.
815 * Each context can be used for different sessions to (possibly) different
816 * ISDS server with different credentials. */
817 struct isds_ctx *isds_ctx_create(void) {
818 struct isds_ctx *context;
819 context = malloc(sizeof(*context));
820 if (context) memset(context, 0, sizeof(*context));
821 return context;
824 #if HAVE_LIBCURL
825 /* Close possibly opened connection to Czech POINT document deposit without
826 * resetting long_message buffer.
827 * XXX: Do not use czp_close_connection() if you do not want to destroy log
828 * message.
829 * @context is Czech POINT session context. */
830 static isds_error czp_do_close_connection(struct isds_ctx *context) {
831 if (!context) return IE_INVALID_CONTEXT;
832 _isds_close_connection(context);
833 return IE_SUCCESS;
837 /* Discard credentials.
838 * @context is ISDS context
839 * @discard_saved_username is true for removing saved username, false for
840 * keeping it.
841 * Only that. It does not cause log out, connection close or similar. */
842 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context,
843 _Bool discard_saved_username) {
844 if(!context) return IE_INVALID_CONTEXT;
846 if (context->username) {
847 memset(context->username, 0, strlen(context->username));
848 zfree(context->username);
850 if (context->password) {
851 memset(context->password, 0, strlen(context->password));
852 zfree(context->password);
854 isds_pki_credentials_free(&context->pki_credentials);
855 if (discard_saved_username && context->saved_username) {
856 memset(context->saved_username, 0, strlen(context->saved_username));
857 zfree(context->saved_username);
860 return IE_SUCCESS;
862 #endif /* HAVE_LIBCURL */
865 /* Destroy ISDS context and free memory.
866 * @context will be NULLed on success. */
867 isds_error isds_ctx_free(struct isds_ctx **context) {
868 if (!context || !*context) {
869 return IE_INVALID_CONTEXT;
872 #if HAVE_LIBCURL
873 /* Discard credentials and close connection */
874 switch ((*context)->type) {
875 case CTX_TYPE_NONE: break;
876 case CTX_TYPE_ISDS: isds_logout(*context); break;
877 case CTX_TYPE_CZP:
878 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
879 czp_do_close_connection(*context); break;
882 /* For sure */
883 _isds_discard_credentials(*context, 1);
885 /* Free other structures */
886 free((*context)->tls_verify_server);
887 free((*context)->tls_ca_file);
888 free((*context)->tls_ca_dir);
889 free((*context)->tls_crl_file);
890 #endif /* HAVE_LIBCURL */
891 free((*context)->long_message);
893 free(*context);
894 *context = NULL;
895 return IE_SUCCESS;
899 /* Return long message text produced by library function, e.g. detailed error
900 * message. Returned pointer is only valid until new library function is
901 * called for the same context. Could be NULL, especially if NULL context is
902 * supplied. Return string is locale encoded. */
903 char *isds_long_message(const struct isds_ctx *context) {
904 if (!context) return NULL;
905 return context->long_message;
909 /* Stores message into context' long_message buffer.
910 * Application can pick the message up using isds_long_message().
911 * NULL @message truncates the buffer but does not deallocate it.
912 * @message is coded in locale encoding */
913 _hidden isds_error isds_log_message(struct isds_ctx *context,
914 const char *message) {
915 char *buffer;
916 size_t length;
918 if (!context) return IE_INVALID_CONTEXT;
920 /* FIXME: Check for integer overflow */
921 length = 1 + ((message) ? strlen(message) : 0);
922 buffer = realloc(context->long_message, length);
923 if (!buffer) return IE_NOMEM;
925 if (message)
926 strcpy(buffer, message);
927 else
928 *buffer = '\0';
930 context->long_message = buffer;
931 return IE_SUCCESS;
935 /* Appends message into context' long_message buffer.
936 * Application can pick the message up using isds_long_message().
937 * NULL message has void effect. */
938 _hidden isds_error isds_append_message(struct isds_ctx *context,
939 const char *message) {
940 char *buffer;
941 size_t old_length, length;
943 if (!context) return IE_INVALID_CONTEXT;
944 if (!message) return IE_SUCCESS;
945 if (!context->long_message)
946 return isds_log_message(context, message);
948 old_length = strlen(context->long_message);
949 /* FIXME: Check for integer overflow */
950 length = 1 + old_length + strlen(message);
951 buffer = realloc(context->long_message, length);
952 if (!buffer) return IE_NOMEM;
954 strcpy(buffer + old_length, message);
956 context->long_message = buffer;
957 return IE_SUCCESS;
961 /* Stores formatted message into context' long_message buffer.
962 * Application can pick the message up using isds_long_message(). */
963 _hidden isds_error isds_printf_message(struct isds_ctx *context,
964 const char *format, ...) {
965 va_list ap;
966 int length;
968 if (!context) return IE_INVALID_CONTEXT;
969 va_start(ap, format);
970 length = isds_vasprintf(&(context->long_message), format, ap);
971 va_end(ap);
973 return (length < 0) ? IE_ERROR: IE_SUCCESS;
977 /* Set logging up.
978 * @facilities is bit mask of isds_log_facility values,
979 * @level is verbosity level. */
980 void isds_set_logging(const unsigned int facilities,
981 const isds_log_level level) {
982 log_facilities = facilities;
983 log_level = level;
987 /* Register callback function libisds calls when new global log message is
988 * produced by library. Library logs to stderr by default.
989 * @callback is function provided by application libisds will call. See type
990 * definition for @callback argument explanation. Pass NULL to revert logging to
991 * default behaviour.
992 * @data is application specific data @callback gets as last argument */
993 void isds_set_log_callback(isds_log_callback callback, void *data) {
994 log_callback = callback;
995 log_callback_data = data;
999 /* Log @message in class @facility with log @level into global log. @message
1000 * is printf(3) formatting string, variadic arguments may be necessary.
1001 * For debugging purposes. */
1002 _hidden isds_error isds_log(const isds_log_facility facility,
1003 const isds_log_level level, const char *message, ...) {
1004 va_list ap;
1005 char *buffer = NULL;
1006 int length;
1008 if (level > log_level) return IE_SUCCESS;
1009 if (!(log_facilities & facility)) return IE_SUCCESS;
1010 if (!message) return IE_INVAL;
1012 if (log_callback) {
1013 /* Pass message to application supplied callback function */
1014 va_start(ap, message);
1015 length = isds_vasprintf(&buffer, message, ap);
1016 va_end(ap);
1018 if (length == -1) {
1019 return IE_ERROR;
1021 if (length > 0) {
1022 log_callback(facility, level, buffer, length, log_callback_data);
1024 free(buffer);
1025 } else {
1026 /* Default: Log it to stderr */
1027 va_start(ap, message);
1028 vfprintf(stderr, message, ap);
1029 va_end(ap);
1030 /* Line buffered printf is default.
1031 * fflush(stderr);*/
1034 return IE_SUCCESS;
1038 /* Set timeout in milliseconds for each network job like connecting to server
1039 * or sending message. Use 0 to disable timeout limits. */
1040 isds_error isds_set_timeout(struct isds_ctx *context,
1041 const unsigned int timeout) {
1042 if (!context) return IE_INVALID_CONTEXT;
1043 zfree(context->long_message);
1045 #if HAVE_LIBCURL
1046 context->timeout = timeout;
1048 if (context->curl) {
1049 CURLcode curl_err;
1051 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
1052 if (!curl_err)
1053 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1054 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
1055 context->timeout);
1056 #else
1057 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
1058 context->timeout / 1000);
1059 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1060 if (curl_err) return IE_ERROR;
1063 return IE_SUCCESS;
1064 #else /* not HAVE_LIBCURL */
1065 return IE_NOTSUP;
1066 #endif
1070 /* Register callback function libisds calls periodically during HTTP data
1071 * transfer.
1072 * @context is session context
1073 * @callback is function provided by application libisds will call. See type
1074 * definition for @callback argument explanation.
1075 * @data is application specific data @callback gets as last argument */
1076 isds_error isds_set_progress_callback(struct isds_ctx *context,
1077 isds_progress_callback callback, void *data) {
1078 if (!context) return IE_INVALID_CONTEXT;
1079 zfree(context->long_message);
1081 #if HAVE_LIBCURL
1082 context->progress_callback = callback;
1083 context->progress_callback_data = data;
1085 return IE_SUCCESS;
1086 #else /* not HAVE_LIBCURL */
1087 return IE_NOTSUP;
1088 #endif
1092 /* Change context settings.
1093 * @context is context which setting will be applied to
1094 * @option is name of option. It determines the type of last argument. See
1095 * isds_option definition for more info.
1096 * @... is value of new setting. Type is determined by @option
1097 * */
1098 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1099 ...) {
1100 isds_error err = IE_SUCCESS;
1101 va_list ap;
1102 #if HAVE_LIBCURL
1103 char *pointer, *string;
1104 #endif
1106 if (!context) return IE_INVALID_CONTEXT;
1107 zfree(context->long_message);
1109 va_start(ap, option);
1111 #define REPLACE_VA_BOOLEAN(destination) { \
1112 if (!(destination)) { \
1113 (destination) = malloc(sizeof(*(destination))); \
1114 if (!(destination)) { \
1115 err = IE_NOMEM; goto leave; \
1118 *(destination) = (_Bool) !!va_arg(ap, int); \
1121 #define REPLACE_VA_STRING(destination) { \
1122 string = va_arg(ap, char *); \
1123 if (string) { \
1124 pointer = realloc((destination), 1 + strlen(string)); \
1125 if (!pointer) { err = IE_NOMEM; goto leave; } \
1126 strcpy(pointer, string); \
1127 (destination) = pointer; \
1128 } else { \
1129 free(destination); \
1130 (destination) = NULL; \
1134 switch (option) {
1135 case IOPT_TLS_VERIFY_SERVER:
1136 #if HAVE_LIBCURL
1137 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1138 #else
1139 err = IE_NOTSUP; goto leave;
1140 #endif
1141 break;
1142 case IOPT_TLS_CA_FILE:
1143 #if HAVE_LIBCURL
1144 REPLACE_VA_STRING(context->tls_ca_file);
1145 #else
1146 err = IE_NOTSUP; goto leave;
1147 #endif
1148 break;
1149 case IOPT_TLS_CA_DIRECTORY:
1150 #if HAVE_LIBCURL
1151 REPLACE_VA_STRING(context->tls_ca_dir);
1152 #else
1153 err = IE_NOTSUP; goto leave;
1154 #endif
1155 break;
1156 case IOPT_TLS_CRL_FILE:
1157 #if HAVE_LIBCURL
1158 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1159 REPLACE_VA_STRING(context->tls_crl_file);
1160 #else
1161 isds_log_message(context,
1162 _("Curl library does not support CRL definition"));
1163 err = IE_NOTSUP;
1164 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1165 #else
1166 err = IE_NOTSUP; goto leave;
1167 #endif /* not HAVE_LIBCURL */
1168 break;
1169 case IOPT_NORMALIZE_MIME_TYPE:
1170 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1171 break;
1173 default:
1174 err = IE_ENUM; goto leave;
1177 #undef REPLACE_VA_STRING
1178 #undef REPLACE_VA_BOOLEAN
1180 leave:
1181 va_end(ap);
1182 return err;
1186 #if HAVE_LIBCURL
1187 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1188 * Destination for NULL argument will not be touched.
1189 * Destination pointers must be freed before calling this function.
1190 * If @username is @context->saved_username, the saved_username will not be
1191 * replaced. The saved_username is clobbered only if context has set otp
1192 * member.
1193 * Return IE_SUCCESS on success. */
1194 static isds_error _isds_store_credentials(struct isds_ctx *context,
1195 const char *username, const char *password,
1196 const struct isds_pki_credentials *pki_credentials) {
1197 if (NULL == context) return IE_INVALID_CONTEXT;
1199 /* FIXME: mlock password
1200 * (I have a library) */
1202 if (username) {
1203 context->username = strdup(username);
1204 if (context->otp && context->saved_username != username)
1205 context->saved_username = strdup(username);
1207 if (password) {
1208 if (NULL == context->otp_credentials)
1209 context->password = strdup(password);
1210 else
1211 context->password = _isds_astrcat(password,
1212 context->otp_credentials->otp_code);
1214 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1216 if ((NULL != username && NULL == context->username) ||
1217 (NULL != password && NULL == context->password) ||
1218 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1219 (context->otp && NULL != context->username &&
1220 NULL == context->saved_username)) {
1221 return IE_NOMEM;
1224 return IE_SUCCESS;
1226 #endif
1229 /* Connect and log into ISDS server.
1230 * All required arguments will be copied, you do not have to keep them after
1231 * that.
1232 * ISDS supports six different authentication methods. Exact method is
1233 * selected on @username, @password, @pki_credentials, and @otp arguments:
1234 * - If @pki_credentials == NULL, @username and @password must be supplied
1235 * and then
1236 * - If @otp == NULL, simple authentication by username and password will
1237 * be proceeded.
1238 * - If @otp != NULL, authentication by username and password and OTP
1239 * will be used.
1240 * - If @pki_credentials != NULL, then
1241 * - If @username == NULL, only certificate will be used
1242 * - If @username != NULL, then
1243 * - If @password == NULL, then certificate will be used and
1244 * @username shifts meaning to box ID. This is used for hosted
1245 * services.
1246 * - Otherwise all three arguments will be used.
1247 * Please note, that different cases require different certificate type
1248 * (system qualified one or commercial non qualified one). This library
1249 * does not check such political issues. Please see ISDS Specification
1250 * for more details.
1251 * @url is base address of ISDS web service. Pass extern isds_locator
1252 * variable to use production ISDS instance without client certificate
1253 * authentication (or extern isds_cert_locator with client certificate
1254 * authentication or extern isds_otp_locators with OTP authentication).
1255 * Passing NULL has the same effect, autoselection between isds_locator,
1256 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1257 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1258 * isds_otp_testing_locator) variable to select testing instance.
1259 * @username is user name of ISDS user or box ID
1260 * @password is user's secret password
1261 * @pki_credentials defines public key cryptographic material to use in client
1262 * authentication.
1263 * @otp selects one-time password authentication method to use, defines OTP
1264 * code (if known) and returns fine grade resolution of OTP procedure.
1265 * @return:
1266 * IE_SUCCESS if authentication succeeds
1267 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1268 * requested, fine grade reason will be set into @otp->resolution. Error
1269 * message from server can be obtained by isds_long_message() call.
1270 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1271 * server has sent OTP code through side channel. Application is expected to
1272 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1273 * this call to complete second phase of TOTP authentication;
1274 * or other appropriate error. */
1275 isds_error isds_login(struct isds_ctx *context, const char *url,
1276 const char *username, const char *password,
1277 const struct isds_pki_credentials *pki_credentials,
1278 struct isds_otp *otp) {
1279 #if HAVE_LIBCURL
1280 isds_error err = IE_NOT_LOGGED_IN;
1281 isds_error soap_err;
1282 xmlNsPtr isds_ns = NULL;
1283 xmlNodePtr request = NULL;
1284 xmlNodePtr response = NULL;
1285 #endif /* HAVE_LIBCURL */
1287 if (!context) return IE_INVALID_CONTEXT;
1288 zfree(context->long_message);
1290 #if HAVE_LIBCURL
1291 /* Close connection if already logged in */
1292 if (context->curl) {
1293 _isds_close_connection(context);
1296 /* Store configuration */
1297 context->type = CTX_TYPE_ISDS;
1298 zfree(context->url);
1300 /* Mangle base URI according to requested authentication method */
1301 if (NULL == pki_credentials) {
1302 isds_log(ILF_SEC, ILL_INFO,
1303 _("Selected authentication method: no certificate, "
1304 "username and password\n"));
1305 if (!username || !password) {
1306 isds_log_message(context,
1307 _("Both username and password must be supplied"));
1308 return IE_INVAL;
1310 context->otp_credentials = otp;
1311 context->otp = (NULL != context->otp_credentials);
1313 if (!context->otp) {
1314 /* Default locator is official system (without certificate or
1315 * OTP) */
1316 context->url = strdup((NULL != url) ? url : isds_locator);
1317 } else {
1318 const char *authenticator_uri = NULL;
1319 if (!url) url = isds_otp_locator;
1320 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1321 switch (context->otp_credentials->method) {
1322 case OTP_HMAC:
1323 isds_log(ILF_SEC, ILL_INFO,
1324 _("Selected authentication method: "
1325 "HMAC-based one-time password\n"));
1326 authenticator_uri =
1327 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1328 break;
1329 case OTP_TIME:
1330 isds_log(ILF_SEC, ILL_INFO,
1331 _("Selected authentication method: "
1332 "Time-based one-time password\n"));
1333 if (context->otp_credentials->otp_code == NULL) {
1334 isds_log(ILF_SEC, ILL_INFO,
1335 _("OTP code has not been provided by "
1336 "application, requesting server for "
1337 "new one.\n"));
1338 authenticator_uri =
1339 "%1$sas/processLogin?type=totp&sendSms=true&"
1340 "uri=%1$sapps/";
1341 } else {
1342 isds_log(ILF_SEC, ILL_INFO,
1343 _("OTP code has been provided by "
1344 "application, not requesting server "
1345 "for new one.\n"));
1346 authenticator_uri =
1347 "%1$sas/processLogin?type=totp&"
1348 "uri=%1$sapps/";
1350 break;
1351 default:
1352 isds_log_message(context,
1353 _("Unknown one-time password authentication "
1354 "method requested by application"));
1355 return IE_ENUM;
1357 if (-1 == isds_asprintf(&context->url, authenticator_uri, url))
1358 return IE_NOMEM;
1360 } else {
1361 /* Default locator is official system (with client certificate) */
1362 context->otp = 0;
1363 context->otp_credentials = NULL;
1364 if (!url) url = isds_cert_locator;
1366 if (!username) {
1367 isds_log(ILF_SEC, ILL_INFO,
1368 _("Selected authentication method: system certificate, "
1369 "no username and no password\n"));
1370 password = NULL;
1371 context->url = _isds_astrcat(url, "cert/");
1372 } else {
1373 if (!password) {
1374 isds_log(ILF_SEC, ILL_INFO,
1375 _("Selected authentication method: system certificate, "
1376 "box ID and no password\n"));
1377 context->url = _isds_astrcat(url, "hspis/");
1378 } else {
1379 isds_log(ILF_SEC, ILL_INFO,
1380 _("Selected authentication method: commercial "
1381 "certificate, username and password\n"));
1382 context->url = _isds_astrcat(url, "certds/");
1386 if (!(context->url))
1387 return IE_NOMEM;
1389 /* Prepare CURL handle */
1390 context->curl = curl_easy_init();
1391 if (!(context->curl))
1392 return IE_ERROR;
1394 /* Build log-in request */
1395 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1396 if (!request) {
1397 isds_log_message(context, _("Could not build ISDS log-in request"));
1398 return IE_ERROR;
1400 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1401 if(!isds_ns) {
1402 isds_log_message(context, _("Could not create ISDS name space"));
1403 xmlFreeNode(request);
1404 return IE_ERROR;
1406 xmlSetNs(request, isds_ns);
1408 /* Store credentials */
1409 _isds_discard_credentials(context, 1);
1410 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1411 _isds_discard_credentials(context, 1);
1412 xmlFreeNode(request);
1413 return IE_NOMEM;
1416 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1417 username, url);
1419 /* Send log-in request */
1420 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1422 if (context->otp) {
1423 /* Revert context URL from OTP authentication service URL to OTP web
1424 * service base URL for subsequent calls. Potenial isds_login() retry
1425 * will re-set context URL again. */
1426 zfree(context->url);
1427 context->url = _isds_astrcat(url, "apps/");
1428 if (context->url == NULL) {
1429 soap_err = IE_NOMEM;
1431 /* Detach pointer to OTP credentials from context */
1432 context->otp_credentials = NULL;
1435 /* Remove credentials */
1436 _isds_discard_credentials(context, 0);
1438 /* Destroy log-in request */
1439 xmlFreeNode(request);
1441 if (soap_err) {
1442 xmlFreeNodeList(response);
1443 _isds_close_connection(context);
1444 return soap_err;
1447 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1448 * authentication succeeded if soap_err == IE_SUCCESS */
1449 err = IE_SUCCESS;
1451 xmlFreeNodeList(response);
1453 if (!err)
1454 isds_log(ILF_ISDS, ILL_DEBUG,
1455 _("User %s has been logged into server %s successfully\n"),
1456 username, url);
1457 return err;
1458 #else /* not HAVE_LIBCURL */
1459 return IE_NOTSUP;
1460 #endif
1464 /* Log out from ISDS server discards credentials and connection configuration. */
1465 isds_error isds_logout(struct isds_ctx *context) {
1466 if (!context) return IE_INVALID_CONTEXT;
1467 zfree(context->long_message);
1469 #if HAVE_LIBCURL
1470 if (context->curl) {
1471 if (context->otp) {
1472 isds_error err = _isds_invalidate_otp_cookie(context);
1473 if (err) return err;
1476 /* Close connection */
1477 _isds_close_connection(context);
1479 /* Discard credentials for sure. They should not survive isds_login(),
1480 * even successful .*/
1481 _isds_discard_credentials(context, 1);
1482 zfree(context->url);
1484 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1485 } else {
1486 _isds_discard_credentials(context, 1);
1488 return IE_SUCCESS;
1489 #else /* not HAVE_LIBCURL */
1490 return IE_NOTSUP;
1491 #endif
1495 /* Verify connection to ISDS is alive and server is responding.
1496 * Send dummy request to ISDS and expect dummy response. */
1497 isds_error isds_ping(struct isds_ctx *context) {
1498 #if HAVE_LIBCURL
1499 isds_error soap_err;
1500 xmlNsPtr isds_ns = NULL;
1501 xmlNodePtr request = NULL;
1502 xmlNodePtr response = NULL;
1503 #endif /* HAVE_LIBCURL */
1505 if (!context) return IE_INVALID_CONTEXT;
1506 zfree(context->long_message);
1508 #if HAVE_LIBCURL
1509 /* Check if connection is established */
1510 if (!context->curl) return IE_CONNECTION_CLOSED;
1513 /* Build dummy request */
1514 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1515 if (!request) {
1516 isds_log_message(context, _("Could build ISDS dummy request"));
1517 return IE_ERROR;
1519 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1520 if(!isds_ns) {
1521 isds_log_message(context, _("Could not create ISDS name space"));
1522 xmlFreeNode(request);
1523 return IE_ERROR;
1525 xmlSetNs(request, isds_ns);
1527 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1529 /* Sent dummy request */
1530 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1532 /* Destroy log-in request */
1533 xmlFreeNode(request);
1535 if (soap_err) {
1536 isds_log(ILF_ISDS, ILL_DEBUG,
1537 _("ISDS server could not be contacted\n"));
1538 xmlFreeNodeList(response);
1539 return soap_err;
1542 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1543 * authentication succeeded if soap_err == IE_SUCCESS */
1544 /* TODO: ISDS documentation does not specify response body.
1545 * However real server sends back DummyOperationResponse */
1548 xmlFreeNodeList(response);
1550 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1552 return IE_SUCCESS;
1553 #else /* not HAVE_LIBCURL */
1554 return IE_NOTSUP;
1555 #endif
1559 /* Send bogus request to ISDS.
1560 * Just for test purposes */
1561 isds_error isds_bogus_request(struct isds_ctx *context) {
1562 #if HAVE_LIBCURL
1563 isds_error err;
1564 xmlNsPtr isds_ns = NULL;
1565 xmlNodePtr request = NULL;
1566 xmlDocPtr response = NULL;
1567 xmlChar *code = NULL, *message = NULL;
1568 #endif
1570 if (!context) return IE_INVALID_CONTEXT;
1571 zfree(context->long_message);
1573 #if HAVE_LIBCURL
1574 /* Check if connection is established */
1575 if (!context->curl) {
1576 /* Testing printf message */
1577 isds_printf_message(context, "%s", _("I said connection closed"));
1578 return IE_CONNECTION_CLOSED;
1582 /* Build dummy request */
1583 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1584 if (!request) {
1585 isds_log_message(context, _("Could build ISDS bogus request"));
1586 return IE_ERROR;
1588 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1589 if(!isds_ns) {
1590 isds_log_message(context, _("Could not create ISDS name space"));
1591 xmlFreeNode(request);
1592 return IE_ERROR;
1594 xmlSetNs(request, isds_ns);
1596 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1598 /* Sent bogus request */
1599 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1601 /* Destroy request */
1602 xmlFreeNode(request);
1604 if (err) {
1605 isds_log(ILF_ISDS, ILL_DEBUG,
1606 _("Processing ISDS response on bogus request failed\n"));
1607 xmlFreeDoc(response);
1608 return err;
1611 /* Check for response status */
1612 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1613 &code, &message, NULL);
1614 if (err) {
1615 isds_log(ILF_ISDS, ILL_DEBUG,
1616 _("ISDS response on bogus request is missing status\n"));
1617 free(code);
1618 free(message);
1619 xmlFreeDoc(response);
1620 return err;
1622 if (xmlStrcmp(code, BAD_CAST "0000")) {
1623 char *code_locale = _isds_utf82locale((char*)code);
1624 char *message_locale = _isds_utf82locale((char*)message);
1625 isds_log(ILF_ISDS, ILL_DEBUG,
1626 _("Server refused bogus request (code=%s, message=%s)\n"),
1627 code_locale, message_locale);
1628 /* XXX: Literal error messages from ISDS are Czech messages
1629 * (English sometimes) in UTF-8. It's hard to catch them for
1630 * translation. Successfully gettextized would return in locale
1631 * encoding, unsuccessfully translated would pass in UTF-8. */
1632 isds_log_message(context, message_locale);
1633 free(code_locale);
1634 free(message_locale);
1635 free(code);
1636 free(message);
1637 xmlFreeDoc(response);
1638 return IE_ISDS;
1642 free(code);
1643 free(message);
1644 xmlFreeDoc(response);
1646 isds_log(ILF_ISDS, ILL_DEBUG,
1647 _("Bogus message accepted by server. This should not happen.\n"));
1649 return IE_SUCCESS;
1650 #else /* not HAVE_LIBCURL */
1651 return IE_NOTSUP;
1652 #endif
1656 #if HAVE_LIBCURL
1657 /* Serialize XML subtree to buffer preserving XML indentation.
1658 * @context is session context
1659 * @subtree is XML element to be serialized (with children)
1660 * @buffer is automatically reallocated buffer where serialize to
1661 * @length is size of serialized stream in bytes
1662 * @return standard error code, free @buffer in case of error */
1663 static isds_error serialize_subtree(struct isds_ctx *context,
1664 xmlNodePtr subtree, void **buffer, size_t *length) {
1665 isds_error err = IE_SUCCESS;
1666 xmlBufferPtr xml_buffer = NULL;
1667 xmlSaveCtxtPtr save_ctx = NULL;
1668 xmlDocPtr subtree_doc = NULL;
1669 xmlNodePtr subtree_copy;
1670 xmlNsPtr isds_ns;
1671 void *new_buffer;
1673 if (!context) return IE_INVALID_CONTEXT;
1674 if (!buffer) return IE_INVAL;
1675 zfree(*buffer);
1676 if (!subtree || !length) return IE_INVAL;
1678 /* Make temporary XML document with @subtree root element */
1679 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1680 * It can result in not well-formed on invalid XML tree (e.g. name space
1681 * prefix definition can miss. */
1682 /*FIXME */
1684 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1685 if (!subtree_doc) {
1686 isds_log_message(context, _("Could not build temporary document"));
1687 err = IE_ERROR;
1688 goto leave;
1691 /* XXX: Copy subtree and attach the copy to document.
1692 * One node can not bee attached into more document at the same time.
1693 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1694 * automatically.
1695 * XXX: Check xmlSaveTree() too. */
1696 subtree_copy = xmlCopyNodeList(subtree);
1697 if (!subtree_copy) {
1698 isds_log_message(context, _("Could not copy subtree"));
1699 err = IE_ERROR;
1700 goto leave;
1702 xmlDocSetRootElement(subtree_doc, subtree_copy);
1704 /* Only this way we get namespace definition as @xmlns:isds,
1705 * otherwise we get namespace prefix without definition */
1706 /* FIXME: Don't overwrite original default namespace */
1707 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1708 if(!isds_ns) {
1709 isds_log_message(context, _("Could not create ISDS name space"));
1710 err = IE_ERROR;
1711 goto leave;
1713 xmlSetNs(subtree_copy, isds_ns);
1716 /* Serialize the document into buffer */
1717 xml_buffer = xmlBufferCreate();
1718 if (!xml_buffer) {
1719 isds_log_message(context, _("Could not create xmlBuffer"));
1720 err = IE_ERROR;
1721 goto leave;
1723 /* Last argument 0 means to not format the XML tree */
1724 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1725 if (!save_ctx) {
1726 isds_log_message(context, _("Could not create XML serializer"));
1727 err = IE_ERROR;
1728 goto leave;
1730 /* XXX: According LibXML documentation, this function does not return
1731 * meaningful value yet */
1732 xmlSaveDoc(save_ctx, subtree_doc);
1733 if (-1 == xmlSaveFlush(save_ctx)) {
1734 isds_log_message(context,
1735 _("Could not serialize XML subtree"));
1736 err = IE_ERROR;
1737 goto leave;
1739 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1740 * even after xmlSaveFlush(). Thus close it here */
1741 xmlSaveClose(save_ctx); save_ctx = NULL;
1744 /* Store and detach buffer from xml_buffer */
1745 *buffer = xml_buffer->content;
1746 *length = xml_buffer->use;
1747 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1749 /* Shrink buffer */
1750 new_buffer = realloc(*buffer, *length);
1751 if (new_buffer) *buffer = new_buffer;
1753 leave:
1754 if (err) {
1755 zfree(*buffer);
1756 *length = 0;
1759 xmlSaveClose(save_ctx);
1760 xmlBufferFree(xml_buffer);
1761 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1762 return err;
1764 #endif /* HAVE_LIBCURL */
1767 #if 0
1768 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1769 * @context is session context
1770 * @document is original document where @nodeset points to
1771 * @nodeset is XPath node set to dump (recursively)
1772 * @buffer is automatically reallocated buffer where serialize to
1773 * @length is size of serialized stream in bytes
1774 * @return standard error code, free @buffer in case of error */
1775 static isds_error dump_nodeset(struct isds_ctx *context,
1776 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1777 void **buffer, size_t *length) {
1778 isds_error err = IE_SUCCESS;
1779 xmlBufferPtr xml_buffer = NULL;
1780 void *new_buffer;
1782 if (!context) return IE_INVALID_CONTEXT;
1783 if (!buffer) return IE_INVAL;
1784 zfree(*buffer);
1785 if (!document || !nodeset || !length) return IE_INVAL;
1786 *length = 0;
1788 /* Empty node set results into NULL buffer */
1789 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1790 goto leave;
1793 /* Resulting the document into buffer */
1794 xml_buffer = xmlBufferCreate();
1795 if (!xml_buffer) {
1796 isds_log_message(context, _("Could not create xmlBuffer"));
1797 err = IE_ERROR;
1798 goto leave;
1801 /* Iterate over all nodes */
1802 for (int i = 0; i < nodeset->nodeNr; i++) {
1803 /* Serialize node.
1804 * XXX: xmlNodeDump() appends to xml_buffer. */
1805 if (-1 ==
1806 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1807 isds_log_message(context, _("Could not dump XML node"));
1808 err = IE_ERROR;
1809 goto leave;
1813 /* Store and detach buffer from xml_buffer */
1814 *buffer = xml_buffer->content;
1815 *length = xml_buffer->use;
1816 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1818 /* Shrink buffer */
1819 new_buffer = realloc(*buffer, *length);
1820 if (new_buffer) *buffer = new_buffer;
1823 leave:
1824 if (err) {
1825 zfree(*buffer);
1826 *length = 0;
1829 xmlBufferFree(xml_buffer);
1830 return err;
1832 #endif
1834 #if 0
1835 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1836 * @context is session context
1837 * @document is original document where @nodeset points to
1838 * @nodeset is XPath node set to dump (recursively)
1839 * @buffer is automatically reallocated buffer where serialize to
1840 * @length is size of serialized stream in bytes
1841 * @return standard error code, free @buffer in case of error */
1842 static isds_error dump_nodeset(struct isds_ctx *context,
1843 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1844 void **buffer, size_t *length) {
1845 isds_error err = IE_SUCCESS;
1846 xmlBufferPtr xml_buffer = NULL;
1847 xmlSaveCtxtPtr save_ctx = NULL;
1848 void *new_buffer;
1850 if (!context) return IE_INVALID_CONTEXT;
1851 if (!buffer) return IE_INVAL;
1852 zfree(*buffer);
1853 if (!document || !nodeset || !length) return IE_INVAL;
1854 *length = 0;
1856 /* Empty node set results into NULL buffer */
1857 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1858 goto leave;
1861 /* Resulting the document into buffer */
1862 xml_buffer = xmlBufferCreate();
1863 if (!xml_buffer) {
1864 isds_log_message(context, _("Could not create xmlBuffer"));
1865 err = IE_ERROR;
1866 goto leave;
1868 if (xmlSubstituteEntitiesDefault(1)) {
1869 isds_log_message(context, _("Could not disable attribute escaping"));
1870 err = IE_ERROR;
1871 goto leave;
1873 /* Last argument means:
1874 * 0 to not format the XML tree
1875 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1876 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1877 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1878 if (!save_ctx) {
1879 isds_log_message(context, _("Could not create XML serializer"));
1880 err = IE_ERROR;
1881 goto leave;
1883 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1884 isds_log_message(context, _("Could not disable attribute escaping"));
1885 err = IE_ERROR;
1886 goto leave;
1890 /* Iterate over all nodes */
1891 for (int i = 0; i < nodeset->nodeNr; i++) {
1892 /* Serialize node.
1893 * XXX: xmlNodeDump() appends to xml_buffer. */
1894 /*if (-1 ==
1895 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1897 /* XXX: According LibXML documentation, this function does not return
1898 * meaningful value yet */
1899 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1900 if (-1 == xmlSaveFlush(save_ctx)) {
1901 isds_log_message(context,
1902 _("Could not serialize XML subtree"));
1903 err = IE_ERROR;
1904 goto leave;
1908 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1909 * even after xmlSaveFlush(). Thus close it here */
1910 xmlSaveClose(save_ctx); save_ctx = NULL;
1912 /* Store and detach buffer from xml_buffer */
1913 *buffer = xml_buffer->content;
1914 *length = xml_buffer->use;
1915 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1917 /* Shrink buffer */
1918 new_buffer = realloc(*buffer, *length);
1919 if (new_buffer) *buffer = new_buffer;
1921 leave:
1922 if (err) {
1923 zfree(*buffer);
1924 *length = 0;
1927 xmlSaveClose(save_ctx);
1928 xmlBufferFree(xml_buffer);
1929 return err;
1931 #endif
1934 #if HAVE_LIBCURL
1935 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1936 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1937 if (!string || !type) return IE_INVAL;
1939 if (!xmlStrcmp(string, BAD_CAST "FO"))
1940 *type = DBTYPE_FO;
1941 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1942 *type = DBTYPE_PFO;
1943 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1944 *type = DBTYPE_PFO_ADVOK;
1945 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1946 *type = DBTYPE_PFO_DANPOR;
1947 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1948 *type = DBTYPE_PFO_INSSPR;
1949 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1950 *type = DBTYPE_PO;
1951 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1952 *type = DBTYPE_PO_ZAK;
1953 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1954 *type = DBTYPE_PO_REQ;
1955 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1956 *type = DBTYPE_OVM;
1957 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1958 *type = DBTYPE_OVM_NOTAR;
1959 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1960 *type = DBTYPE_OVM_EXEKUT;
1961 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1962 *type = DBTYPE_OVM_REQ;
1963 else
1964 return IE_ENUM;
1965 return IE_SUCCESS;
1969 /* Convert ISDS dbType enum @type to UTF-8 string.
1970 * @Return pointer to static string, or NULL if unknown enum value */
1971 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1972 switch(type) {
1973 /* DBTYPE_SYSTEM is invalid value from point of view of public
1974 * SOAP interface. */
1975 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1976 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1977 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1978 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1979 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1980 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1981 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1982 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1983 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1984 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1985 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1986 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1987 default: return NULL; break;
1992 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
1993 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1994 if (!string || !type) return IE_INVAL;
1996 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1997 *type = USERTYPE_PRIMARY;
1998 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1999 *type = USERTYPE_ENTRUSTED;
2000 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2001 *type = USERTYPE_ADMINISTRATOR;
2002 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2003 *type = USERTYPE_OFFICIAL;
2004 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2005 *type = USERTYPE_OFFICIAL_CERT;
2006 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2007 *type = USERTYPE_LIQUIDATOR;
2008 else
2009 return IE_ENUM;
2010 return IE_SUCCESS;
2014 /* Convert ISDS userType enum @type to UTF-8 string.
2015 * @Return pointer to static string, or NULL if unknown enum value */
2016 static const xmlChar *isds_UserType2string(const isds_UserType type) {
2017 switch(type) {
2018 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
2019 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
2020 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
2021 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
2022 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
2023 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
2024 default: return NULL; break;
2029 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2030 static isds_error string2isds_sender_type(const xmlChar *string,
2031 isds_sender_type *type) {
2032 if (!string || !type) return IE_INVAL;
2034 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2035 *type = SENDERTYPE_PRIMARY;
2036 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2037 *type = SENDERTYPE_ENTRUSTED;
2038 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2039 *type = SENDERTYPE_ADMINISTRATOR;
2040 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2041 *type = SENDERTYPE_OFFICIAL;
2042 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2043 *type = SENDERTYPE_VIRTUAL;
2044 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2045 *type = SENDERTYPE_OFFICIAL_CERT;
2046 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2047 *type = SENDERTYPE_LIQUIDATOR;
2048 else
2049 return IE_ENUM;
2050 return IE_SUCCESS;
2054 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2055 static isds_error string2isds_payment_type(const xmlChar *string,
2056 isds_payment_type *type) {
2057 if (!string || !type) return IE_INVAL;
2059 if (!xmlStrcmp(string, BAD_CAST "K"))
2060 *type = PAYMENT_SENDER;
2061 else if (!xmlStrcmp(string, BAD_CAST "O"))
2062 *type = PAYMENT_RESPONSE;
2063 else if (!xmlStrcmp(string, BAD_CAST "G"))
2064 *type = PAYMENT_SPONSOR;
2065 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2066 *type = PAYMENT_SPONSOR_LIMITED;
2067 else if (!xmlStrcmp(string, BAD_CAST "D"))
2068 *type = PAYMENT_SPONSOR_EXTERNAL;
2069 else if (!xmlStrcmp(string, BAD_CAST "E"))
2070 *type = PAYMENT_STAMP;
2071 else
2072 return IE_ENUM;
2073 return IE_SUCCESS;
2077 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2078 * ciEventType is integer but we convert it from string representation
2079 * directly. */
2080 static isds_error string2isds_credit_event_type(const xmlChar *string,
2081 isds_credit_event_type *type) {
2082 if (!string || !type) return IE_INVAL;
2084 if (!xmlStrcmp(string, BAD_CAST "1"))
2085 *type = ISDS_CREDIT_CHARGED;
2086 else if (!xmlStrcmp(string, BAD_CAST "2"))
2087 *type = ISDS_CREDIT_DISCHARGED;
2088 else if (!xmlStrcmp(string, BAD_CAST "3"))
2089 *type = ISDS_CREDIT_MESSAGE_SENT;
2090 else if (!xmlStrcmp(string, BAD_CAST "4"))
2091 *type = ISDS_CREDIT_STORAGE_SET;
2092 else if (!xmlStrcmp(string, BAD_CAST "5"))
2093 *type = ISDS_CREDIT_EXPIRED;
2094 else
2095 return IE_ENUM;
2096 return IE_SUCCESS;
2100 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2101 * @Return pointer to static string, or NULL if unknown enum value */
2102 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2103 switch(type) {
2104 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2105 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2106 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2107 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2108 default: return NULL; break;
2111 #endif /* HAVE_LIBCURL */
2114 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2115 * @Return IE_ENUM if @string is not valid enum member */
2116 static isds_error string2isds_FileMetaType(const xmlChar *string,
2117 isds_FileMetaType *type) {
2118 if (!string || !type) return IE_INVAL;
2120 if (!xmlStrcmp(string, BAD_CAST "main"))
2121 *type = FILEMETATYPE_MAIN;
2122 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2123 *type = FILEMETATYPE_ENCLOSURE;
2124 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2125 *type = FILEMETATYPE_SIGNATURE;
2126 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2127 *type = FILEMETATYPE_META;
2128 else
2129 return IE_ENUM;
2130 return IE_SUCCESS;
2134 /* Convert UTF-8 @string to ISDS hash @algorithm.
2135 * @Return IE_ENUM if @string is not valid enum member */
2136 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2137 isds_hash_algorithm *algorithm) {
2138 if (!string || !algorithm) return IE_INVAL;
2140 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2141 *algorithm = HASH_ALGORITHM_MD5;
2142 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2143 *algorithm = HASH_ALGORITHM_SHA_1;
2144 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2145 *algorithm = HASH_ALGORITHM_SHA_224;
2146 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2147 *algorithm = HASH_ALGORITHM_SHA_256;
2148 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2149 *algorithm = HASH_ALGORITHM_SHA_384;
2150 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2151 *algorithm = HASH_ALGORITHM_SHA_512;
2152 else
2153 return IE_ENUM;
2154 return IE_SUCCESS;
2158 #if HAVE_LIBCURL
2159 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2160 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2161 if (!time || !string) return IE_INVAL;
2163 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2164 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2165 return IE_ERROR;
2167 return IE_SUCCESS;
2171 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2172 * respects the @time microseconds too. */
2173 static isds_error timeval2timestring(const struct timeval *time,
2174 xmlChar **string) {
2175 struct tm broken;
2177 if (!time || !string) return IE_INVAL;
2179 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
2180 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2182 /* TODO: small negative year should be formatted as "-0012". This is not
2183 * true for glibc "%04d". We should implement it.
2184 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
2185 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2186 if (-1 == isds_asprintf((char **) string,
2187 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
2188 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2189 broken.tm_hour, broken.tm_min, broken.tm_sec,
2190 time->tv_usec))
2191 return IE_ERROR;
2193 return IE_SUCCESS;
2195 #endif /* HAVE_LIBCURL */
2198 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2199 * It respects microseconds too.
2200 * In case of error, @time will be freed. */
2201 static isds_error timestring2timeval(const xmlChar *string,
2202 struct timeval **time) {
2203 struct tm broken;
2204 char *offset, *delim, *endptr;
2205 char subseconds[7];
2206 int offset_hours, offset_minutes;
2207 int i;
2208 #ifdef _WIN32
2209 int tmp;
2210 #endif
2212 if (!time) return IE_INVAL;
2213 if (!string) {
2214 zfree(*time);
2215 return IE_INVAL;
2218 memset(&broken, 0, sizeof(broken));
2220 if (!*time) {
2221 *time = calloc(1, sizeof(**time));
2222 if (!*time) return IE_NOMEM;
2223 } else {
2224 memset(*time, 0, sizeof(**time));
2228 /* xsd:date is ISO 8601 string, thus ASCII */
2229 /*TODO: negative year */
2231 #ifdef _WIN32
2232 i = 0;
2233 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2234 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2235 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2236 &i)) < 6) {
2237 zfree(*time);
2238 return IE_DATE;
2241 broken.tm_year -= 1900;
2242 broken.tm_mon--;
2243 offset = (char*)string + i;
2244 #else
2245 /* Parse date and time without subseconds and offset */
2246 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2247 if (!offset) {
2248 zfree(*time);
2249 return IE_DATE;
2251 #endif
2253 /* Get subseconds */
2254 if (*offset == '.' ) {
2255 offset++;
2257 /* Copy first 6 digits, pad it with zeros.
2258 * XXX: It truncates longer number, no round.
2259 * Current server implementation uses only millisecond resolution. */
2260 /* TODO: isdigit() is locale sensitive */
2261 for (i = 0;
2262 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
2263 i++, offset++) {
2264 subseconds[i] = *offset;
2266 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
2267 subseconds[i] = '0';
2269 subseconds[6] = '\0';
2271 /* Convert it into integer */
2272 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
2273 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
2274 (*time)->tv_usec == LONG_MAX) {
2275 zfree(*time);
2276 return IE_DATE;
2279 /* move to the zone offset delimiter or signal NULL*/
2280 delim = strchr(offset, '-');
2281 if (!delim)
2282 delim = strchr(offset, '+');
2283 if (!delim)
2284 delim = strchr(offset, 'Z');
2285 offset = delim;
2288 /* Get zone offset */
2289 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2290 * "" equals to "Z" and it means UTC zone. */
2291 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2292 * colon separator */
2293 if (offset && (*offset == '-' || *offset == '+')) {
2294 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2295 zfree(*time);
2296 return IE_DATE;
2298 if (*offset == '+') {
2299 broken.tm_hour -= offset_hours;
2300 broken.tm_min -= offset_minutes;
2301 } else {
2302 broken.tm_hour += offset_hours;
2303 broken.tm_min += offset_minutes;
2307 /* Convert to time_t */
2308 (*time)->tv_sec = _isds_timegm(&broken);
2309 if ((*time)->tv_sec == (time_t) -1) {
2310 zfree(*time);
2311 return IE_DATE;
2314 return IE_SUCCESS;
2318 /* Convert unsigned int into isds_message_status.
2319 * @context is session context
2320 * @number is pointer to number value. NULL will be treated as invalid value.
2321 * @status is automatically reallocated status
2322 * @return IE_SUCCESS, or error code and free status */
2323 static isds_error uint2isds_message_status(struct isds_ctx *context,
2324 const unsigned long int *number, isds_message_status **status) {
2325 if (!context) return IE_INVALID_CONTEXT;
2326 if (!status) return IE_INVAL;
2328 free(*status); *status = NULL;
2329 if (!number) return IE_INVAL;
2331 if (*number < 1 || *number > 10) {
2332 isds_printf_message(context, _("Invalid message status value: %lu"),
2333 *number);
2334 return IE_ENUM;
2337 *status = malloc(sizeof(**status));
2338 if (!*status) return IE_NOMEM;
2340 **status = 1 << *number;
2341 return IE_SUCCESS;
2345 /* Convert event description string into isds_event members type and
2346 * description
2347 * @string is raw event description starting with event prefix
2348 * @event is structure where to store type and stripped description to
2349 * @return standard error code, unknown prefix is not classified as an error.
2350 * */
2351 static isds_error eventstring2event(const xmlChar *string,
2352 struct isds_event* event) {
2353 const xmlChar *known_prefixes[] = {
2354 BAD_CAST "EV0:",
2355 BAD_CAST "EV1:",
2356 BAD_CAST "EV2:",
2357 BAD_CAST "EV3:",
2358 BAD_CAST "EV4:",
2359 BAD_CAST "EV5:",
2360 BAD_CAST "EV11:",
2361 BAD_CAST "EV12:",
2362 BAD_CAST "EV13:"
2364 const isds_event_type types[] = {
2365 EVENT_ENTERED_SYSTEM,
2366 EVENT_ACCEPTED_BY_RECIPIENT,
2367 EVENT_ACCEPTED_BY_FICTION,
2368 EVENT_UNDELIVERABLE,
2369 EVENT_COMMERCIAL_ACCEPTED,
2370 EVENT_DELIVERED,
2371 EVENT_PRIMARY_LOGIN,
2372 EVENT_ENTRUSTED_LOGIN,
2373 EVENT_SYSCERT_LOGIN
2375 unsigned int index;
2376 size_t length;
2378 if (!string || !event) return IE_INVAL;
2380 if (!event->type) {
2381 event->type = malloc(sizeof(*event->type));
2382 if (!(event->type)) return IE_NOMEM;
2384 zfree(event->description);
2386 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2387 index++) {
2388 length = xmlUTF8Strlen(known_prefixes[index]);
2390 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2391 /* Prefix is known */
2392 *event->type = types[index];
2394 /* Strip prefix from description and spaces */
2395 /* TODO: Recognize all white spaces from UCS blank class and
2396 * operate on UTF-8 chars. */
2397 for (; string[length] != '\0' && string[length] == ' '; length++);
2398 event->description = strdup((char *) (string + length));
2399 if (!(event->description)) return IE_NOMEM;
2401 return IE_SUCCESS;
2405 /* Unknown event prefix.
2406 * XSD allows any string */
2407 char *string_locale = _isds_utf82locale((char *) string);
2408 isds_log(ILF_ISDS, ILL_WARNING,
2409 _("Unknown delivery info event prefix: %s\n"), string_locale);
2410 free(string_locale);
2412 *event->type = EVENT_UKNOWN;
2413 event->description = strdup((char *) string);
2414 if (!(event->description)) return IE_NOMEM;
2416 return IE_SUCCESS;
2420 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2421 * and leave label */
2422 #define EXTRACT_STRING(element, string) { \
2423 xmlXPathFreeObject(result); \
2424 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2425 if (NULL == (result)) { \
2426 err = IE_ERROR; \
2427 goto leave; \
2429 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2430 if (result->nodesetval->nodeNr > 1) { \
2431 isds_printf_message(context, _("Multiple %s element"), element); \
2432 err = IE_ERROR; \
2433 goto leave; \
2435 (string) = (char *) \
2436 xmlXPathCastNodeSetToString(result->nodesetval); \
2437 if (NULL == (string)) { \
2438 err = IE_ERROR; \
2439 goto leave; \
2444 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2446 char *string = NULL; \
2447 EXTRACT_STRING(element, string); \
2449 if (string) { \
2450 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2451 if (!(booleanPtr)) { \
2452 free(string); \
2453 err = IE_NOMEM; \
2454 goto leave; \
2457 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2458 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2459 *(booleanPtr) = 1; \
2460 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2461 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2462 *(booleanPtr) = 0; \
2463 else { \
2464 char *string_locale = _isds_utf82locale((char*)string); \
2465 isds_printf_message(context, \
2466 _("%s value is not valid boolean: %s"), \
2467 element, string_locale); \
2468 free(string_locale); \
2469 free(string); \
2470 err = IE_ERROR; \
2471 goto leave; \
2474 free(string); \
2478 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2480 char *string = NULL; \
2481 EXTRACT_STRING(element, string); \
2482 if (string) { \
2483 long int number; \
2484 char *endptr; \
2486 number = strtol((char*)string, &endptr, 10); \
2488 if (*endptr != '\0') { \
2489 char *string_locale = _isds_utf82locale((char *)string); \
2490 isds_printf_message(context, \
2491 _("%s is not valid integer: %s"), \
2492 element, string_locale); \
2493 free(string_locale); \
2494 free(string); \
2495 err = IE_ISDS; \
2496 goto leave; \
2499 if (number == LONG_MIN || number == LONG_MAX) { \
2500 char *string_locale = _isds_utf82locale((char *)string); \
2501 isds_printf_message(context, \
2502 _("%s value out of range of long int: %s"), \
2503 element, string_locale); \
2504 free(string_locale); \
2505 free(string); \
2506 err = IE_ERROR; \
2507 goto leave; \
2510 free(string); string = NULL; \
2512 if (!(preallocated)) { \
2513 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2514 if (!(longintPtr)) { \
2515 err = IE_NOMEM; \
2516 goto leave; \
2519 *(longintPtr) = number; \
2523 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2525 char *string = NULL; \
2526 EXTRACT_STRING(element, string); \
2527 if (string) { \
2528 long int number; \
2529 char *endptr; \
2531 number = strtol((char*)string, &endptr, 10); \
2533 if (*endptr != '\0') { \
2534 char *string_locale = _isds_utf82locale((char *)string); \
2535 isds_printf_message(context, \
2536 _("%s is not valid integer: %s"), \
2537 element, string_locale); \
2538 free(string_locale); \
2539 free(string); \
2540 err = IE_ISDS; \
2541 goto leave; \
2544 if (number == LONG_MIN || number == LONG_MAX) { \
2545 char *string_locale = _isds_utf82locale((char *)string); \
2546 isds_printf_message(context, \
2547 _("%s value out of range of long int: %s"), \
2548 element, string_locale); \
2549 free(string_locale); \
2550 free(string); \
2551 err = IE_ERROR; \
2552 goto leave; \
2555 free(string); string = NULL; \
2556 if (number < 0) { \
2557 isds_printf_message(context, \
2558 _("%s value is negative: %ld"), element, number); \
2559 err = IE_ERROR; \
2560 goto leave; \
2563 if (!(preallocated)) { \
2564 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2565 if (!(ulongintPtr)) { \
2566 err = IE_NOMEM; \
2567 goto leave; \
2570 *(ulongintPtr) = number; \
2574 #define EXTRACT_DATE(element, tmPtr) { \
2575 char *string = NULL; \
2576 EXTRACT_STRING(element, string); \
2577 if (NULL != string) { \
2578 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2579 if (NULL == (tmPtr)) { \
2580 free(string); \
2581 err = IE_NOMEM; \
2582 goto leave; \
2584 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2585 if (err) { \
2586 if (err == IE_NOTSUP) { \
2587 err = IE_ISDS; \
2588 char *string_locale = _isds_utf82locale(string); \
2589 char *element_locale = _isds_utf82locale(element); \
2590 isds_printf_message(context, _("Invalid %s value: %s"), \
2591 element_locale, string_locale); \
2592 free(string_locale); \
2593 free(element_locale); \
2595 free(string); \
2596 goto leave; \
2598 free(string); \
2602 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2603 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2604 NULL); \
2605 if ((required) && (!string)) { \
2606 char *attribute_locale = _isds_utf82locale(attribute); \
2607 char *element_locale = \
2608 _isds_utf82locale((char *)xpath_ctx->node->name); \
2609 isds_printf_message(context, \
2610 _("Could not extract required %s attribute value from " \
2611 "%s element"), attribute_locale, element_locale); \
2612 free(element_locale); \
2613 free(attribute_locale); \
2614 err = IE_ERROR; \
2615 goto leave; \
2620 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2622 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2623 (xmlChar *) (string)); \
2624 if (!node) { \
2625 isds_printf_message(context, \
2626 _("Could not add %s child to %s element"), \
2627 element, (parent)->name); \
2628 err = IE_ERROR; \
2629 goto leave; \
2633 #define INSERT_STRING(parent, element, string) \
2634 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2636 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2638 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2639 else { INSERT_STRING(parent, element, "false"); } \
2642 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2644 if (booleanPtr) { \
2645 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2646 } else { \
2647 INSERT_STRING(parent, element, NULL); \
2651 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2652 if ((longintPtr)) { \
2653 /* FIXME: locale sensitive */ \
2654 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2655 err = IE_NOMEM; \
2656 goto leave; \
2658 INSERT_STRING(parent, element, buffer) \
2659 free(buffer); (buffer) = NULL; \
2660 } else { INSERT_STRING(parent, element, NULL) } \
2663 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2664 if ((ulongintPtr)) { \
2665 /* FIXME: locale sensitive */ \
2666 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2667 err = IE_NOMEM; \
2668 goto leave; \
2670 INSERT_STRING(parent, element, buffer) \
2671 free(buffer); (buffer) = NULL; \
2672 } else { INSERT_STRING(parent, element, NULL) } \
2675 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2677 /* FIXME: locale sensitive */ \
2678 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2679 err = IE_NOMEM; \
2680 goto leave; \
2682 INSERT_STRING(parent, element, buffer) \
2683 free(buffer); (buffer) = NULL; \
2686 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2687 * new attribute. */
2688 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2690 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2691 (xmlChar *) (string)); \
2692 if (!attribute_node) { \
2693 isds_printf_message(context, _("Could not add %s " \
2694 "attribute to %s element"), \
2695 (attribute), (parent)->name); \
2696 err = IE_ERROR; \
2697 goto leave; \
2701 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2702 if (string) { \
2703 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2704 if (length > (maximum)) { \
2705 isds_printf_message(context, \
2706 ngettext("%s has more than %d characters", \
2707 "%s has more than %d characters", (maximum)), \
2708 (name), (maximum)); \
2709 err = IE_2BIG; \
2710 goto leave; \
2712 if (length < (minimum)) { \
2713 isds_printf_message(context, \
2714 ngettext("%s has less than %d characters", \
2715 "%s has less than %d characters", (minimum)), \
2716 (name), (minimum)); \
2717 err = IE_2SMALL; \
2718 goto leave; \
2723 #define INSERT_ELEMENT(child, parent, element) \
2725 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2726 if (!(child)) { \
2727 isds_printf_message(context, \
2728 _("Could not add %s child to %s element"), \
2729 (element), (parent)->name); \
2730 err = IE_ERROR; \
2731 goto leave; \
2736 /* Find child element by name in given XPath context and switch context onto
2737 * it. The child must be uniq and must exist. Otherwise fails.
2738 * @context is ISDS context
2739 * @child is child element name
2740 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2741 * into it child. In error case, the @xpath_ctx keeps original value. */
2742 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2743 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2744 isds_error err = IE_SUCCESS;
2745 xmlXPathObjectPtr result = NULL;
2747 if (!context) return IE_INVALID_CONTEXT;
2748 if (!child || !xpath_ctx) return IE_INVAL;
2750 /* Find child */
2751 result = xmlXPathEvalExpression(child, xpath_ctx);
2752 if (!result) {
2753 err = IE_XML;
2754 goto leave;
2757 /* No match */
2758 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2759 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2760 char *child_locale = _isds_utf82locale((char*) child);
2761 isds_printf_message(context,
2762 _("%s element does not contain %s child"),
2763 parent_locale, child_locale);
2764 free(child_locale);
2765 free(parent_locale);
2766 err = IE_NOEXIST;
2767 goto leave;
2770 /* More matches */
2771 if (result->nodesetval->nodeNr > 1) {
2772 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2773 char *child_locale = _isds_utf82locale((char*) child);
2774 isds_printf_message(context,
2775 _("%s element contains multiple %s children"),
2776 parent_locale, child_locale);
2777 free(child_locale);
2778 free(parent_locale);
2779 err = IE_NOTUNIQ;
2780 goto leave;
2783 /* Switch context */
2784 xpath_ctx->node = result->nodesetval->nodeTab[0];
2786 leave:
2787 xmlXPathFreeObject(result);
2788 return err;
2793 #if HAVE_LIBCURL
2794 /* Find and convert XSD:gPersonName group in current node into structure
2795 * @context is ISDS context
2796 * @personName is automatically reallocated person name structure. If no member
2797 * value is found, will be freed.
2798 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2799 * elements
2800 * In case of error @personName will be freed. */
2801 static isds_error extract_gPersonName(struct isds_ctx *context,
2802 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2803 isds_error err = IE_SUCCESS;
2804 xmlXPathObjectPtr result = NULL;
2806 if (!context) return IE_INVALID_CONTEXT;
2807 if (!personName) return IE_INVAL;
2808 isds_PersonName_free(personName);
2809 if (!xpath_ctx) return IE_INVAL;
2812 *personName = calloc(1, sizeof(**personName));
2813 if (!*personName) {
2814 err = IE_NOMEM;
2815 goto leave;
2818 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2819 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2820 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2821 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2823 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2824 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2825 isds_PersonName_free(personName);
2827 leave:
2828 if (err) isds_PersonName_free(personName);
2829 xmlXPathFreeObject(result);
2830 return err;
2834 /* Find and convert XSD:gAddress group in current node into structure
2835 * @context is ISDS context
2836 * @address is automatically reallocated address structure. If no member
2837 * value is found, will be freed.
2838 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2839 * elements
2840 * In case of error @address will be freed. */
2841 static isds_error extract_gAddress(struct isds_ctx *context,
2842 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2843 isds_error err = IE_SUCCESS;
2844 xmlXPathObjectPtr result = NULL;
2846 if (!context) return IE_INVALID_CONTEXT;
2847 if (!address) return IE_INVAL;
2848 isds_Address_free(address);
2849 if (!xpath_ctx) return IE_INVAL;
2852 *address = calloc(1, sizeof(**address));
2853 if (!*address) {
2854 err = IE_NOMEM;
2855 goto leave;
2858 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2859 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2860 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2861 EXTRACT_STRING("isds:adNumberInMunicipality",
2862 (*address)->adNumberInMunicipality);
2863 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2864 EXTRACT_STRING("isds:adState", (*address)->adState);
2866 if (!(*address)->adCity && !(*address)->adStreet &&
2867 !(*address)->adNumberInStreet &&
2868 !(*address)->adNumberInMunicipality &&
2869 !(*address)->adZipCode && !(*address)->adState)
2870 isds_Address_free(address);
2872 leave:
2873 if (err) isds_Address_free(address);
2874 xmlXPathFreeObject(result);
2875 return err;
2879 /* Find and convert isds:biDate element in current node into structure
2880 * @context is ISDS context
2881 * @biDate is automatically reallocated birth date structure. If no member
2882 * value is found, will be freed.
2883 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2884 * element
2885 * In case of error @biDate will be freed. */
2886 static isds_error extract_BiDate(struct isds_ctx *context,
2887 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2888 isds_error err = IE_SUCCESS;
2889 xmlXPathObjectPtr result = NULL;
2890 char *string = NULL;
2892 if (!context) return IE_INVALID_CONTEXT;
2893 if (!biDate) return IE_INVAL;
2894 zfree(*biDate);
2895 if (!xpath_ctx) return IE_INVAL;
2897 EXTRACT_STRING("isds:biDate", string);
2898 if (string) {
2899 *biDate = calloc(1, sizeof(**biDate));
2900 if (!*biDate) {
2901 err = IE_NOMEM;
2902 goto leave;
2904 err = _isds_datestring2tm((xmlChar *)string, *biDate);
2905 if (err) {
2906 if (err == IE_NOTSUP) {
2907 err = IE_ISDS;
2908 char *string_locale = _isds_utf82locale(string);
2909 isds_printf_message(context,
2910 _("Invalid isds:biDate value: %s"), string_locale);
2911 free(string_locale);
2913 goto leave;
2917 leave:
2918 if (err) zfree(*biDate);
2919 free(string);
2920 xmlXPathFreeObject(result);
2921 return err;
2925 /* Convert isds:dBOwnerInfo XML tree into structure
2926 * @context is ISDS context
2927 * @db_owner_info is automatically reallocated box owner info structure
2928 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2929 * In case of error @db_owner_info will be freed. */
2930 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2931 struct isds_DbOwnerInfo **db_owner_info,
2932 xmlXPathContextPtr xpath_ctx) {
2933 isds_error err = IE_SUCCESS;
2934 xmlXPathObjectPtr result = NULL;
2935 char *string = NULL;
2937 if (!context) return IE_INVALID_CONTEXT;
2938 if (!db_owner_info) return IE_INVAL;
2939 isds_DbOwnerInfo_free(db_owner_info);
2940 if (!xpath_ctx) return IE_INVAL;
2943 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2944 if (!*db_owner_info) {
2945 err = IE_NOMEM;
2946 goto leave;
2949 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2951 EXTRACT_STRING("isds:dbType", string);
2952 if (string) {
2953 (*db_owner_info)->dbType =
2954 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2955 if (!(*db_owner_info)->dbType) {
2956 err = IE_NOMEM;
2957 goto leave;
2959 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2960 if (err) {
2961 zfree((*db_owner_info)->dbType);
2962 if (err == IE_ENUM) {
2963 err = IE_ISDS;
2964 char *string_locale = _isds_utf82locale(string);
2965 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2966 string_locale);
2967 free(string_locale);
2969 goto leave;
2971 zfree(string);
2974 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2976 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2977 xpath_ctx);
2978 if (err) goto leave;
2980 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2982 (*db_owner_info)->birthInfo =
2983 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2984 if (!(*db_owner_info)->birthInfo) {
2985 err = IE_NOMEM;
2986 goto leave;
2988 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2989 xpath_ctx);
2990 if (err) goto leave;
2991 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2992 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2993 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2994 if (!(*db_owner_info)->birthInfo->biDate &&
2995 !(*db_owner_info)->birthInfo->biCity &&
2996 !(*db_owner_info)->birthInfo->biCounty &&
2997 !(*db_owner_info)->birthInfo->biState)
2998 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3000 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3001 if (err) goto leave;
3003 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3004 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3005 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3006 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3007 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3009 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3011 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3012 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3013 (*db_owner_info)->dbOpenAddressing);
3015 leave:
3016 if (err) isds_DbOwnerInfo_free(db_owner_info);
3017 free(string);
3018 xmlXPathFreeObject(result);
3019 return err;
3023 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3024 * @context is session context
3025 * @owner is libisds structure with box description
3026 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3027 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3028 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
3030 isds_error err = IE_SUCCESS;
3031 xmlNodePtr node;
3032 xmlChar *string = NULL;
3034 if (!context) return IE_INVALID_CONTEXT;
3035 if (!owner || !db_owner_info) return IE_INVAL;
3038 /* Build XSD:tDbOwnerInfo */
3039 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3040 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3042 /* dbType */
3043 if (owner->dbType) {
3044 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
3045 if (!type_string) {
3046 isds_printf_message(context, _("Invalid dbType value: %d"),
3047 *(owner->dbType));
3048 err = IE_ENUM;
3049 goto leave;
3051 INSERT_STRING(db_owner_info, "dbType", type_string);
3053 INSERT_STRING(db_owner_info, "ic", owner->ic);
3054 if (owner->personName) {
3055 INSERT_STRING(db_owner_info, "pnFirstName",
3056 owner->personName->pnFirstName);
3057 INSERT_STRING(db_owner_info, "pnMiddleName",
3058 owner->personName->pnMiddleName);
3059 INSERT_STRING(db_owner_info, "pnLastName",
3060 owner->personName->pnLastName);
3061 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3062 owner->personName->pnLastNameAtBirth);
3064 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3065 if (owner->birthInfo) {
3066 if (owner->birthInfo->biDate) {
3067 if (!tm2datestring(owner->birthInfo->biDate, &string))
3068 INSERT_STRING(db_owner_info, "biDate", string);
3069 free(string); string = NULL;
3071 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
3072 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
3073 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
3075 if (owner->address) {
3076 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
3077 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
3078 INSERT_STRING(db_owner_info, "adNumberInStreet",
3079 owner->address->adNumberInStreet);
3080 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3081 owner->address->adNumberInMunicipality);
3082 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
3083 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
3085 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3086 INSERT_STRING(db_owner_info, "email", owner->email);
3087 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3089 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3090 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3092 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3093 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3095 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3097 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3098 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3099 owner->dbOpenAddressing);
3101 leave:
3102 free(string);
3103 return err;
3107 /* Convert XSD:tDbUserInfo XML tree into structure
3108 * @context is ISDS context
3109 * @db_user_info is automatically reallocated user info structure
3110 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3111 * In case of error @db_user_info will be freed. */
3112 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3113 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3114 isds_error err = IE_SUCCESS;
3115 xmlXPathObjectPtr result = NULL;
3116 char *string = NULL;
3118 if (!context) return IE_INVALID_CONTEXT;
3119 if (!db_user_info) return IE_INVAL;
3120 isds_DbUserInfo_free(db_user_info);
3121 if (!xpath_ctx) return IE_INVAL;
3124 *db_user_info = calloc(1, sizeof(**db_user_info));
3125 if (!*db_user_info) {
3126 err = IE_NOMEM;
3127 goto leave;
3130 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3132 EXTRACT_STRING("isds:userType", string);
3133 if (string) {
3134 (*db_user_info)->userType =
3135 calloc(1, sizeof(*((*db_user_info)->userType)));
3136 if (!(*db_user_info)->userType) {
3137 err = IE_NOMEM;
3138 goto leave;
3140 err = string2isds_UserType((xmlChar *)string,
3141 (*db_user_info)->userType);
3142 if (err) {
3143 zfree((*db_user_info)->userType);
3144 if (err == IE_ENUM) {
3145 err = IE_ISDS;
3146 char *string_locale = _isds_utf82locale(string);
3147 isds_printf_message(context,
3148 _("Unknown isds:userType value: %s"), string_locale);
3149 free(string_locale);
3151 goto leave;
3153 zfree(string);
3156 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3158 (*db_user_info)->personName =
3159 calloc(1, sizeof(*((*db_user_info)->personName)));
3160 if (!(*db_user_info)->personName) {
3161 err = IE_NOMEM;
3162 goto leave;
3165 err = extract_gPersonName(context, &(*db_user_info)->personName,
3166 xpath_ctx);
3167 if (err) goto leave;
3169 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3170 if (err) goto leave;
3172 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3173 if (err) goto leave;
3175 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3176 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3178 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3179 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3180 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3182 /* ???: Default value is "CZ" according specification. Should we provide
3183 * it? */
3184 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3186 leave:
3187 if (err) isds_DbUserInfo_free(db_user_info);
3188 free(string);
3189 xmlXPathFreeObject(result);
3190 return err;
3194 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3195 * @context is session context
3196 * @user is libisds structure with user description
3197 * @db_user_info is XML element of XSD:tDbUserInfo */
3198 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3199 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3201 isds_error err = IE_SUCCESS;
3202 xmlNodePtr node;
3203 xmlChar *string = NULL;
3205 if (!context) return IE_INVALID_CONTEXT;
3206 if (!user || !db_user_info) return IE_INVAL;
3208 /* Build XSD:tDbUserInfo */
3209 if (user->personName) {
3210 INSERT_STRING(db_user_info, "pnFirstName",
3211 user->personName->pnFirstName);
3212 INSERT_STRING(db_user_info, "pnMiddleName",
3213 user->personName->pnMiddleName);
3214 INSERT_STRING(db_user_info, "pnLastName",
3215 user->personName->pnLastName);
3216 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3217 user->personName->pnLastNameAtBirth);
3219 if (user->address) {
3220 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3221 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3222 INSERT_STRING(db_user_info, "adNumberInStreet",
3223 user->address->adNumberInStreet);
3224 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3225 user->address->adNumberInMunicipality);
3226 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3227 INSERT_STRING(db_user_info, "adState", user->address->adState);
3229 if (user->biDate) {
3230 if (!tm2datestring(user->biDate, &string))
3231 INSERT_STRING(db_user_info, "biDate", string);
3232 zfree(string);
3234 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3235 INSERT_STRING(db_user_info, "userID", user->userID);
3237 /* userType */
3238 if (user->userType) {
3239 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3240 if (!type_string) {
3241 isds_printf_message(context, _("Invalid userType value: %d"),
3242 *(user->userType));
3243 err = IE_ENUM;
3244 goto leave;
3246 INSERT_STRING(db_user_info, "userType", type_string);
3249 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3250 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3251 INSERT_STRING(db_user_info, "ic", user->ic);
3252 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3253 INSERT_STRING(db_user_info, "firmName", user->firmName);
3254 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3255 INSERT_STRING(db_user_info, "caCity", user->caCity);
3256 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3257 INSERT_STRING(db_user_info, "caState", user->caState);
3259 leave:
3260 free(string);
3261 return err;
3265 /* Convert XSD:tPDZRec XML tree into structure
3266 * @context is ISDS context
3267 * @permission is automatically reallocated commercial permission structure
3268 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3269 * In case of error @permission will be freed. */
3270 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3271 struct isds_commercial_permission **permission,
3272 xmlXPathContextPtr xpath_ctx) {
3273 isds_error err = IE_SUCCESS;
3274 xmlXPathObjectPtr result = NULL;
3275 char *string = NULL;
3277 if (!context) return IE_INVALID_CONTEXT;
3278 if (!permission) return IE_INVAL;
3279 isds_commercial_permission_free(permission);
3280 if (!xpath_ctx) return IE_INVAL;
3283 *permission = calloc(1, sizeof(**permission));
3284 if (!*permission) {
3285 err = IE_NOMEM;
3286 goto leave;
3289 EXTRACT_STRING("isds:PDZType", string);
3290 if (string) {
3291 err = string2isds_payment_type((xmlChar *)string,
3292 &(*permission)->type);
3293 if (err) {
3294 if (err == IE_ENUM) {
3295 err = IE_ISDS;
3296 char *string_locale = _isds_utf82locale(string);
3297 isds_printf_message(context,
3298 _("Unknown isds:PDZType value: %s"), string_locale);
3299 free(string_locale);
3301 goto leave;
3303 zfree(string);
3306 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3307 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3309 EXTRACT_STRING("isds:PDZExpire", string);
3310 if (string) {
3311 err = timestring2timeval((xmlChar *) string,
3312 &((*permission)->expiration));
3313 if (err) {
3314 char *string_locale = _isds_utf82locale(string);
3315 if (err == IE_DATE) err = IE_ISDS;
3316 isds_printf_message(context,
3317 _("Could not convert PDZExpire as ISO time: %s"),
3318 string_locale);
3319 free(string_locale);
3320 goto leave;
3322 zfree(string);
3325 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3326 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3328 leave:
3329 if (err) isds_commercial_permission_free(permission);
3330 free(string);
3331 xmlXPathFreeObject(result);
3332 return err;
3336 /* Convert XSD:tCiRecord XML tree into structure
3337 * @context is ISDS context
3338 * @event is automatically reallocated commercial credit event structure
3339 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3340 * In case of error @event will be freed. */
3341 static isds_error extract_CiRecord(struct isds_ctx *context,
3342 struct isds_credit_event **event,
3343 xmlXPathContextPtr xpath_ctx) {
3344 isds_error err = IE_SUCCESS;
3345 xmlXPathObjectPtr result = NULL;
3346 char *string = NULL;
3347 long int *number_ptr;
3349 if (!context) return IE_INVALID_CONTEXT;
3350 if (!event) return IE_INVAL;
3351 isds_credit_event_free(event);
3352 if (!xpath_ctx) return IE_INVAL;
3355 *event = calloc(1, sizeof(**event));
3356 if (!*event) {
3357 err = IE_NOMEM;
3358 goto leave;
3361 EXTRACT_STRING("isds:ciEventTime", string);
3362 if (string) {
3363 err = timestring2timeval((xmlChar *) string,
3364 &(*event)->time);
3365 if (err) {
3366 char *string_locale = _isds_utf82locale(string);
3367 if (err == IE_DATE) err = IE_ISDS;
3368 isds_printf_message(context,
3369 _("Could not convert ciEventTime as ISO time: %s"),
3370 string_locale);
3371 free(string_locale);
3372 goto leave;
3374 zfree(string);
3377 EXTRACT_STRING("isds:ciEventType", string);
3378 if (string) {
3379 err = string2isds_credit_event_type((xmlChar *)string,
3380 &(*event)->type);
3381 if (err) {
3382 if (err == IE_ENUM) {
3383 err = IE_ISDS;
3384 char *string_locale = _isds_utf82locale(string);
3385 isds_printf_message(context,
3386 _("Unknown isds:ciEventType value: %s"), string_locale);
3387 free(string_locale);
3389 goto leave;
3391 zfree(string);
3394 number_ptr = &((*event)->credit_change);
3395 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3396 number_ptr = &(*event)->new_credit;
3397 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3399 switch((*event)->type) {
3400 case ISDS_CREDIT_CHARGED:
3401 EXTRACT_STRING("isds:ciTransID",
3402 (*event)->details.charged.transaction);
3403 break;
3404 case ISDS_CREDIT_DISCHARGED:
3405 EXTRACT_STRING("isds:ciTransID",
3406 (*event)->details.discharged.transaction);
3407 break;
3408 case ISDS_CREDIT_MESSAGE_SENT:
3409 EXTRACT_STRING("isds:ciRecipientID",
3410 (*event)->details.message_sent.recipient);
3411 EXTRACT_STRING("isds:ciPDZID",
3412 (*event)->details.message_sent.message_id);
3413 break;
3414 case ISDS_CREDIT_STORAGE_SET:
3415 number_ptr = &((*event)->details.storage_set.new_capacity);
3416 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3417 EXTRACT_DATE("isds:ciNewFrom",
3418 (*event)->details.storage_set.new_valid_from);
3419 EXTRACT_DATE("isds:ciNewTo",
3420 (*event)->details.storage_set.new_valid_to);
3421 EXTRACT_LONGINT("isds:ciOldCapacity",
3422 (*event)->details.storage_set.old_capacity, 0);
3423 EXTRACT_DATE("isds:ciOldFrom",
3424 (*event)->details.storage_set.old_valid_from);
3425 EXTRACT_DATE("isds:ciOldTo",
3426 (*event)->details.storage_set.old_valid_to);
3427 EXTRACT_STRING("isds:ciDoneBy",
3428 (*event)->details.storage_set.initiator);
3429 break;
3430 case ISDS_CREDIT_EXPIRED:
3431 break;
3434 leave:
3435 if (err) isds_credit_event_free(event);
3436 free(string);
3437 xmlXPathFreeObject(result);
3438 return err;
3442 #endif /* HAVE_LIBCURL */
3445 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3446 * isds_envelope structure. The envelope is automatically allocated but not
3447 * reallocated. The date are just appended into envelope structure.
3448 * @context is ISDS context
3449 * @envelope is automatically allocated message envelope structure
3450 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3451 * In case of error @envelope will be freed. */
3452 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3453 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3454 isds_error err = IE_SUCCESS;
3455 xmlXPathObjectPtr result = NULL;
3457 if (!context) return IE_INVALID_CONTEXT;
3458 if (!envelope) return IE_INVAL;
3459 if (!xpath_ctx) return IE_INVAL;
3462 if (!*envelope) {
3463 /* Allocate envelope */
3464 *envelope = calloc(1, sizeof(**envelope));
3465 if (!*envelope) {
3466 err = IE_NOMEM;
3467 goto leave;
3469 } else {
3470 /* Else free former data */
3471 zfree((*envelope)->dmSenderOrgUnit);
3472 zfree((*envelope)->dmSenderOrgUnitNum);
3473 zfree((*envelope)->dbIDRecipient);
3474 zfree((*envelope)->dmRecipientOrgUnit);
3475 zfree((*envelope)->dmRecipientOrgUnitNum);
3476 zfree((*envelope)->dmToHands);
3477 zfree((*envelope)->dmAnnotation);
3478 zfree((*envelope)->dmRecipientRefNumber);
3479 zfree((*envelope)->dmSenderRefNumber);
3480 zfree((*envelope)->dmRecipientIdent);
3481 zfree((*envelope)->dmSenderIdent);
3482 zfree((*envelope)->dmLegalTitleLaw);
3483 zfree((*envelope)->dmLegalTitleYear);
3484 zfree((*envelope)->dmLegalTitleSect);
3485 zfree((*envelope)->dmLegalTitlePar);
3486 zfree((*envelope)->dmLegalTitlePoint);
3487 zfree((*envelope)->dmPersonalDelivery);
3488 zfree((*envelope)->dmAllowSubstDelivery);
3491 /* Extract envelope elements added by sender or ISDS
3492 * (XSD: gMessageEnvelopeSub type) */
3493 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3494 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3495 (*envelope)->dmSenderOrgUnitNum, 0);
3496 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3497 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3498 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3499 (*envelope)->dmRecipientOrgUnitNum, 0);
3500 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3501 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3502 EXTRACT_STRING("isds:dmRecipientRefNumber",
3503 (*envelope)->dmRecipientRefNumber);
3504 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3505 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3506 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3508 /* Extract envelope elements regarding law reference */
3509 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3510 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3511 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3512 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3513 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3515 /* Extract envelope other elements */
3516 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3517 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3518 (*envelope)->dmAllowSubstDelivery);
3520 leave:
3521 if (err) isds_envelope_free(envelope);
3522 xmlXPathFreeObject(result);
3523 return err;
3528 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3529 * isds_envelope structure. The envelope is automatically allocated but not
3530 * reallocated. The date are just appended into envelope structure.
3531 * @context is ISDS context
3532 * @envelope is automatically allocated message envelope structure
3533 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3534 * In case of error @envelope will be freed. */
3535 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3536 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3537 isds_error err = IE_SUCCESS;
3538 xmlXPathObjectPtr result = NULL;
3540 if (!context) return IE_INVALID_CONTEXT;
3541 if (!envelope) return IE_INVAL;
3542 if (!xpath_ctx) return IE_INVAL;
3545 if (!*envelope) {
3546 /* Allocate envelope */
3547 *envelope = calloc(1, sizeof(**envelope));
3548 if (!*envelope) {
3549 err = IE_NOMEM;
3550 goto leave;
3552 } else {
3553 /* Else free former data */
3554 zfree((*envelope)->dmID);
3555 zfree((*envelope)->dbIDSender);
3556 zfree((*envelope)->dmSender);
3557 zfree((*envelope)->dmSenderAddress);
3558 zfree((*envelope)->dmSenderType);
3559 zfree((*envelope)->dmRecipient);
3560 zfree((*envelope)->dmRecipientAddress);
3561 zfree((*envelope)->dmAmbiguousRecipient);
3564 /* Extract envelope elements added by ISDS
3565 * (XSD: gMessageEnvelope type) */
3566 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3567 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3568 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3569 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3570 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3571 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3572 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3573 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3574 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3575 (*envelope)->dmAmbiguousRecipient);
3577 /* Extract envelope elements added by sender and ISDS
3578 * (XSD: gMessageEnvelope type) */
3579 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3580 if (err) goto leave;
3582 leave:
3583 if (err) isds_envelope_free(envelope);
3584 xmlXPathFreeObject(result);
3585 return err;
3589 /* Convert other envelope elements from XML tree into isds_envelope structure:
3590 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3591 * The envelope is automatically allocated but not reallocated.
3592 * The data are just appended into envelope structure.
3593 * @context is ISDS context
3594 * @envelope is automatically allocated message envelope structure
3595 * @xpath_ctx is XPath context with current node as parent desired elements
3596 * In case of error @envelope will be freed. */
3597 static isds_error append_status_size_times(struct isds_ctx *context,
3598 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3599 isds_error err = IE_SUCCESS;
3600 xmlXPathObjectPtr result = NULL;
3601 char *string = NULL;
3602 unsigned long int *unumber = NULL;
3604 if (!context) return IE_INVALID_CONTEXT;
3605 if (!envelope) return IE_INVAL;
3606 if (!xpath_ctx) return IE_INVAL;
3609 if (!*envelope) {
3610 /* Allocate new */
3611 *envelope = calloc(1, sizeof(**envelope));
3612 if (!*envelope) {
3613 err = IE_NOMEM;
3614 goto leave;
3616 } else {
3617 /* Free old data */
3618 zfree((*envelope)->dmMessageStatus);
3619 zfree((*envelope)->dmAttachmentSize);
3620 zfree((*envelope)->dmDeliveryTime);
3621 zfree((*envelope)->dmAcceptanceTime);
3625 /* dmMessageStatus element is mandatory */
3626 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3627 if (!unumber) {
3628 isds_log_message(context,
3629 _("Missing mandatory sisds:dmMessageStatus integer"));
3630 err = IE_ISDS;
3631 goto leave;
3633 err = uint2isds_message_status(context, unumber,
3634 &((*envelope)->dmMessageStatus));
3635 if (err) {
3636 if (err == IE_ENUM) err = IE_ISDS;
3637 goto leave;
3639 free(unumber); unumber = NULL;
3641 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3644 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3645 if (string) {
3646 err = timestring2timeval((xmlChar *) string,
3647 &((*envelope)->dmDeliveryTime));
3648 if (err) {
3649 char *string_locale = _isds_utf82locale(string);
3650 if (err == IE_DATE) err = IE_ISDS;
3651 isds_printf_message(context,
3652 _("Could not convert dmDeliveryTime as ISO time: %s"),
3653 string_locale);
3654 free(string_locale);
3655 goto leave;
3657 zfree(string);
3660 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3661 if (string) {
3662 err = timestring2timeval((xmlChar *) string,
3663 &((*envelope)->dmAcceptanceTime));
3664 if (err) {
3665 char *string_locale = _isds_utf82locale(string);
3666 if (err == IE_DATE) err = IE_ISDS;
3667 isds_printf_message(context,
3668 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3669 string_locale);
3670 free(string_locale);
3671 goto leave;
3673 zfree(string);
3676 leave:
3677 if (err) isds_envelope_free(envelope);
3678 free(unumber);
3679 free(string);
3680 xmlXPathFreeObject(result);
3681 return err;
3685 /* Convert message type attribute of current element into isds_envelope
3686 * structure.
3687 * TODO: This function can be incorporated into append_status_size_times() as
3688 * they are called always together.
3689 * The envelope is automatically allocated but not reallocated.
3690 * The data are just appended into envelope structure.
3691 * @context is ISDS context
3692 * @envelope is automatically allocated message envelope structure
3693 * @xpath_ctx is XPath context with current node as parent of attribute
3694 * carrying message type
3695 * In case of error @envelope will be freed. */
3696 static isds_error append_message_type(struct isds_ctx *context,
3697 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3698 isds_error err = IE_SUCCESS;
3700 if (!context) return IE_INVALID_CONTEXT;
3701 if (!envelope) return IE_INVAL;
3702 if (!xpath_ctx) return IE_INVAL;
3705 if (!*envelope) {
3706 /* Allocate new */
3707 *envelope = calloc(1, sizeof(**envelope));
3708 if (!*envelope) {
3709 err = IE_NOMEM;
3710 goto leave;
3712 } else {
3713 /* Free old data */
3714 zfree((*envelope)->dmType);
3718 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3720 if (!(*envelope)->dmType) {
3721 /* Use default value */
3722 (*envelope)->dmType = strdup("V");
3723 if (!(*envelope)->dmType) {
3724 err = IE_NOMEM;
3725 goto leave;
3727 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3728 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3729 isds_printf_message(context,
3730 _("Message type in dmType attribute is not 1 character long: "
3731 "%s"),
3732 type_locale);
3733 free(type_locale);
3734 err = IE_ISDS;
3735 goto leave;
3738 leave:
3739 if (err) isds_envelope_free(envelope);
3740 return err;
3744 #if HAVE_LIBCURL
3745 /* Convert dmType isds_envelope member into XML attribute and append it to
3746 * current node.
3747 * @context is ISDS context
3748 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3749 * @dm_envelope is XML element the resulting attribute will be appended to.
3750 * @return error code, in case of error context' message is filled. */
3751 static isds_error insert_message_type(struct isds_ctx *context,
3752 const char *type, xmlNodePtr dm_envelope) {
3753 isds_error err = IE_SUCCESS;
3754 xmlAttrPtr attribute_node;
3756 if (!context) return IE_INVALID_CONTEXT;
3757 if (!dm_envelope) return IE_INVAL;
3759 /* Insert optional message type */
3760 if (type) {
3761 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3762 char *type_locale = _isds_utf82locale(type);
3763 isds_printf_message(context,
3764 _("Message type in envelope is not 1 character long: %s"),
3765 type_locale);
3766 free(type_locale);
3767 err = IE_INVAL;
3768 goto leave;
3770 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3773 leave:
3774 return err;
3776 #endif /* HAVE_LIBCURL */
3779 /* Extract message document into reallocated document structure
3780 * @context is ISDS context
3781 * @document is automatically reallocated message documents structure
3782 * @xpath_ctx is XPath context with current node as isds:dmFile
3783 * In case of error @document will be freed. */
3784 static isds_error extract_document(struct isds_ctx *context,
3785 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3786 isds_error err = IE_SUCCESS;
3787 xmlXPathObjectPtr result = NULL;
3788 xmlNodePtr file_node = xpath_ctx->node;
3789 char *string = NULL;
3791 if (!context) return IE_INVALID_CONTEXT;
3792 if (!document) return IE_INVAL;
3793 isds_document_free(document);
3794 if (!xpath_ctx) return IE_INVAL;
3796 *document = calloc(1, sizeof(**document));
3797 if (!*document) {
3798 err = IE_NOMEM;
3799 goto leave;
3802 /* Extract document meta data */
3803 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3804 if (context->normalize_mime_type) {
3805 const char *normalized_type =
3806 isds_normalize_mime_type((*document)->dmMimeType);
3807 if (NULL != normalized_type &&
3808 normalized_type != (*document)->dmMimeType) {
3809 char *new_type = strdup(normalized_type);
3810 if (NULL == new_type) {
3811 isds_printf_message(context,
3812 _("Not enough memory to normalize document MIME type"));
3813 err = IE_NOMEM;
3814 goto leave;
3816 free((*document)->dmMimeType);
3817 (*document)->dmMimeType = new_type;
3821 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3822 err = string2isds_FileMetaType((xmlChar*)string,
3823 &((*document)->dmFileMetaType));
3824 if (err) {
3825 char *meta_type_locale = _isds_utf82locale(string);
3826 isds_printf_message(context,
3827 _("Document has invalid dmFileMetaType attribute value: %s"),
3828 meta_type_locale);
3829 free(meta_type_locale);
3830 err = IE_ISDS;
3831 goto leave;
3833 zfree(string);
3835 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3836 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3837 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3838 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3841 /* Extract document data.
3842 * Base64 encoded blob or XML subtree must be presented. */
3844 /* Check for dmEncodedContent */
3845 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3846 xpath_ctx);
3847 if (!result) {
3848 err = IE_XML;
3849 goto leave;
3852 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3853 /* Here we have Base64 blob */
3854 (*document)->is_xml = 0;
3856 if (result->nodesetval->nodeNr > 1) {
3857 isds_printf_message(context,
3858 _("Document has more dmEncodedContent elements"));
3859 err = IE_ISDS;
3860 goto leave;
3863 xmlXPathFreeObject(result); result = NULL;
3864 EXTRACT_STRING("isds:dmEncodedContent", string);
3866 /* Decode non-empty document */
3867 if (string && string[0] != '\0') {
3868 (*document)->data_length =
3869 _isds_b64decode(string, &((*document)->data));
3870 if ((*document)->data_length == (size_t) -1) {
3871 isds_printf_message(context,
3872 _("Error while Base64-decoding document content"));
3873 err = IE_ERROR;
3874 goto leave;
3877 } else {
3878 /* No Base64 blob, try XML document */
3879 xmlXPathFreeObject(result); result = NULL;
3880 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3881 xpath_ctx);
3882 if (!result) {
3883 err = IE_XML;
3884 goto leave;
3887 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3888 /* Here we have XML document */
3889 (*document)->is_xml = 1;
3891 if (result->nodesetval->nodeNr > 1) {
3892 isds_printf_message(context,
3893 _("Document has more dmXMLContent elements"));
3894 err = IE_ISDS;
3895 goto leave;
3898 /* XXX: We cannot serialize the content simply because:
3899 * - XML document may point out of its scope (e.g. to message
3900 * envelope)
3901 * - isds:dmXMLContent can contain more elements, no element,
3902 * a text node only
3903 * - it's not the XML way
3904 * Thus we provide the only right solution: XML DOM. Let's
3905 * application to cope with this hot potato :) */
3906 (*document)->xml_node_list =
3907 result->nodesetval->nodeTab[0]->children;
3908 } else {
3909 /* No base64 blob, nor XML document */
3910 isds_printf_message(context,
3911 _("Document has no dmEncodedContent, nor dmXMLContent "
3912 "element"));
3913 err = IE_ISDS;
3914 goto leave;
3919 leave:
3920 if (err) isds_document_free(document);
3921 free(string);
3922 xmlXPathFreeObject(result);
3923 xpath_ctx->node = file_node;
3924 return err;
3929 /* Extract message documents into reallocated list of documents
3930 * @context is ISDS context
3931 * @documents is automatically reallocated message documents list structure
3932 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3933 * In case of error @documents will be freed. */
3934 static isds_error extract_documents(struct isds_ctx *context,
3935 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
3936 isds_error err = IE_SUCCESS;
3937 xmlXPathObjectPtr result = NULL;
3938 xmlNodePtr files_node = xpath_ctx->node;
3939 struct isds_list *document, *prev_document = NULL;
3941 if (!context) return IE_INVALID_CONTEXT;
3942 if (!documents) return IE_INVAL;
3943 isds_list_free(documents);
3944 if (!xpath_ctx) return IE_INVAL;
3946 /* Find documents */
3947 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
3948 if (!result) {
3949 err = IE_XML;
3950 goto leave;
3953 /* No match */
3954 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3955 isds_printf_message(context,
3956 _("Message does not contain any document"));
3957 err = IE_ISDS;
3958 goto leave;
3962 /* Iterate over documents */
3963 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3965 /* Allocate and append list item */
3966 document = calloc(1, sizeof(*document));
3967 if (!document) {
3968 err = IE_NOMEM;
3969 goto leave;
3971 document->destructor = (void (*)(void **))isds_document_free;
3972 if (i == 0) *documents = document;
3973 else prev_document->next = document;
3974 prev_document = document;
3976 /* Extract document */
3977 xpath_ctx->node = result->nodesetval->nodeTab[i];
3978 err = extract_document(context,
3979 (struct isds_document **) &(document->data), xpath_ctx);
3980 if (err) goto leave;
3984 leave:
3985 if (err) isds_list_free(documents);
3986 xmlXPathFreeObject(result);
3987 xpath_ctx->node = files_node;
3988 return err;
3992 #if HAVE_LIBCURL
3993 /* Convert isds:dmRecord XML tree into structure
3994 * @context is ISDS context
3995 * @envelope is automatically reallocated message envelope structure
3996 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3997 * In case of error @envelope will be freed. */
3998 static isds_error extract_DmRecord(struct isds_ctx *context,
3999 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4000 isds_error err = IE_SUCCESS;
4001 xmlXPathObjectPtr result = NULL;
4003 if (!context) return IE_INVALID_CONTEXT;
4004 if (!envelope) return IE_INVAL;
4005 isds_envelope_free(envelope);
4006 if (!xpath_ctx) return IE_INVAL;
4009 *envelope = calloc(1, sizeof(**envelope));
4010 if (!*envelope) {
4011 err = IE_NOMEM;
4012 goto leave;
4016 /* Extract tRecord data */
4017 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4019 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4020 * dmAcceptanceTime. */
4021 err = append_status_size_times(context, envelope, xpath_ctx);
4022 if (err) goto leave;
4024 /* Extract envelope elements added by sender and ISDS
4025 * (XSD: gMessageEnvelope type) */
4026 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4027 if (err) goto leave;
4029 /* Get message type */
4030 err = append_message_type(context, envelope, xpath_ctx);
4031 if (err) goto leave;
4034 leave:
4035 if (err) isds_envelope_free(envelope);
4036 xmlXPathFreeObject(result);
4037 return err;
4041 /* Convert XSD:tStateChangesRecord type XML tree into structure
4042 * @context is ISDS context
4043 * @changed_status is automatically reallocated message state change structure
4044 * @xpath_ctx is XPath context with current node as element of
4045 * XSD:tStateChangesRecord type
4046 * In case of error @changed_status will be freed. */
4047 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4048 struct isds_message_status_change **changed_status,
4049 xmlXPathContextPtr xpath_ctx) {
4050 isds_error err = IE_SUCCESS;
4051 xmlXPathObjectPtr result = NULL;
4052 unsigned long int *unumber = NULL;
4053 char *string = NULL;
4055 if (!context) return IE_INVALID_CONTEXT;
4056 if (!changed_status) return IE_INVAL;
4057 isds_message_status_change_free(changed_status);
4058 if (!xpath_ctx) return IE_INVAL;
4061 *changed_status = calloc(1, sizeof(**changed_status));
4062 if (!*changed_status) {
4063 err = IE_NOMEM;
4064 goto leave;
4068 /* Extract tGetStateChangesInput data */
4069 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4071 /* dmEventTime is mandatory */
4072 EXTRACT_STRING("isds:dmEventTime", string);
4073 if (string) {
4074 err = timestring2timeval((xmlChar *) string,
4075 &((*changed_status)->time));
4076 if (err) {
4077 char *string_locale = _isds_utf82locale(string);
4078 if (err == IE_DATE) err = IE_ISDS;
4079 isds_printf_message(context,
4080 _("Could not convert dmEventTime as ISO time: %s"),
4081 string_locale);
4082 free(string_locale);
4083 goto leave;
4085 zfree(string);
4088 /* dmMessageStatus element is mandatory */
4089 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4090 if (!unumber) {
4091 isds_log_message(context,
4092 _("Missing mandatory isds:dmMessageStatus integer"));
4093 err = IE_ISDS;
4094 goto leave;
4096 err = uint2isds_message_status(context, unumber,
4097 &((*changed_status)->dmMessageStatus));
4098 if (err) {
4099 if (err == IE_ENUM) err = IE_ISDS;
4100 goto leave;
4102 zfree(unumber);
4105 leave:
4106 free(unumber);
4107 free(string);
4108 if (err) isds_message_status_change_free(changed_status);
4109 xmlXPathFreeObject(result);
4110 return err;
4112 #endif /* HAVE_LIBCURL */
4115 /* Find and convert isds:dmHash XML tree into structure
4116 * @context is ISDS context
4117 * @envelope is automatically reallocated message hash structure
4118 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4119 * In case of error @hash will be freed. */
4120 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4121 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4122 isds_error err = IE_SUCCESS;
4123 xmlNodePtr old_ctx_node;
4124 xmlXPathObjectPtr result = NULL;
4125 char *string = NULL;
4127 if (!context) return IE_INVALID_CONTEXT;
4128 if (!hash) return IE_INVAL;
4129 isds_hash_free(hash);
4130 if (!xpath_ctx) return IE_INVAL;
4132 old_ctx_node = xpath_ctx->node;
4134 *hash = calloc(1, sizeof(**hash));
4135 if (!*hash) {
4136 err = IE_NOMEM;
4137 goto leave;
4140 /* Locate dmHash */
4141 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4142 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4143 err = IE_ISDS;
4144 goto leave;
4146 if (err) {
4147 err = IE_ERROR;
4148 goto leave;
4151 /* Get hash algorithm */
4152 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4153 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4154 if (err) {
4155 if (err == IE_ENUM) {
4156 char *string_locale = _isds_utf82locale(string);
4157 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4158 string_locale);
4159 free(string_locale);
4161 goto leave;
4163 zfree(string);
4165 /* Get hash value */
4166 EXTRACT_STRING(".", string);
4167 if (!string) {
4168 isds_printf_message(context,
4169 _("sisds:dmHash element is missing hash value"));
4170 err = IE_ISDS;
4171 goto leave;
4173 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4174 if ((*hash)->length == (size_t) -1) {
4175 isds_printf_message(context,
4176 _("Error while Base64-decoding hash value"));
4177 err = IE_ERROR;
4178 goto leave;
4181 leave:
4182 if (err) isds_hash_free(hash);
4183 free(string);
4184 xmlXPathFreeObject(result);
4185 xpath_ctx->node = old_ctx_node;
4186 return err;
4190 /* Find and append isds:dmQTimestamp XML tree into envelope.
4191 * Because one service is allowed to miss time-stamp content, and we think
4192 * other could too (flaw in specification), this function is deliberated and
4193 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4194 * @context is ISDS context
4195 * @envelope is automatically allocated envelope structure
4196 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4197 * child
4198 * In case of error @envelope will be freed. */
4199 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4200 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4201 isds_error err = IE_SUCCESS;
4202 xmlXPathObjectPtr result = NULL;
4203 char *string = NULL;
4205 if (!context) return IE_INVALID_CONTEXT;
4206 if (!envelope) return IE_INVAL;
4207 if (!xpath_ctx) {
4208 isds_envelope_free(envelope);
4209 return IE_INVAL;
4212 if (!*envelope) {
4213 *envelope = calloc(1, sizeof(**envelope));
4214 if (!*envelope) {
4215 err = IE_NOMEM;
4216 goto leave;
4218 } else {
4219 zfree((*envelope)->timestamp);
4220 (*envelope)->timestamp_length = 0;
4223 /* Get dmQTimestamp */
4224 EXTRACT_STRING("sisds:dmQTimestamp", string);
4225 if (!string) {
4226 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4227 goto leave;
4229 (*envelope)->timestamp_length =
4230 _isds_b64decode(string, &((*envelope)->timestamp));
4231 if ((*envelope)->timestamp_length == (size_t) -1) {
4232 isds_printf_message(context,
4233 _("Error while Base64-decoding time stamp value"));
4234 err = IE_ERROR;
4235 goto leave;
4238 leave:
4239 if (err) isds_envelope_free(envelope);
4240 free(string);
4241 xmlXPathFreeObject(result);
4242 return err;
4246 /* Convert XSD tReturnedMessage XML tree into message structure.
4247 * It does not store serialized XML tree into message->raw.
4248 * It does store (pointer to) parsed XML tree into message->xml if needed.
4249 * @context is ISDS context
4250 * @include_documents Use true if documents must be extracted
4251 * (tReturnedMessage XSD type), use false if documents shall be omitted
4252 * (tReturnedMessageEnvelope).
4253 * @message is automatically reallocated message structure
4254 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4255 * type
4256 * In case of error @message will be freed. */
4257 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4258 const _Bool include_documents, struct isds_message **message,
4259 xmlXPathContextPtr xpath_ctx) {
4260 isds_error err = IE_SUCCESS;
4261 xmlNodePtr message_node;
4263 if (!context) return IE_INVALID_CONTEXT;
4264 if (!message) return IE_INVAL;
4265 isds_message_free(message);
4266 if (!xpath_ctx) return IE_INVAL;
4269 *message = calloc(1, sizeof(**message));
4270 if (!*message) {
4271 err = IE_NOMEM;
4272 goto leave;
4275 /* Save message XPATH context node */
4276 message_node = xpath_ctx->node;
4279 /* Extract dmDM */
4280 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4281 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4282 if (err) { err = IE_ERROR; goto leave; }
4283 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4284 if (err) goto leave;
4286 if (include_documents) {
4287 struct isds_list *item;
4289 /* Extract dmFiles */
4290 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4291 xpath_ctx);
4292 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4293 err = IE_ISDS; goto leave;
4295 if (err) { err = IE_ERROR; goto leave; }
4296 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4297 if (err) goto leave;
4299 /* Store xmlDoc of this message if needed */
4300 /* Only if we got a XML document in all the documents. */
4301 for (item = (*message)->documents; item; item = item->next) {
4302 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4303 (*message)->xml = xpath_ctx->doc;
4304 break;
4310 /* Restore context to message */
4311 xpath_ctx->node = message_node;
4313 /* Extract dmHash */
4314 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4315 xpath_ctx);
4316 if (err) goto leave;
4318 /* Extract dmQTimestamp, */
4319 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4320 xpath_ctx);
4321 if (err) goto leave;
4323 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4324 * dmAcceptanceTime. */
4325 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4326 if (err) goto leave;
4328 /* Get message type */
4329 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4330 if (err) goto leave;
4332 leave:
4333 if (err) isds_message_free(message);
4334 return err;
4338 /* Extract message event into reallocated isds_event structure
4339 * @context is ISDS context
4340 * @event is automatically reallocated message event structure
4341 * @xpath_ctx is XPath context with current node as isds:dmEvent
4342 * In case of error @event will be freed. */
4343 static isds_error extract_event(struct isds_ctx *context,
4344 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4345 isds_error err = IE_SUCCESS;
4346 xmlXPathObjectPtr result = NULL;
4347 xmlNodePtr event_node = xpath_ctx->node;
4348 char *string = NULL;
4350 if (!context) return IE_INVALID_CONTEXT;
4351 if (!event) return IE_INVAL;
4352 isds_event_free(event);
4353 if (!xpath_ctx) return IE_INVAL;
4355 *event = calloc(1, sizeof(**event));
4356 if (!*event) {
4357 err = IE_NOMEM;
4358 goto leave;
4361 /* Extract event data.
4362 * All elements are optional according XSD. That's funny. */
4363 EXTRACT_STRING("sisds:dmEventTime", string);
4364 if (string) {
4365 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4366 if (err) {
4367 char *string_locale = _isds_utf82locale(string);
4368 if (err == IE_DATE) err = IE_ISDS;
4369 isds_printf_message(context,
4370 _("Could not convert dmEventTime as ISO time: %s"),
4371 string_locale);
4372 free(string_locale);
4373 goto leave;
4375 zfree(string);
4378 /* dmEventDescr element has prefix and the rest */
4379 EXTRACT_STRING("sisds:dmEventDescr", string);
4380 if (string) {
4381 err = eventstring2event((xmlChar *) string, *event);
4382 if (err) goto leave;
4383 zfree(string);
4386 leave:
4387 if (err) isds_event_free(event);
4388 free(string);
4389 xmlXPathFreeObject(result);
4390 xpath_ctx->node = event_node;
4391 return err;
4395 /* Convert element of XSD tEventsArray type from XML tree into
4396 * isds_list of isds_event's structure. The list is automatically reallocated.
4397 * @context is ISDS context
4398 * @events is automatically reallocated list of event structures
4399 * @xpath_ctx is XPath context with current node as tEventsArray
4400 * In case of error @events will be freed. */
4401 static isds_error extract_events(struct isds_ctx *context,
4402 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4403 isds_error err = IE_SUCCESS;
4404 xmlXPathObjectPtr result = NULL;
4405 xmlNodePtr events_node = xpath_ctx->node;
4406 struct isds_list *event, *prev_event = NULL;
4408 if (!context) return IE_INVALID_CONTEXT;
4409 if (!events) return IE_INVAL;
4410 if (!xpath_ctx) return IE_INVAL;
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 server SOAP body response as XML document
4723 * @raw_response is automatically 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 ISDS status code
4727 * @status_message is 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 free(*code);
4749 free(*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 disables, 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 xmlChar *code = NULL, *message = NULL;
7274 xmlXPathContextPtr xpath_ctx = NULL;
7275 xmlXPathObjectPtr result = NULL;
7276 xmlChar *string = NULL;
7277 #endif
7279 if (!context) return IE_INVALID_CONTEXT;
7280 zfree(context->long_message);
7281 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7283 #if HAVE_LIBCURL
7284 /* Check if connection is established
7285 * TODO: This check should be done downstairs. */
7286 if (!context->curl) return IE_CONNECTION_CLOSED;
7289 /* Build CheckDataBox request */
7290 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7291 if (!request) {
7292 isds_log_message(context,
7293 _("Could build CheckDataBox request"));
7294 return IE_ERROR;
7296 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7297 if(!isds_ns) {
7298 isds_log_message(context, _("Could not create ISDS name space"));
7299 xmlFreeNode(request);
7300 return IE_ERROR;
7302 xmlSetNs(request, isds_ns);
7303 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7304 if (!db_id) {
7305 isds_log_message(context, _("Could not add dbID child to "
7306 "CheckDataBox element"));
7307 xmlFreeNode(request);
7308 return IE_ERROR;
7312 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
7314 /* Sent request */
7315 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7317 /* Destroy request */
7318 xmlFreeNode(request);
7320 if (err) {
7321 isds_log(ILF_ISDS, ILL_DEBUG,
7322 _("Processing ISDS response on CheckDataBox "
7323 "request failed\n"));
7324 goto leave;
7327 /* Check for response status */
7328 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7329 &code, &message, NULL);
7330 if (err) {
7331 isds_log(ILF_ISDS, ILL_DEBUG,
7332 _("ISDS response on CheckDataBox request is missing status\n"));
7333 goto leave;
7336 /* Request processed, but nothing found */
7337 if (!xmlStrcmp(code, BAD_CAST "5001")) {
7338 char *box_id_locale = _isds_utf82locale((char*)box_id);
7339 char *code_locale = _isds_utf82locale((char*)code);
7340 char *message_locale = _isds_utf82locale((char*)message);
7341 isds_log(ILF_ISDS, ILL_DEBUG,
7342 _("Server did not found box %s on CheckDataBox request "
7343 "(code=%s, message=%s)\n"),
7344 box_id_locale, code_locale, message_locale);
7345 isds_log_message(context, message_locale);
7346 free(box_id_locale);
7347 free(code_locale);
7348 free(message_locale);
7349 err = IE_NOEXIST;
7350 goto leave;
7353 /* Other error */
7354 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7355 char *code_locale = _isds_utf82locale((char*)code);
7356 char *message_locale = _isds_utf82locale((char*)message);
7357 isds_log(ILF_ISDS, ILL_DEBUG,
7358 _("Server refused CheckDataBox request "
7359 "(code=%s, message=%s)\n"), code_locale, message_locale);
7360 isds_log_message(context, message_locale);
7361 free(code_locale);
7362 free(message_locale);
7363 err = IE_ISDS;
7364 goto leave;
7367 /* Extract data */
7368 xpath_ctx = xmlXPathNewContext(response);
7369 if (!xpath_ctx) {
7370 err = IE_ERROR;
7371 goto leave;
7373 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7374 err = IE_ERROR;
7375 goto leave;
7377 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7378 xpath_ctx);
7379 if (!result) {
7380 err = IE_ERROR;
7381 goto leave;
7383 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7384 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7385 err = IE_ISDS;
7386 goto leave;
7388 if (result->nodesetval->nodeNr > 1) {
7389 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7390 err = IE_ISDS;
7391 goto leave;
7393 xpath_ctx->node = result->nodesetval->nodeTab[0];
7394 xmlXPathFreeObject(result); result = NULL;
7396 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7399 leave:
7400 free(string);
7401 xmlXPathFreeObject(result);
7402 xmlXPathFreeContext(xpath_ctx);
7404 free(code);
7405 free(message);
7406 xmlFreeDoc(response);
7408 if (!err)
7409 isds_log(ILF_ISDS, ILL_DEBUG,
7410 _("CheckDataBox request processed by server successfully.\n"));
7411 #else /* not HAVE_LIBCURL */
7412 err = IE_NOTSUP;
7413 #endif
7415 return err;
7419 /* Get list of permissions to send commercial messages.
7420 * @context is ISDS session context.
7421 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7422 * @permissions is a reallocated list of permissions (struct
7423 * isds_commercial_permission*) to send commercial messages from @box_id. The
7424 * order of permissions is significant as the server applies the permissions
7425 * and associated pre-paid credits in the order. Empty list means no
7426 * permission.
7427 * @return:
7428 * IE_SUCCESS if the list has been obtained correctly,
7429 * or other appropriate error. */
7430 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7431 const char *box_id, struct isds_list **permissions) {
7432 isds_error err = IE_SUCCESS;
7433 #if HAVE_LIBCURL
7434 xmlDocPtr response = NULL;
7435 xmlXPathContextPtr xpath_ctx = NULL;
7436 xmlXPathObjectPtr result = NULL;
7437 #endif
7439 if (!context) return IE_INVALID_CONTEXT;
7440 zfree(context->long_message);
7441 if (NULL == permissions) return IE_INVAL;
7442 isds_list_free(permissions);
7443 if (NULL == box_id) return IE_INVAL;
7445 #if HAVE_LIBCURL
7446 /* Check if connection is established */
7447 if (!context->curl) return IE_CONNECTION_CLOSED;
7449 /* Do request and check for success */
7450 err = build_send_dbid_request_check_response(context,
7451 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7452 BAD_CAST box_id, NULL, &response, NULL);
7453 if (!err) {
7454 isds_log(ILF_ISDS, ILL_DEBUG,
7455 _("PDZInfo request processed by server successfully.\n"));
7458 /* Extract data */
7459 /* Prepare structure */
7460 xpath_ctx = xmlXPathNewContext(response);
7461 if (!xpath_ctx) {
7462 err = IE_ERROR;
7463 goto leave;
7465 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7466 err = IE_ERROR;
7467 goto leave;
7470 /* Set context node */
7471 result = xmlXPathEvalExpression(BAD_CAST
7472 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7473 xpath_ctx);
7474 if (!result) {
7475 err = IE_ERROR;
7476 goto leave;
7478 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7479 /* Iterate over all permission records */
7480 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7481 struct isds_list *item, *prev_item = NULL;
7483 /* Prepare structure */
7484 item = calloc(1, sizeof(*item));
7485 if (!item) {
7486 err = IE_NOMEM;
7487 goto leave;
7489 item->destructor = (void(*)(void**))isds_commercial_permission_free;
7490 if (i == 0) *permissions = item;
7491 else prev_item->next = item;
7492 prev_item = item;
7494 /* Extract it */
7495 xpath_ctx->node = result->nodesetval->nodeTab[i];
7496 err = extract_DbPDZRecord(context,
7497 (struct isds_commercial_permission **) (&item->data),
7498 xpath_ctx);
7499 if (err) goto leave;
7503 leave:
7504 if (err) {
7505 isds_list_free(permissions);
7508 xmlXPathFreeObject(result);
7509 xmlXPathFreeContext(xpath_ctx);
7510 xmlFreeDoc(response);
7512 #else /* not HAVE_LIBCURL */
7513 err = IE_NOTSUP;
7514 #endif
7516 return err;
7520 /* Get details about credit for sending pre-paid commercial messages.
7521 * @context is ISDS session context.
7522 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
7523 * @from_date is first day of credit history to return in @history. Only
7524 * tm_year, tm_mon and tm_mday carry sane value.
7525 * @to_date is last day of credit history to return in @history. Only
7526 * tm_year, tm_mon and tm_mday carry sane value.
7527 * @credit outputs current credit value into pre-allocated memory. Pass NULL
7528 * if you don't care. This and all other credit values are integers in
7529 * hundredths of Czech Crowns.
7530 * @email outputs notification e-mail address where notifications about credit
7531 * are sent. This is automatically reallocated string. Pass NULL if you don't
7532 * care. It can return NULL if no address is defined.
7533 * @history outputs auto-reallocated list of pointers to struct
7534 * isds_credit_event. Events in closed interval @from_time to @to_time are
7535 * returned. Pass NULL @to_time and @from_time if you don't care. The events
7536 * are sorted by time.
7537 * @return:
7538 * IE_SUCCESS if the credit details have been obtained correctly,
7539 * or other appropriate error. Please note that server allows to retrieve
7540 * only limited history of events. */
7541 isds_error isds_get_commercial_credit(struct isds_ctx *context,
7542 const char *box_id,
7543 const struct tm *from_date, const struct tm *to_date,
7544 long int *credit, char **email, struct isds_list **history) {
7545 isds_error err = IE_SUCCESS;
7546 #if HAVE_LIBCURL
7547 char *box_id_locale = NULL;
7548 xmlNodePtr request = NULL, node;
7549 xmlNsPtr isds_ns = NULL;
7550 xmlChar *string = NULL;
7552 xmlDocPtr response = NULL;
7553 xmlXPathContextPtr xpath_ctx = NULL;
7554 xmlXPathObjectPtr result = NULL;
7556 const xmlChar *codes[] = {
7557 /* TODO: Gather codes by experiment */
7558 BAD_CAST "",
7559 BAD_CAST "",
7560 BAD_CAST "",
7561 BAD_CAST "",
7562 BAD_CAST "",
7563 NULL
7565 const char *meanings[] = {
7566 "Insufficient priviledges for the box",
7567 "The box does not exist",
7568 "Date is too long (history is not available after 15 months)",
7569 "Interval is too long (limit is 3 months)",
7570 "Invalid date"
7572 const isds_error errors[] = {
7573 IE_ISDS,
7574 IE_NOEXIST,
7575 IE_DATE,
7576 IE_DATE,
7577 IE_DATE,
7579 struct code_map_isds_error map = {
7580 .codes = codes,
7581 .meanings = meanings,
7582 .errors = errors
7584 #endif
7586 if (!context) return IE_INVALID_CONTEXT;
7587 zfree(context->long_message);
7589 /* Free output argument */
7590 if (NULL != credit) *credit = 0;
7591 if (NULL != email) zfree(*email);
7592 isds_list_free(history);
7594 if (NULL == box_id) return IE_INVAL;
7596 #if HAVE_LIBCURL
7597 /* Check if connection is established */
7598 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7600 box_id_locale = _isds_utf82locale((char*)box_id);
7601 if (NULL == box_id_locale) {
7602 err = IE_NOMEM;
7603 goto leave;
7606 /* Build request */
7607 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
7608 if (NULL == request) {
7609 isds_printf_message(context,
7610 _("Could not build DataBoxCreditInfo request for %s box"),
7611 box_id_locale);
7612 err = IE_ERROR;
7613 goto leave;
7615 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7616 if(!isds_ns) {
7617 isds_log_message(context, _("Could not create ISDS name space"));
7618 err = IE_ERROR;
7619 goto leave;
7621 xmlSetNs(request, isds_ns);
7623 /* Add mandatory XSD:tIdDbInput child */
7624 INSERT_STRING(request, BAD_CAST "dbID", box_id);
7625 /* Add mandatory dates elements with optional values */
7626 if (from_date) {
7627 err = tm2datestring(from_date, &string);
7628 if (err) {
7629 isds_log_message(context,
7630 _("Could not convert `from_date' argument to ISO date "
7631 "string"));
7632 goto leave;
7634 INSERT_STRING(request, "ciFromDate", string);
7635 zfree(string);
7636 } else {
7637 INSERT_STRING(request, "ciFromDate", NULL);
7639 if (to_date) {
7640 err = tm2datestring(to_date, &string);
7641 if (err) {
7642 isds_log_message(context,
7643 _("Could not convert `to_date' argument to ISO date "
7644 "string"));
7645 goto leave;
7647 INSERT_STRING(request, "ciTodate", string);
7648 zfree(string);
7649 } else {
7650 INSERT_STRING(request, "ciTodate", NULL);
7653 /* Send request and check response*/
7654 err = send_destroy_request_check_response(context,
7655 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
7656 &request, &response, NULL, &map);
7657 if (err) goto leave;
7660 /* Extract data */
7661 /* Set context to the root */
7662 xpath_ctx = xmlXPathNewContext(response);
7663 if (!xpath_ctx) {
7664 err = IE_ERROR;
7665 goto leave;
7667 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7668 err = IE_ERROR;
7669 goto leave;
7671 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
7672 xpath_ctx);
7673 if (!result) {
7674 err = IE_ERROR;
7675 goto leave;
7677 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7678 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
7679 err = IE_ISDS;
7680 goto leave;
7682 if (result->nodesetval->nodeNr > 1) {
7683 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
7684 err = IE_ISDS;
7685 goto leave;
7687 xpath_ctx->node = result->nodesetval->nodeTab[0];
7688 xmlXPathFreeObject(result); result = NULL;
7690 /* Extract common data */
7691 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
7692 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
7694 /* Extract records */
7695 if (NULL == history) goto leave;
7696 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
7697 xpath_ctx);
7698 if (!result) {
7699 err = IE_ERROR;
7700 goto leave;
7702 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7703 struct isds_list *prev_item = NULL;
7705 /* Iterate over all records */
7706 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7707 struct isds_list *item;
7709 /* Prepare structure */
7710 item = calloc(1, sizeof(*item));
7711 if (!item) {
7712 err = IE_NOMEM;
7713 goto leave;
7715 item->destructor = (void(*)(void**))isds_credit_event_free;
7716 if (i == 0) *history = item;
7717 else prev_item->next = item;
7718 prev_item = item;
7720 /* Extract it */
7721 xpath_ctx->node = result->nodesetval->nodeTab[i];
7722 err = extract_CiRecord(context,
7723 (struct isds_credit_event **) (&item->data),
7724 xpath_ctx);
7725 if (err) goto leave;
7729 leave:
7730 if (!err) {
7731 isds_log(ILF_ISDS, ILL_DEBUG,
7732 _("DataBoxCreditInfo request processed by server successfully.\n"));
7734 if (err) {
7735 isds_list_free(history);
7736 zfree(*email)
7739 free(box_id_locale);
7740 xmlXPathFreeObject(result);
7741 xmlXPathFreeContext(xpath_ctx);
7742 xmlFreeDoc(response);
7744 #else /* not HAVE_LIBCURL */
7745 err = IE_NOTSUP;
7746 #endif
7748 return err;
7752 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
7753 * code, destroy response and log success.
7754 * @context is ISDS session context.
7755 * @service_name is name of SERVICE_DB_MANIPULATION service
7756 * @box_id is UTF-8 encoded box identifier as zero terminated string
7757 * @approval is optional external approval of box manipulation
7758 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7759 * NULL, if you don't care. */
7760 static isds_error build_send_manipulationdbid_request_check_drop_response(
7761 struct isds_ctx *context, const xmlChar *service_name,
7762 const xmlChar *box_id, const struct isds_approval *approval,
7763 xmlChar **refnumber) {
7764 isds_error err = IE_SUCCESS;
7765 #if HAVE_LIBCURL
7766 xmlDocPtr response = NULL;
7767 #endif
7769 if (!context) return IE_INVALID_CONTEXT;
7770 zfree(context->long_message);
7771 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
7773 #if HAVE_LIBCURL
7774 /* Check if connection is established */
7775 if (!context->curl) return IE_CONNECTION_CLOSED;
7777 /* Do request and check for success */
7778 err = build_send_dbid_request_check_response(context,
7779 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
7780 &response, refnumber);
7781 xmlFreeDoc(response);
7783 if (!err) {
7784 char *service_name_locale = _isds_utf82locale((char *) service_name);
7785 isds_log(ILF_ISDS, ILL_DEBUG,
7786 _("%s request processed by server successfully.\n"),
7787 service_name_locale);
7788 free(service_name_locale);
7790 #else /* not HAVE_LIBCURL */
7791 err = IE_NOTSUP;
7792 #endif
7794 return err;
7798 /* Switch box into state where box can receive commercial messages (off by
7799 * default)
7800 * @context is ISDS session context.
7801 * @box_id is UTF-8 encoded box identifier as zero terminated string
7802 * @allow is true for enable, false for disable commercial messages income
7803 * @approval is optional external approval of box manipulation
7804 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7805 * NULL, if you don't care. */
7806 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
7807 const char *box_id, const _Bool allow,
7808 const struct isds_approval *approval, char **refnumber) {
7809 return build_send_manipulationdbid_request_check_drop_response(context,
7810 (allow) ? BAD_CAST "SetOpenAddressing" :
7811 BAD_CAST "ClearOpenAddressing",
7812 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7816 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
7817 * message acceptance). This is just a box permission. Sender must apply
7818 * such role by sending each message.
7819 * @context is ISDS session context.
7820 * @box_id is UTF-8 encoded box identifier as zero terminated string
7821 * @allow is true for enable, false for disable OVM role permission
7822 * @approval is optional external approval of box manipulation
7823 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7824 * NULL, if you don't care. */
7825 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
7826 const char *box_id, const _Bool allow,
7827 const struct isds_approval *approval, char **refnumber) {
7828 return build_send_manipulationdbid_request_check_drop_response(context,
7829 (allow) ? BAD_CAST "SetEffectiveOVM" :
7830 BAD_CAST "ClearEffectiveOVM",
7831 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7835 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
7836 * code, destroy response and log success.
7837 * @context is ISDS session context.
7838 * @service_name is name of SERVICE_DB_MANIPULATION service
7839 * @owner is structure describing box
7840 * @approval is optional external approval of box manipulation
7841 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7842 * NULL, if you don't care. */
7843 static isds_error build_send_manipulationdbowner_request_check_drop_response(
7844 struct isds_ctx *context, const xmlChar *service_name,
7845 const struct isds_DbOwnerInfo *owner,
7846 const struct isds_approval *approval, xmlChar **refnumber) {
7847 isds_error err = IE_SUCCESS;
7848 #if HAVE_LIBCURL
7849 char *service_name_locale = NULL;
7850 xmlNodePtr request = NULL, db_owner_info;
7851 xmlNsPtr isds_ns = NULL;
7852 #endif
7855 if (!context) return IE_INVALID_CONTEXT;
7856 zfree(context->long_message);
7857 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
7859 #if HAVE_LIBCURL
7860 service_name_locale = _isds_utf82locale((char*)service_name);
7861 if (!service_name_locale) {
7862 err = IE_NOMEM;
7863 goto leave;
7866 /* Build request */
7867 request = xmlNewNode(NULL, service_name);
7868 if (!request) {
7869 isds_printf_message(context,
7870 _("Could not build %s request"), service_name_locale);
7871 err = IE_ERROR;
7872 goto leave;
7874 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7875 if(!isds_ns) {
7876 isds_log_message(context, _("Could not create ISDS name space"));
7877 err = IE_ERROR;
7878 goto leave;
7880 xmlSetNs(request, isds_ns);
7883 /* Add XSD:tOwnerInfoInput child*/
7884 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
7885 err = insert_DbOwnerInfo(context, owner, db_owner_info);
7886 if (err) goto leave;
7888 /* Add XSD:gExtApproval*/
7889 err = insert_GExtApproval(context, approval, request);
7890 if (err) goto leave;
7892 /* Send it to server and process response */
7893 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7894 service_name, &request, refnumber);
7896 leave:
7897 xmlFreeNode(request);
7898 free(service_name_locale);
7899 #else /* not HAVE_LIBCURL */
7900 err = IE_NOTSUP;
7901 #endif
7903 return err;
7907 /* Switch box accessibility state on request of box owner.
7908 * Despite the name, owner must do the request off-line. This function is
7909 * designed for such off-line meeting points (e.g. Czech POINT).
7910 * @context is ISDS session context.
7911 * @box identifies box to switch accessibility state.
7912 * @allow is true for making accessible, false to disallow access.
7913 * @approval is optional external approval of box manipulation
7914 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7915 * NULL, if you don't care. */
7916 isds_error isds_switch_box_accessibility_on_owner_request(
7917 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7918 const _Bool allow, const struct isds_approval *approval,
7919 char **refnumber) {
7920 return build_send_manipulationdbowner_request_check_drop_response(context,
7921 (allow) ? BAD_CAST "EnableOwnDataBox" :
7922 BAD_CAST "DisableOwnDataBox",
7923 box, approval, (xmlChar **) refnumber);
7927 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
7928 * date.
7929 * @context is ISDS session context.
7930 * @box identifies box to switch accessibility state.
7931 * @since is date since accessibility has been denied. This can be past too.
7932 * Only tm_year, tm_mon and tm_mday carry sane value.
7933 * @approval is optional external approval of box manipulation
7934 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7935 * NULL, if you don't care. */
7936 isds_error isds_disable_box_accessibility_externaly(
7937 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7938 const struct tm *since, const struct isds_approval *approval,
7939 char **refnumber) {
7940 isds_error err = IE_SUCCESS;
7941 #if HAVE_LIBCURL
7942 char *service_name_locale = NULL;
7943 xmlNodePtr request = NULL, node;
7944 xmlNsPtr isds_ns = NULL;
7945 xmlChar *string = NULL;
7946 #endif
7949 if (!context) return IE_INVALID_CONTEXT;
7950 zfree(context->long_message);
7951 if (!box || !since) return IE_INVAL;
7953 #if HAVE_LIBCURL
7954 /* Build request */
7955 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
7956 if (!request) {
7957 isds_printf_message(context,
7958 _("Could not build %s request"), "DisableDataBoxExternally");
7959 err = IE_ERROR;
7960 goto leave;
7962 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7963 if(!isds_ns) {
7964 isds_log_message(context, _("Could not create ISDS name space"));
7965 err = IE_ERROR;
7966 goto leave;
7968 xmlSetNs(request, isds_ns);
7971 /* Add @box identification */
7972 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7973 err = insert_DbOwnerInfo(context, box, node);
7974 if (err) goto leave;
7976 /* Add @since date */
7977 err = tm2datestring(since, &string);
7978 if(err) {
7979 isds_log_message(context,
7980 _("Could not convert `since' argument to ISO date string"));
7981 goto leave;
7983 INSERT_STRING(request, "dbOwnerDisableDate", string);
7984 zfree(string);
7986 /* Add @approval */
7987 err = insert_GExtApproval(context, approval, request);
7988 if (err) goto leave;
7990 /* Send it to server and process response */
7991 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7992 BAD_CAST "DisableDataBoxExternally", &request,
7993 (xmlChar **) refnumber);
7995 leave:
7996 free(string);
7997 xmlFreeNode(request);
7998 free(service_name_locale);
7999 #else /* not HAVE_LIBCURL */
8000 err = IE_NOTSUP;
8001 #endif
8003 return err;
8007 #if HAVE_LIBCURL
8008 /* Insert struct isds_message data (envelope (recipient data optional) and
8009 * documents into XML tree
8010 * @context is session context
8011 * @outgoing_message is libisds structure with message data
8012 * @create_message is XML CreateMessage or CreateMultipleMessage element
8013 * @process_recipient true for recipient data serialization, false for no
8014 * serialization */
8015 static isds_error insert_envelope_files(struct isds_ctx *context,
8016 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8017 const _Bool process_recipient) {
8019 isds_error err = IE_SUCCESS;
8020 xmlNodePtr envelope, dm_files, node;
8021 xmlChar *string = NULL;
8023 if (!context) return IE_INVALID_CONTEXT;
8024 if (!outgoing_message || !create_message) return IE_INVAL;
8027 /* Build envelope */
8028 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8029 if (!envelope) {
8030 isds_printf_message(context, _("Could not add dmEnvelope child to "
8031 "%s element"), create_message->name);
8032 return IE_ERROR;
8035 if (!outgoing_message->envelope) {
8036 isds_log_message(context, _("Outgoing message is missing envelope"));
8037 err = IE_INVAL;
8038 goto leave;
8041 /* Insert optional message type */
8042 err = insert_message_type(context, outgoing_message->envelope->dmType,
8043 envelope);
8044 if (err) goto leave;
8046 INSERT_STRING(envelope, "dmSenderOrgUnit",
8047 outgoing_message->envelope->dmSenderOrgUnit);
8048 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8049 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8051 if (process_recipient) {
8052 if (!outgoing_message->envelope->dbIDRecipient) {
8053 isds_log_message(context,
8054 _("Outgoing message is missing recipient box identifier"));
8055 err = IE_INVAL;
8056 goto leave;
8058 INSERT_STRING(envelope, "dbIDRecipient",
8059 outgoing_message->envelope->dbIDRecipient);
8061 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8062 outgoing_message->envelope->dmRecipientOrgUnit);
8063 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8064 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8065 INSERT_STRING(envelope, "dmToHands",
8066 outgoing_message->envelope->dmToHands);
8069 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8070 "dmAnnotation");
8071 INSERT_STRING(envelope, "dmAnnotation",
8072 outgoing_message->envelope->dmAnnotation);
8074 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8075 0, 50, "dmRecipientRefNumber");
8076 INSERT_STRING(envelope, "dmRecipientRefNumber",
8077 outgoing_message->envelope->dmRecipientRefNumber);
8079 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8080 0, 50, "dmSenderRefNumber");
8081 INSERT_STRING(envelope, "dmSenderRefNumber",
8082 outgoing_message->envelope->dmSenderRefNumber);
8084 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8085 0, 50, "dmRecipientIdent");
8086 INSERT_STRING(envelope, "dmRecipientIdent",
8087 outgoing_message->envelope->dmRecipientIdent);
8089 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8090 0, 50, "dmSenderIdent");
8091 INSERT_STRING(envelope, "dmSenderIdent",
8092 outgoing_message->envelope->dmSenderIdent);
8094 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8095 outgoing_message->envelope->dmLegalTitleLaw, string);
8096 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8097 outgoing_message->envelope->dmLegalTitleYear, string);
8098 INSERT_STRING(envelope, "dmLegalTitleSect",
8099 outgoing_message->envelope->dmLegalTitleSect);
8100 INSERT_STRING(envelope, "dmLegalTitlePar",
8101 outgoing_message->envelope->dmLegalTitlePar);
8102 INSERT_STRING(envelope, "dmLegalTitlePoint",
8103 outgoing_message->envelope->dmLegalTitlePoint);
8105 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8106 outgoing_message->envelope->dmPersonalDelivery);
8107 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8108 outgoing_message->envelope->dmAllowSubstDelivery);
8110 /* ???: Should we require value for dbEffectiveOVM sender?
8111 * ISDS has default as true */
8112 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8113 INSERT_BOOLEAN(envelope, "dmOVM",
8114 outgoing_message->envelope->dmPublishOwnID);
8117 /* Append dmFiles */
8118 if (!outgoing_message->documents) {
8119 isds_log_message(context,
8120 _("Outgoing message is missing list of documents"));
8121 err = IE_INVAL;
8122 goto leave;
8124 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8125 if (!dm_files) {
8126 isds_printf_message(context, _("Could not add dmFiles child to "
8127 "%s element"), create_message->name);
8128 err = IE_ERROR;
8129 goto leave;
8132 /* Check for document hierarchy */
8133 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8134 if (err) goto leave;
8136 /* Process each document */
8137 for (struct isds_list *item =
8138 (struct isds_list *) outgoing_message->documents;
8139 item; item = item->next) {
8140 if (!item->data) {
8141 isds_log_message(context,
8142 _("List of documents contains empty item"));
8143 err = IE_INVAL;
8144 goto leave;
8146 /* FIXME: Check for dmFileMetaType and for document references.
8147 * Only first document can be of MAIN type */
8148 err = insert_document(context, (struct isds_document*) item->data,
8149 dm_files);
8151 if (err) goto leave;
8154 leave:
8155 free(string);
8156 return err;
8158 #endif /* HAVE_LIBCURL */
8161 /* Send a message via ISDS to a recipient
8162 * @context is session context
8163 * @outgoing_message is message to send; Some members are mandatory (like
8164 * dbIDRecipient), some are optional and some are irrelevant (especially data
8165 * about sender). Included pointer to isds_list documents must contain at
8166 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8167 * members will be filled with valid data from ISDS. Exact list of write
8168 * members is subject to change. Currently dmID is changed.
8169 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8170 isds_error isds_send_message(struct isds_ctx *context,
8171 struct isds_message *outgoing_message) {
8173 isds_error err = IE_SUCCESS;
8174 #if HAVE_LIBCURL
8175 xmlNsPtr isds_ns = NULL;
8176 xmlNodePtr request = NULL;
8177 xmlDocPtr response = NULL;
8178 xmlChar *code = NULL, *message = NULL;
8179 xmlXPathContextPtr xpath_ctx = NULL;
8180 xmlXPathObjectPtr result = NULL;
8181 /*_Bool message_is_complete = 0;*/
8182 #endif
8184 if (!context) return IE_INVALID_CONTEXT;
8185 zfree(context->long_message);
8186 if (!outgoing_message) return IE_INVAL;
8188 #if HAVE_LIBCURL
8189 /* Check if connection is established
8190 * TODO: This check should be done downstairs. */
8191 if (!context->curl) return IE_CONNECTION_CLOSED;
8194 /* Build CreateMessage request */
8195 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8196 if (!request) {
8197 isds_log_message(context,
8198 _("Could not build CreateMessage request"));
8199 return IE_ERROR;
8201 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8202 if(!isds_ns) {
8203 isds_log_message(context, _("Could not create ISDS name space"));
8204 xmlFreeNode(request);
8205 return IE_ERROR;
8207 xmlSetNs(request, isds_ns);
8209 /* Append envelope and files */
8210 err = insert_envelope_files(context, outgoing_message, request, 1);
8211 if (err) goto leave;
8214 /* Signal we can serialize message since now */
8215 /*message_is_complete = 1;*/
8218 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8220 /* Sent request */
8221 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8223 /* Don't' destroy request, we want to provide it to application later */
8225 if (err) {
8226 isds_log(ILF_ISDS, ILL_DEBUG,
8227 _("Processing ISDS response on CreateMessage "
8228 "request failed\n"));
8229 goto leave;
8232 /* Check for response status */
8233 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8234 &code, &message, NULL);
8235 if (err) {
8236 isds_log(ILF_ISDS, ILL_DEBUG,
8237 _("ISDS response on CreateMessage request "
8238 "is missing status\n"));
8239 goto leave;
8242 /* Request processed, but refused by server or server failed */
8243 if (xmlStrcmp(code, BAD_CAST "0000")) {
8244 char *box_id_locale =
8245 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8246 char *code_locale = _isds_utf82locale((char*)code);
8247 char *message_locale = _isds_utf82locale((char*)message);
8248 isds_log(ILF_ISDS, ILL_DEBUG,
8249 _("Server did not accept message for %s on CreateMessage "
8250 "request (code=%s, message=%s)\n"),
8251 box_id_locale, code_locale, message_locale);
8252 isds_log_message(context, message_locale);
8253 free(box_id_locale);
8254 free(code_locale);
8255 free(message_locale);
8256 err = IE_ISDS;
8257 goto leave;
8261 /* Extract data */
8262 xpath_ctx = xmlXPathNewContext(response);
8263 if (!xpath_ctx) {
8264 err = IE_ERROR;
8265 goto leave;
8267 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8268 err = IE_ERROR;
8269 goto leave;
8271 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8272 xpath_ctx);
8273 if (!result) {
8274 err = IE_ERROR;
8275 goto leave;
8277 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8278 isds_log_message(context, _("Missing CreateMessageResponse element"));
8279 err = IE_ISDS;
8280 goto leave;
8282 if (result->nodesetval->nodeNr > 1) {
8283 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8284 err = IE_ISDS;
8285 goto leave;
8287 xpath_ctx->node = result->nodesetval->nodeTab[0];
8288 xmlXPathFreeObject(result); result = NULL;
8290 if (outgoing_message->envelope->dmID) {
8291 free(outgoing_message->envelope->dmID);
8292 outgoing_message->envelope->dmID = NULL;
8294 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8295 if (!outgoing_message->envelope->dmID) {
8296 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8297 "but did not return assigned message ID\n"));
8300 leave:
8301 /* TODO: Serialize message into structure member raw */
8302 /* XXX: Each web service transport message in different format.
8303 * Therefore it's not possible to save them directly.
8304 * To save them, one must figure out common format.
8305 * We can leave it on application, or we can implement the ESS format. */
8306 /*if (message_is_complete) {
8307 if (outgoing_message->envelope->dmID) {
8309 /* Add assigned message ID as first child*/
8310 /*xmlNodePtr dmid_text = xmlNewText(
8311 (xmlChar *) outgoing_message->envelope->dmID);
8312 if (!dmid_text) goto serialization_failed;
8314 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8315 BAD_CAST "dmID");
8316 if (!dmid_element) {
8317 xmlFreeNode(dmid_text);
8318 goto serialization_failed;
8321 xmlNodePtr dmid_element_with_text =
8322 xmlAddChild(dmid_element, dmid_text);
8323 if (!dmid_element_with_text) {
8324 xmlFreeNode(dmid_element);
8325 xmlFreeNode(dmid_text);
8326 goto serialization_failed;
8329 node = xmlAddPrevSibling(envelope->childern,
8330 dmid_element_with_text);
8331 if (!node) {
8332 xmlFreeNodeList(dmid_element_with_text);
8333 goto serialization_failed;
8337 /* Serialize message with ID into raw */
8338 /*buffer = serialize_element(envelope)*/
8339 /* }
8341 serialization_failed:
8345 /* Clean up */
8346 xmlXPathFreeObject(result);
8347 xmlXPathFreeContext(xpath_ctx);
8349 free(code);
8350 free(message);
8351 xmlFreeDoc(response);
8352 xmlFreeNode(request);
8354 if (!err)
8355 isds_log(ILF_ISDS, ILL_DEBUG,
8356 _("CreateMessage request processed by server "
8357 "successfully.\n"));
8358 #else /* not HAVE_LIBCURL */
8359 err = IE_NOTSUP;
8360 #endif
8362 return err;
8366 /* Send a message via ISDS to a multiple recipients
8367 * @context is session context
8368 * @outgoing_message is message to send; Some members are mandatory,
8369 * some are optional and some are irrelevant (especially data
8370 * about sender). Data about recipient will be substituted by ISDS from
8371 * @copies. Included pointer to isds_list documents must
8372 * contain at least one document of FILEMETATYPE_MAIN.
8373 * @copies is list of isds_message_copy structures addressing all desired
8374 * recipients. This is read-write structure, some members will be filled with
8375 * valid data from ISDS (message IDs, error codes, error descriptions).
8376 * @return
8377 * ISDS_SUCCESS if all messages have been sent
8378 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8379 * succeeded messages can be identified by copies->data->error),
8380 * or other error code if something other goes wrong. */
8381 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8382 const struct isds_message *outgoing_message,
8383 struct isds_list *copies) {
8385 isds_error err = IE_SUCCESS;
8386 #if HAVE_LIBCURL
8387 isds_error append_err;
8388 xmlNsPtr isds_ns = NULL;
8389 xmlNodePtr request = NULL, recipients, recipient, node;
8390 struct isds_list *item;
8391 struct isds_message_copy *copy;
8392 xmlDocPtr response = NULL;
8393 xmlChar *code = NULL, *message = NULL;
8394 xmlXPathContextPtr xpath_ctx = NULL;
8395 xmlXPathObjectPtr result = NULL;
8396 xmlChar *string = NULL;
8397 int i;
8398 #endif
8400 if (!context) return IE_INVALID_CONTEXT;
8401 zfree(context->long_message);
8402 if (!outgoing_message || !copies) return IE_INVAL;
8404 #if HAVE_LIBCURL
8405 /* Check if connection is established
8406 * TODO: This check should be done downstairs. */
8407 if (!context->curl) return IE_CONNECTION_CLOSED;
8410 /* Build CreateMultipleMessage request */
8411 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
8412 if (!request) {
8413 isds_log_message(context,
8414 _("Could not build CreateMultipleMessage request"));
8415 return IE_ERROR;
8417 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8418 if(!isds_ns) {
8419 isds_log_message(context, _("Could not create ISDS name space"));
8420 xmlFreeNode(request);
8421 return IE_ERROR;
8423 xmlSetNs(request, isds_ns);
8426 /* Build recipients */
8427 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8428 if (!recipients) {
8429 isds_log_message(context, _("Could not add dmRecipients child to "
8430 "CreateMultipleMessage element"));
8431 xmlFreeNode(request);
8432 return IE_ERROR;
8435 /* Insert each recipient */
8436 for (item = copies; item; item = item->next) {
8437 copy = (struct isds_message_copy *) item->data;
8438 if (!copy) {
8439 isds_log_message(context,
8440 _("`copies' list item contains empty data"));
8441 err = IE_INVAL;
8442 goto leave;
8445 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
8446 if (!recipient) {
8447 isds_log_message(context, _("Could not add dmRecipient child to "
8448 "dmRecipients element"));
8449 err = IE_ERROR;
8450 goto leave;
8453 if (!copy->dbIDRecipient) {
8454 isds_log_message(context,
8455 _("Message copy is missing recipient box identifier"));
8456 err = IE_INVAL;
8457 goto leave;
8459 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
8460 INSERT_STRING(recipient, "dmRecipientOrgUnit",
8461 copy->dmRecipientOrgUnit);
8462 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
8463 copy->dmRecipientOrgUnitNum, string);
8464 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
8467 /* Append envelope and files */
8468 err = insert_envelope_files(context, outgoing_message, request, 0);
8469 if (err) goto leave;
8472 isds_log(ILF_ISDS, ILL_DEBUG,
8473 _("Sending CreateMultipleMessage request to ISDS\n"));
8475 /* Sent request */
8476 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8477 if (err) {
8478 isds_log(ILF_ISDS, ILL_DEBUG,
8479 _("Processing ISDS response on CreateMultipleMessage "
8480 "request failed\n"));
8481 goto leave;
8484 /* Check for response status */
8485 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8486 &code, &message, NULL);
8487 if (err) {
8488 isds_log(ILF_ISDS, ILL_DEBUG,
8489 _("ISDS response on CreateMultipleMessage request "
8490 "is missing status\n"));
8491 goto leave;
8494 /* Request processed, but some copies failed */
8495 if (!xmlStrcmp(code, BAD_CAST "0004")) {
8496 char *box_id_locale =
8497 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8498 char *code_locale = _isds_utf82locale((char*)code);
8499 char *message_locale = _isds_utf82locale((char*)message);
8500 isds_log(ILF_ISDS, ILL_DEBUG,
8501 _("Server did accept message for multiple recipients "
8502 "on CreateMultipleMessage request but delivery to "
8503 "some of them failed (code=%s, message=%s)\n"),
8504 box_id_locale, code_locale, message_locale);
8505 isds_log_message(context, message_locale);
8506 free(box_id_locale);
8507 free(code_locale);
8508 free(message_locale);
8509 err = IE_PARTIAL_SUCCESS;
8512 /* Request refused by server as whole */
8513 else if (xmlStrcmp(code, BAD_CAST "0000")) {
8514 char *box_id_locale =
8515 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8516 char *code_locale = _isds_utf82locale((char*)code);
8517 char *message_locale = _isds_utf82locale((char*)message);
8518 isds_log(ILF_ISDS, ILL_DEBUG,
8519 _("Server did not accept message for multiple recipients "
8520 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
8521 box_id_locale, code_locale, message_locale);
8522 isds_log_message(context, message_locale);
8523 free(box_id_locale);
8524 free(code_locale);
8525 free(message_locale);
8526 err = IE_ISDS;
8527 goto leave;
8531 /* Extract data */
8532 xpath_ctx = xmlXPathNewContext(response);
8533 if (!xpath_ctx) {
8534 err = IE_ERROR;
8535 goto leave;
8537 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8538 err = IE_ERROR;
8539 goto leave;
8541 result = xmlXPathEvalExpression(
8542 BAD_CAST "/isds:CreateMultipleMessageResponse"
8543 "/isds:dmMultipleStatus/isds:dmSingleStatus",
8544 xpath_ctx);
8545 if (!result) {
8546 err = IE_ERROR;
8547 goto leave;
8549 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8550 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
8551 err = IE_ISDS;
8552 goto leave;
8555 /* Extract message ID and delivery status for each copy */
8556 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
8557 item = item->next, i++) {
8558 copy = (struct isds_message_copy *) item->data;
8559 xpath_ctx->node = result->nodesetval->nodeTab[i];
8561 append_err = append_TMStatus(context, copy, xpath_ctx);
8562 if (append_err) {
8563 err = append_err;
8564 goto leave;
8567 if (item || i < result->nodesetval->nodeNr) {
8568 isds_printf_message(context, _("ISDS returned unexpected number of "
8569 "message copy delivery states: %d"),
8570 result->nodesetval->nodeNr);
8571 err = IE_ISDS;
8572 goto leave;
8576 leave:
8577 /* Clean up */
8578 free(string);
8579 xmlXPathFreeObject(result);
8580 xmlXPathFreeContext(xpath_ctx);
8582 free(code);
8583 free(message);
8584 xmlFreeDoc(response);
8585 xmlFreeNode(request);
8587 if (!err)
8588 isds_log(ILF_ISDS, ILL_DEBUG,
8589 _("CreateMultipleMessageResponse request processed by server "
8590 "successfully.\n"));
8591 #else /* not HAVE_LIBCURL */
8592 err = IE_NOTSUP;
8593 #endif
8595 return err;
8599 /* Get list of messages. This is common core for getting sent or received
8600 * messages.
8601 * Any criterion argument can be NULL, if you don't care about it.
8602 * @context is session context. Must not be NULL.
8603 * @outgoing_direction is true if you want list of outgoing messages,
8604 * it's false if you want incoming messages.
8605 * @from_time is minimal time and date of message sending inclusive.
8606 * @to_time is maximal time and date of message sending inclusive
8607 * @organization_unit_number is number of sender/recipient respectively.
8608 * @status_filter is bit field of isds_message_status values. Use special
8609 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8610 * all values, you can use bit-wise arithmetic if you want.)
8611 * @offset is index of first message we are interested in. First message is 1.
8612 * Set to 0 (or 1) if you don't care.
8613 * @number is maximal length of list you want to get as input value, outputs
8614 * number of messages matching these criteria. Can be NULL if you don't care
8615 * (applies to output value either).
8616 * @messages is automatically reallocated list of isds_message's. Be ware that
8617 * it returns only brief overview (envelope and some other fields) about each
8618 * message, not the complete message. FIXME: Specify exact fields.
8619 * The list is sorted by delivery time in ascending order.
8620 * Use NULL if you don't care about don't need the data (useful if you want to
8621 * know only the @number). If you provide &NULL, list will be allocated on
8622 * heap, if you provide pointer to non-NULL, list will be freed automatically
8623 * at first. Also in case of error the list will be NULLed.
8624 * @return IE_SUCCESS or appropriate error code. */
8625 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
8626 _Bool outgoing_direction,
8627 const struct timeval *from_time, const struct timeval *to_time,
8628 const long int *organization_unit_number,
8629 const unsigned int status_filter,
8630 const unsigned long int offset, unsigned long int *number,
8631 struct isds_list **messages) {
8633 isds_error err = IE_SUCCESS;
8634 #if HAVE_LIBCURL
8635 xmlNsPtr isds_ns = NULL;
8636 xmlNodePtr request = NULL, node;
8637 xmlDocPtr response = NULL;
8638 xmlChar *code = NULL, *message = NULL;
8639 xmlXPathContextPtr xpath_ctx = NULL;
8640 xmlXPathObjectPtr result = NULL;
8641 xmlChar *string = NULL;
8642 long unsigned int count = 0;
8643 #endif
8645 if (!context) return IE_INVALID_CONTEXT;
8646 zfree(context->long_message);
8648 /* Free former message list if any */
8649 if (messages) isds_list_free(messages);
8651 #if HAVE_LIBCURL
8652 /* Check if connection is established
8653 * TODO: This check should be done downstairs. */
8654 if (!context->curl) return IE_CONNECTION_CLOSED;
8656 /* Build GetListOf*Messages request */
8657 request = xmlNewNode(NULL,
8658 (outgoing_direction) ?
8659 BAD_CAST "GetListOfSentMessages" :
8660 BAD_CAST "GetListOfReceivedMessages"
8662 if (!request) {
8663 isds_log_message(context,
8664 (outgoing_direction) ?
8665 _("Could not build GetListOfSentMessages request") :
8666 _("Could not build GetListOfReceivedMessages request")
8668 return IE_ERROR;
8670 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8671 if(!isds_ns) {
8672 isds_log_message(context, _("Could not create ISDS name space"));
8673 xmlFreeNode(request);
8674 return IE_ERROR;
8676 xmlSetNs(request, isds_ns);
8679 if (from_time) {
8680 err = timeval2timestring(from_time, &string);
8681 if (err) goto leave;
8683 INSERT_STRING(request, "dmFromTime", string);
8684 free(string); string = NULL;
8686 if (to_time) {
8687 err = timeval2timestring(to_time, &string);
8688 if (err) goto leave;
8690 INSERT_STRING(request, "dmToTime", string);
8691 free(string); string = NULL;
8693 if (outgoing_direction) {
8694 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
8695 organization_unit_number, string);
8696 } else {
8697 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
8698 organization_unit_number, string);
8701 if (status_filter > MESSAGESTATE_ANY) {
8702 isds_printf_message(context,
8703 _("Invalid message state filter value: %ld"), status_filter);
8704 err = IE_INVAL;
8705 goto leave;
8707 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
8709 if (offset > 0 ) {
8710 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
8711 } else {
8712 INSERT_STRING(request, "dmOffset", "1");
8715 /* number 0 means no limit */
8716 if (number && *number == 0) {
8717 INSERT_STRING(request, "dmLimit", NULL);
8718 } else {
8719 INSERT_ULONGINT(request, "dmLimit", number, string);
8723 isds_log(ILF_ISDS, ILL_DEBUG,
8724 (outgoing_direction) ?
8725 _("Sending GetListOfSentMessages request to ISDS\n") :
8726 _("Sending GetListOfReceivedMessages request to ISDS\n")
8729 /* Sent request */
8730 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
8731 xmlFreeNode(request); request = NULL;
8733 if (err) {
8734 isds_log(ILF_ISDS, ILL_DEBUG,
8735 (outgoing_direction) ?
8736 _("Processing ISDS response on GetListOfSentMessages "
8737 "request failed\n") :
8738 _("Processing ISDS response on GetListOfReceivedMessages "
8739 "request failed\n")
8741 goto leave;
8744 /* Check for response status */
8745 err = isds_response_status(context, SERVICE_DM_INFO, response,
8746 &code, &message, NULL);
8747 if (err) {
8748 isds_log(ILF_ISDS, ILL_DEBUG,
8749 (outgoing_direction) ?
8750 _("ISDS response on GetListOfSentMessages request "
8751 "is missing status\n") :
8752 _("ISDS response on GetListOfReceivedMessages request "
8753 "is missing status\n")
8755 goto leave;
8758 /* Request processed, but nothing found */
8759 if (xmlStrcmp(code, BAD_CAST "0000")) {
8760 char *code_locale = _isds_utf82locale((char*)code);
8761 char *message_locale = _isds_utf82locale((char*)message);
8762 isds_log(ILF_ISDS, ILL_DEBUG,
8763 (outgoing_direction) ?
8764 _("Server refused GetListOfSentMessages request "
8765 "(code=%s, message=%s)\n") :
8766 _("Server refused GetListOfReceivedMessages request "
8767 "(code=%s, message=%s)\n"),
8768 code_locale, message_locale);
8769 isds_log_message(context, message_locale);
8770 free(code_locale);
8771 free(message_locale);
8772 err = IE_ISDS;
8773 goto leave;
8777 /* Extract data */
8778 xpath_ctx = xmlXPathNewContext(response);
8779 if (!xpath_ctx) {
8780 err = IE_ERROR;
8781 goto leave;
8783 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8784 err = IE_ERROR;
8785 goto leave;
8787 result = xmlXPathEvalExpression(
8788 (outgoing_direction) ?
8789 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
8790 "isds:dmRecords/isds:dmRecord" :
8791 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
8792 "isds:dmRecords/isds:dmRecord",
8793 xpath_ctx);
8794 if (!result) {
8795 err = IE_ERROR;
8796 goto leave;
8799 /* Fill output arguments in */
8800 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8801 struct isds_envelope *envelope;
8802 struct isds_list *item = NULL, *last_item = NULL;
8804 for (count = 0; count < result->nodesetval->nodeNr; count++) {
8805 /* Create new message */
8806 item = calloc(1, sizeof(*item));
8807 if (!item) {
8808 err = IE_NOMEM;
8809 goto leave;
8811 item->destructor = (void(*)(void**)) &isds_message_free;
8812 item->data = calloc(1, sizeof(struct isds_message));
8813 if (!item->data) {
8814 isds_list_free(&item);
8815 err = IE_NOMEM;
8816 goto leave;
8819 /* Extract envelope data */
8820 xpath_ctx->node = result->nodesetval->nodeTab[count];
8821 envelope = NULL;
8822 err = extract_DmRecord(context, &envelope, xpath_ctx);
8823 if (err) {
8824 isds_list_free(&item);
8825 goto leave;
8828 /* Attach extracted envelope */
8829 ((struct isds_message *) item->data)->envelope = envelope;
8831 /* Append new message into the list */
8832 if (!*messages) {
8833 *messages = last_item = item;
8834 } else {
8835 last_item->next = item;
8836 last_item = item;
8840 if (number) *number = count;
8842 leave:
8843 if (err) {
8844 isds_list_free(messages);
8847 free(string);
8848 xmlXPathFreeObject(result);
8849 xmlXPathFreeContext(xpath_ctx);
8851 free(code);
8852 free(message);
8853 xmlFreeDoc(response);
8854 xmlFreeNode(request);
8856 if (!err)
8857 isds_log(ILF_ISDS, ILL_DEBUG,
8858 (outgoing_direction) ?
8859 _("GetListOfSentMessages request processed by server "
8860 "successfully.\n") :
8861 _("GetListOfReceivedMessages request processed by server "
8862 "successfully.\n")
8864 #else /* not HAVE_LIBCURL */
8865 err = IE_NOTSUP;
8866 #endif
8867 return err;
8871 /* Get list of outgoing (already sent) messages.
8872 * Any criterion argument can be NULL, if you don't care about it.
8873 * @context is session context. Must not be NULL.
8874 * @from_time is minimal time and date of message sending inclusive.
8875 * @to_time is maximal time and date of message sending inclusive
8876 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
8877 * @status_filter is bit field of isds_message_status values. Use special
8878 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8879 * all values, you can use bit-wise arithmetic if you want.)
8880 * @offset is index of first message we are interested in. First message is 1.
8881 * Set to 0 (or 1) if you don't care.
8882 * @number is maximal length of list you want to get as input value, outputs
8883 * number of messages matching these criteria. Can be NULL if you don't care
8884 * (applies to output value either).
8885 * @messages is automatically reallocated list of isds_message's. Be ware that
8886 * it returns only brief overview (envelope and some other fields) about each
8887 * message, not the complete message. FIXME: Specify exact fields.
8888 * The list is sorted by delivery time in ascending order.
8889 * Use NULL if you don't care about the meta data (useful if you want to know
8890 * only the @number). If you provide &NULL, list will be allocated on heap,
8891 * if you provide pointer to non-NULL, list will be freed automatically at
8892 * first. Also in case of error the list will be NULLed.
8893 * @return IE_SUCCESS or appropriate error code. */
8894 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
8895 const struct timeval *from_time, const struct timeval *to_time,
8896 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
8897 const unsigned long int offset, unsigned long int *number,
8898 struct isds_list **messages) {
8900 return isds_get_list_of_messages(
8901 context, 1,
8902 from_time, to_time, dmSenderOrgUnitNum, status_filter,
8903 offset, number,
8904 messages);
8908 /* Get list of incoming (addressed to you) messages.
8909 * Any criterion argument can be NULL, if you don't care about it.
8910 * @context is session context. Must not be NULL.
8911 * @from_time is minimal time and date of message sending inclusive.
8912 * @to_time is maximal time and date of message sending inclusive
8913 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
8914 * @status_filter is bit field of isds_message_status values. Use special
8915 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8916 * all values, you can use bit-wise arithmetic if you want.)
8917 * @offset is index of first message we are interested in. First message is 1.
8918 * Set to 0 (or 1) if you don't care.
8919 * @number is maximal length of list you want to get as input value, outputs
8920 * number of messages matching these criteria. Can be NULL if you don't care
8921 * (applies to output value either).
8922 * @messages is automatically reallocated list of isds_message's. Be ware that
8923 * it returns only brief overview (envelope and some other fields) about each
8924 * message, not the complete message. FIXME: Specify exact fields.
8925 * Use NULL if you don't care about the meta data (useful if you want to know
8926 * only the @number). If you provide &NULL, list will be allocated on heap,
8927 * if you provide pointer to non-NULL, list will be freed automatically at
8928 * first. Also in case of error the list will be NULLed.
8929 * @return IE_SUCCESS or appropriate error code. */
8930 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
8931 const struct timeval *from_time, const struct timeval *to_time,
8932 const long int *dmRecipientOrgUnitNum,
8933 const unsigned int status_filter,
8934 const unsigned long int offset, unsigned long int *number,
8935 struct isds_list **messages) {
8937 return isds_get_list_of_messages(
8938 context, 0,
8939 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
8940 offset, number,
8941 messages);
8945 /* Get list of sent message state changes.
8946 * Any criterion argument can be NULL, if you don't care about it.
8947 * @context is session context. Must not be NULL.
8948 * @from_time is minimal time and date of status changes inclusive
8949 * @to_time is maximal time and date of status changes inclusive
8950 * @changed_states is automatically reallocated list of
8951 * isds_message_status_change's. If you provide &NULL, list will be allocated
8952 * on heap, if you provide pointer to non-NULL, list will be freed
8953 * automatically at first. Also in case of error the list will be NULLed.
8954 * XXX: The list item ordering is not specified.
8955 * XXX: Server provides only `recent' changes.
8956 * @return IE_SUCCESS or appropriate error code. */
8957 isds_error isds_get_list_of_sent_message_state_changes(
8958 struct isds_ctx *context,
8959 const struct timeval *from_time, const struct timeval *to_time,
8960 struct isds_list **changed_states) {
8962 isds_error err = IE_SUCCESS;
8963 #if HAVE_LIBCURL
8964 xmlNsPtr isds_ns = NULL;
8965 xmlNodePtr request = NULL, node;
8966 xmlDocPtr response = NULL;
8967 xmlXPathContextPtr xpath_ctx = NULL;
8968 xmlXPathObjectPtr result = NULL;
8969 xmlChar *string = NULL;
8970 long unsigned int count = 0;
8971 #endif
8973 if (!context) return IE_INVALID_CONTEXT;
8974 zfree(context->long_message);
8976 /* Free former message list if any */
8977 isds_list_free(changed_states);
8979 #if HAVE_LIBCURL
8980 /* Check if connection is established
8981 * TODO: This check should be done downstairs. */
8982 if (!context->curl) return IE_CONNECTION_CLOSED;
8984 /* Build GetMessageStateChanges request */
8985 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
8986 if (!request) {
8987 isds_log_message(context,
8988 _("Could not build GetMessageStateChanges request"));
8989 return IE_ERROR;
8991 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8992 if(!isds_ns) {
8993 isds_log_message(context, _("Could not create ISDS name space"));
8994 xmlFreeNode(request);
8995 return IE_ERROR;
8997 xmlSetNs(request, isds_ns);
9000 if (from_time) {
9001 err = timeval2timestring(from_time, &string);
9002 if (err) goto leave;
9004 INSERT_STRING(request, "dmFromTime", string);
9005 zfree(string);
9007 if (to_time) {
9008 err = timeval2timestring(to_time, &string);
9009 if (err) goto leave;
9011 INSERT_STRING(request, "dmToTime", string);
9012 zfree(string);
9015 /* Sent request */
9016 err = send_destroy_request_check_response(context,
9017 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9018 &response, NULL, NULL);
9019 if (err) goto leave;
9022 /* Extract data */
9023 xpath_ctx = xmlXPathNewContext(response);
9024 if (!xpath_ctx) {
9025 err = IE_ERROR;
9026 goto leave;
9028 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9029 err = IE_ERROR;
9030 goto leave;
9032 result = xmlXPathEvalExpression(
9033 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9034 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9035 if (!result) {
9036 err = IE_ERROR;
9037 goto leave;
9040 /* Fill output arguments in */
9041 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9042 struct isds_list *item = NULL, *last_item = NULL;
9044 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9045 /* Create new status change */
9046 item = calloc(1, sizeof(*item));
9047 if (!item) {
9048 err = IE_NOMEM;
9049 goto leave;
9051 item->destructor =
9052 (void(*)(void**)) &isds_message_status_change_free;
9054 /* Extract message status change */
9055 xpath_ctx->node = result->nodesetval->nodeTab[count];
9056 err = extract_StateChangesRecord(context,
9057 (struct isds_message_status_change **) &item->data,
9058 xpath_ctx);
9059 if (err) {
9060 isds_list_free(&item);
9061 goto leave;
9064 /* Append new message status change into the list */
9065 if (!*changed_states) {
9066 *changed_states = last_item = item;
9067 } else {
9068 last_item->next = item;
9069 last_item = item;
9074 leave:
9075 if (err) {
9076 isds_list_free(changed_states);
9079 free(string);
9080 xmlXPathFreeObject(result);
9081 xmlXPathFreeContext(xpath_ctx);
9082 xmlFreeDoc(response);
9083 xmlFreeNode(request);
9085 if (!err)
9086 isds_log(ILF_ISDS, ILL_DEBUG,
9087 _("GetMessageStateChanges request processed by server "
9088 "successfully.\n"));
9089 #else /* not HAVE_LIBCURL */
9090 err = IE_NOTSUP;
9091 #endif
9092 return err;
9096 #if HAVE_LIBCURL
9097 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9098 * code
9099 * @context is session context
9100 * @service is ISDS WS service handler
9101 * @service_name is name of SERVICE_DM_OPERATIONS
9102 * @message_id is message ID to send as service argument to ISDS
9103 * @response is server SOAP body response as XML document
9104 * @raw_response is automatically reallocated bit stream with response body. Use
9105 * NULL if you don't care
9106 * @raw_response_length is size of @raw_response in bytes
9107 * @code is ISDS status code
9108 * @status_message is ISDS status message
9109 * @return error coded from lower layer, context message will be set up
9110 * appropriately. */
9111 static isds_error build_send_check_message_request(struct isds_ctx *context,
9112 const isds_service service, const xmlChar *service_name,
9113 const char *message_id,
9114 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9115 xmlChar **code, xmlChar **status_message) {
9117 isds_error err = IE_SUCCESS;
9118 char *service_name_locale = NULL, *message_id_locale = NULL;
9119 xmlNodePtr request = NULL, node;
9120 xmlNsPtr isds_ns = NULL;
9122 if (!context) return IE_INVALID_CONTEXT;
9123 if (!service_name || !message_id) return IE_INVAL;
9124 if (!response || !code || !status_message) return IE_INVAL;
9125 if (!raw_response_length && raw_response) return IE_INVAL;
9127 /* Free output argument */
9128 xmlFreeDoc(*response); *response = NULL;
9129 if (raw_response) zfree(*raw_response);
9130 free(*code);
9131 free(*status_message);
9134 /* Check if connection is established
9135 * TODO: This check should be done downstairs. */
9136 if (!context->curl) return IE_CONNECTION_CLOSED;
9138 service_name_locale = _isds_utf82locale((char*)service_name);
9139 message_id_locale = _isds_utf82locale(message_id);
9140 if (!service_name_locale || !message_id_locale) {
9141 err = IE_NOMEM;
9142 goto leave;
9145 /* Build request */
9146 request = xmlNewNode(NULL, service_name);
9147 if (!request) {
9148 isds_printf_message(context,
9149 _("Could not build %s request for %s message ID"),
9150 service_name_locale, message_id_locale);
9151 err = IE_ERROR;
9152 goto leave;
9154 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9155 if(!isds_ns) {
9156 isds_log_message(context, _("Could not create ISDS name space"));
9157 err = IE_ERROR;
9158 goto leave;
9160 xmlSetNs(request, isds_ns);
9163 /* Add requested ID */
9164 err = validate_message_id_length(context, (xmlChar *) message_id);
9165 if (err) goto leave;
9166 INSERT_STRING(request, "dmID", message_id);
9169 isds_log(ILF_ISDS, ILL_DEBUG,
9170 _("Sending %s request for %s message ID to ISDS\n"),
9171 service_name_locale, message_id_locale);
9173 /* Send request */
9174 err = isds(context, service, request, response,
9175 raw_response, raw_response_length);
9176 xmlFreeNode(request); request = NULL;
9178 if (err) {
9179 isds_log(ILF_ISDS, ILL_DEBUG,
9180 _("Processing ISDS response on %s request failed\n"),
9181 service_name_locale);
9182 goto leave;
9185 /* Check for response status */
9186 err = isds_response_status(context, service, *response,
9187 code, status_message, NULL);
9188 if (err) {
9189 isds_log(ILF_ISDS, ILL_DEBUG,
9190 _("ISDS response on %s request is missing status\n"),
9191 service_name_locale);
9192 goto leave;
9195 /* Request processed, but nothing found */
9196 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9197 char *code_locale = _isds_utf82locale((char*) *code);
9198 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9199 isds_log(ILF_ISDS, ILL_DEBUG,
9200 _("Server refused %s request for %s message ID "
9201 "(code=%s, message=%s)\n"),
9202 service_name_locale, message_id_locale,
9203 code_locale, status_message_locale);
9204 isds_log_message(context, status_message_locale);
9205 free(code_locale);
9206 free(status_message_locale);
9207 err = IE_ISDS;
9208 goto leave;
9211 leave:
9212 free(message_id_locale);
9213 free(service_name_locale);
9214 xmlFreeNode(request);
9215 return err;
9219 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9220 * signed data and free ISDS response.
9221 * @context is session context
9222 * @message_id is UTF-8 encoded message ID for logging purpose
9223 * @response is parsed XML document. It will be freed and NULLed in the middle
9224 * of function run to save memory. This is not guaranteed in case of error.
9225 * @request_name is name of ISDS request used to construct response root
9226 * element name and for logging purpose.
9227 * @raw is reallocated output buffer with DER encoded CMS data
9228 * @raw_length is size of @raw buffer in bytes
9229 * @returns standard error codes, in case of error, @raw will be freed and
9230 * NULLed, @response sometimes. */
9231 static isds_error find_extract_signed_data_free_response(
9232 struct isds_ctx *context, const xmlChar *message_id,
9233 xmlDocPtr *response, const xmlChar *request_name,
9234 void **raw, size_t *raw_length) {
9236 isds_error err = IE_SUCCESS;
9237 char *xpath_expression = NULL;
9238 xmlXPathContextPtr xpath_ctx = NULL;
9239 xmlXPathObjectPtr result = NULL;
9240 char *encoded_structure = NULL;
9242 if (!context) return IE_INVALID_CONTEXT;
9243 if (!raw) return IE_INVAL;
9244 zfree(*raw);
9245 if (!message_id || !response || !*response || !request_name || !raw_length)
9246 return IE_INVAL;
9248 /* Build XPath expression */
9249 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9250 "Response/isds:dmSignature");
9251 if (!xpath_expression) return IE_NOMEM;
9253 /* Extract data */
9254 xpath_ctx = xmlXPathNewContext(*response);
9255 if (!xpath_ctx) {
9256 err = IE_ERROR;
9257 goto leave;
9259 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9260 err = IE_ERROR;
9261 goto leave;
9263 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9264 if (!result) {
9265 err = IE_ERROR;
9266 goto leave;
9268 /* Empty response */
9269 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9270 char *message_id_locale = _isds_utf82locale((char*) message_id);
9271 isds_printf_message(context,
9272 _("Server did not return any signed data for message ID `%s' "
9273 "on %s request"),
9274 message_id_locale, request_name);
9275 free(message_id_locale);
9276 err = IE_ISDS;
9277 goto leave;
9279 /* More responses */
9280 if (result->nodesetval->nodeNr > 1) {
9281 char *message_id_locale = _isds_utf82locale((char*) message_id);
9282 isds_printf_message(context,
9283 _("Server did return more signed data for message ID `%s' "
9284 "on %s request"),
9285 message_id_locale, request_name);
9286 free(message_id_locale);
9287 err = IE_ISDS;
9288 goto leave;
9290 /* One response */
9291 xpath_ctx->node = result->nodesetval->nodeTab[0];
9293 /* Extract PKCS#7 structure */
9294 EXTRACT_STRING(".", encoded_structure);
9295 if (!encoded_structure) {
9296 isds_log_message(context, _("dmSignature element is empty"));
9299 /* Here we have delivery info as standalone CMS in encoded_structure.
9300 * We don't need any other data, free them: */
9301 xmlXPathFreeObject(result); result = NULL;
9302 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9303 xmlFreeDoc(*response); *response = NULL;
9306 /* Decode PKCS#7 to DER format */
9307 *raw_length = _isds_b64decode(encoded_structure, raw);
9308 if (*raw_length == (size_t) -1) {
9309 isds_log_message(context,
9310 _("Error while Base64-decoding PKCS#7 structure"));
9311 err = IE_ERROR;
9312 goto leave;
9315 leave:
9316 if (err) {
9317 zfree(*raw);
9318 raw_length = 0;
9321 free(encoded_structure);
9322 xmlXPathFreeObject(result);
9323 xmlXPathFreeContext(xpath_ctx);
9324 free(xpath_expression);
9326 return err;
9328 #endif /* HAVE_LIBCURL */
9331 /* Download incoming message envelope identified by ID.
9332 * @context is session context
9333 * @message_id is message identifier (you can get them from
9334 * isds_get_list_of_received_messages())
9335 * @message is automatically reallocated message retrieved from ISDS.
9336 * It will miss documents per se. Use isds_get_received_message(), if you are
9337 * interested in documents (content) too.
9338 * Returned hash and timestamp require documents to be verifiable. */
9339 isds_error isds_get_received_envelope(struct isds_ctx *context,
9340 const char *message_id, struct isds_message **message) {
9342 isds_error err = IE_SUCCESS;
9343 #if HAVE_LIBCURL
9344 xmlDocPtr response = NULL;
9345 xmlChar *code = NULL, *status_message = NULL;
9346 xmlXPathContextPtr xpath_ctx = NULL;
9347 xmlXPathObjectPtr result = NULL;
9348 #endif
9350 if (!context) return IE_INVALID_CONTEXT;
9351 zfree(context->long_message);
9353 /* Free former message if any */
9354 if (!message) return IE_INVAL;
9355 isds_message_free(message);
9357 #if HAVE_LIBCURL
9358 /* Do request and check for success */
9359 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9360 BAD_CAST "MessageEnvelopeDownload", message_id,
9361 &response, NULL, NULL, &code, &status_message);
9362 if (err) goto leave;
9364 /* Extract data */
9365 xpath_ctx = xmlXPathNewContext(response);
9366 if (!xpath_ctx) {
9367 err = IE_ERROR;
9368 goto leave;
9370 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9371 err = IE_ERROR;
9372 goto leave;
9374 result = xmlXPathEvalExpression(
9375 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9376 "isds:dmReturnedMessageEnvelope",
9377 xpath_ctx);
9378 if (!result) {
9379 err = IE_ERROR;
9380 goto leave;
9382 /* Empty response */
9383 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9384 char *message_id_locale = _isds_utf82locale((char*) message_id);
9385 isds_printf_message(context,
9386 _("Server did not return any envelope for ID `%s' "
9387 "on MessageEnvelopeDownload request"), message_id_locale);
9388 free(message_id_locale);
9389 err = IE_ISDS;
9390 goto leave;
9392 /* More envelops */
9393 if (result->nodesetval->nodeNr > 1) {
9394 char *message_id_locale = _isds_utf82locale((char*) message_id);
9395 isds_printf_message(context,
9396 _("Server did return more envelopes for ID `%s' "
9397 "on MessageEnvelopeDownload request"), message_id_locale);
9398 free(message_id_locale);
9399 err = IE_ISDS;
9400 goto leave;
9402 /* One message */
9403 xpath_ctx->node = result->nodesetval->nodeTab[0];
9405 /* Extract the envelope (= message without documents, hence 0) */
9406 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9407 if (err) goto leave;
9409 /* Save XML blob */
9410 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9411 &(*message)->raw_length);
9413 leave:
9414 if (err) {
9415 isds_message_free(message);
9418 xmlXPathFreeObject(result);
9419 xmlXPathFreeContext(xpath_ctx);
9421 free(code);
9422 free(status_message);
9423 if (!*message || !(*message)->xml) {
9424 xmlFreeDoc(response);
9427 if (!err)
9428 isds_log(ILF_ISDS, ILL_DEBUG,
9429 _("MessageEnvelopeDownload request processed by server "
9430 "successfully.\n")
9432 #else /* not HAVE_LIBCURL */
9433 err = IE_NOTSUP;
9434 #endif
9435 return err;
9439 /* Load delivery info of any format from buffer.
9440 * @context is session context
9441 * @raw_type advertises format of @buffer content. Only delivery info types
9442 * are accepted.
9443 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9444 * retrieve such data from message->raw after calling
9445 * isds_get_signed_delivery_info().
9446 * @length is length of buffer in bytes.
9447 * @message is automatically reallocated message parsed from @buffer.
9448 * @strategy selects how buffer will be attached into raw isds_message member.
9449 * */
9450 isds_error isds_load_delivery_info(struct isds_ctx *context,
9451 const isds_raw_type raw_type,
9452 const void *buffer, const size_t length,
9453 struct isds_message **message, const isds_buffer_strategy strategy) {
9455 isds_error err = IE_SUCCESS;
9456 message_ns_type message_ns;
9457 xmlDocPtr message_doc = NULL;
9458 xmlXPathContextPtr xpath_ctx = NULL;
9459 xmlXPathObjectPtr result = NULL;
9460 void *xml_stream = NULL;
9461 size_t xml_stream_length = 0;
9463 if (!context) return IE_INVALID_CONTEXT;
9464 zfree(context->long_message);
9465 if (!message) return IE_INVAL;
9466 isds_message_free(message);
9467 if (!buffer) return IE_INVAL;
9470 /* Select buffer format and extract XML from CMS*/
9471 switch (raw_type) {
9472 case RAWTYPE_DELIVERYINFO:
9473 message_ns = MESSAGE_NS_UNSIGNED;
9474 xml_stream = (void *) buffer;
9475 xml_stream_length = length;
9476 break;
9478 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
9479 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9480 xml_stream = (void *) buffer;
9481 xml_stream_length = length;
9482 break;
9484 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
9485 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9486 err = _isds_extract_cms_data(context, buffer, length,
9487 &xml_stream, &xml_stream_length);
9488 if (err) goto leave;
9489 break;
9491 default:
9492 isds_log_message(context, _("Bad raw delivery representation type"));
9493 return IE_INVAL;
9494 break;
9497 isds_log(ILF_ISDS, ILL_DEBUG,
9498 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
9499 xml_stream_length, xml_stream);
9501 /* Convert delivery info XML stream into XPath context */
9502 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9503 if (!message_doc) {
9504 err = IE_XML;
9505 goto leave;
9507 xpath_ctx = xmlXPathNewContext(message_doc);
9508 if (!xpath_ctx) {
9509 err = IE_ERROR;
9510 goto leave;
9512 /* XXX: Name spaces mangled for signed delivery info:
9513 * http://isds.czechpoint.cz/v20/delivery:
9515 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
9516 * <q:dmDelivery>
9517 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9518 * <p:dmID>170272</p:dmID>
9519 * ...
9520 * </p:dmDm>
9521 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9522 * ...
9523 * </q:dmEvents>...</q:dmEvents>
9524 * </q:dmDelivery>
9525 * </q:GetDeliveryInfoResponse>
9526 * */
9527 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
9528 err = IE_ERROR;
9529 goto leave;
9531 result = xmlXPathEvalExpression(
9532 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
9533 xpath_ctx);
9534 if (!result) {
9535 err = IE_ERROR;
9536 goto leave;
9538 /* Empty delivery info */
9539 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9540 isds_printf_message(context,
9541 _("XML document is not sisds:dmDelivery document"));
9542 err = IE_ISDS;
9543 goto leave;
9545 /* More delivery info's */
9546 if (result->nodesetval->nodeNr > 1) {
9547 isds_printf_message(context,
9548 _("XML document has more sisds:dmDelivery elements"));
9549 err = IE_ISDS;
9550 goto leave;
9552 /* One delivery info */
9553 xpath_ctx->node = result->nodesetval->nodeTab[0];
9555 /* Extract the envelope (= message without documents, hence 0).
9556 * XXX: extract_TReturnedMessage() can obtain attachments size,
9557 * but delivery info carries none. It's coded as option elements,
9558 * so it should work. */
9559 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9560 if (err) goto leave;
9562 /* Extract events */
9563 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
9564 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
9565 if (err) { err = IE_ERROR; goto leave; }
9566 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
9567 if (err) goto leave;
9569 /* Append raw CMS structure into message */
9570 (*message)->raw_type = raw_type;
9571 switch (strategy) {
9572 case BUFFER_DONT_STORE:
9573 break;
9574 case BUFFER_COPY:
9575 (*message)->raw = malloc(length);
9576 if (!(*message)->raw) {
9577 err = IE_NOMEM;
9578 goto leave;
9580 memcpy((*message)->raw, buffer, length);
9581 (*message)->raw_length = length;
9582 break;
9583 case BUFFER_MOVE:
9584 (*message)->raw = (void *) buffer;
9585 (*message)->raw_length = length;
9586 break;
9587 default:
9588 err = IE_ENUM;
9589 goto leave;
9592 leave:
9593 if (err) {
9594 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
9595 isds_message_free(message);
9598 xmlXPathFreeObject(result);
9599 xmlXPathFreeContext(xpath_ctx);
9600 if (!*message || !(*message)->xml) {
9601 xmlFreeDoc(message_doc);
9603 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9605 if (!err)
9606 isds_log(ILF_ISDS, ILL_DEBUG,
9607 _("Delivery info loaded successfully.\n"));
9608 return err;
9612 /* Download signed delivery info-sheet of given message identified by ID.
9613 * @context is session context
9614 * @message_id is message identifier (you can get them from
9615 * isds_get_list_of_{sent,received}_messages())
9616 * @message is automatically reallocated message retrieved from ISDS.
9617 * It will miss documents per se. Use isds_get_signed_received_message(),
9618 * if you are interested in documents (content). OTOH, only this function
9619 * can get list events message has gone through. */
9620 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
9621 const char *message_id, struct isds_message **message) {
9623 isds_error err = IE_SUCCESS;
9624 #if HAVE_LIBCURL
9625 xmlDocPtr response = NULL;
9626 xmlChar *code = NULL, *status_message = NULL;
9627 void *raw = NULL;
9628 size_t raw_length = 0;
9629 #endif
9631 if (!context) return IE_INVALID_CONTEXT;
9632 zfree(context->long_message);
9634 /* Free former message if any */
9635 if (!message) return IE_INVAL;
9636 isds_message_free(message);
9638 #if HAVE_LIBCURL
9639 /* Do request and check for success */
9640 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9641 BAD_CAST "GetSignedDeliveryInfo", message_id,
9642 &response, NULL, NULL, &code, &status_message);
9643 if (err) goto leave;
9645 /* Find signed delivery info, extract it into raw and maybe free
9646 * response */
9647 err = find_extract_signed_data_free_response(context,
9648 (xmlChar *)message_id, &response,
9649 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
9650 if (err) goto leave;
9652 /* Parse delivery info */
9653 err = isds_load_delivery_info(context,
9654 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
9655 message, BUFFER_MOVE);
9656 if (err) goto leave;
9658 raw = NULL;
9660 leave:
9661 if (err) {
9662 isds_message_free(message);
9665 free(raw);
9666 free(code);
9667 free(status_message);
9668 xmlFreeDoc(response);
9670 if (!err)
9671 isds_log(ILF_ISDS, ILL_DEBUG,
9672 _("GetSignedDeliveryInfo request processed by server "
9673 "successfully.\n")
9675 #else /* not HAVE_LIBCURL */
9676 err = IE_NOTSUP;
9677 #endif
9678 return err;
9682 /* Download delivery info-sheet of given message identified by ID.
9683 * @context is session context
9684 * @message_id is message identifier (you can get them from
9685 * isds_get_list_of_{sent,received}_messages())
9686 * @message is automatically reallocated message retrieved from ISDS.
9687 * It will miss documents per se. Use isds_get_received_message(), if you are
9688 * interested in documents (content). OTOH, only this function can get list
9689 * of events message has gone through. */
9690 isds_error isds_get_delivery_info(struct isds_ctx *context,
9691 const char *message_id, struct isds_message **message) {
9693 isds_error err = IE_SUCCESS;
9694 #if HAVE_LIBCURL
9695 xmlDocPtr response = NULL;
9696 xmlChar *code = NULL, *status_message = NULL;
9697 xmlNodePtr delivery_node = NULL;
9698 void *raw = NULL;
9699 size_t raw_length = 0;
9700 #endif
9702 if (!context) return IE_INVALID_CONTEXT;
9703 zfree(context->long_message);
9705 /* Free former message if any */
9706 if (!message) return IE_INVAL;
9707 isds_message_free(message);
9709 #if HAVE_LIBCURL
9710 /* Do request and check for success */
9711 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9712 BAD_CAST "GetDeliveryInfo", message_id,
9713 &response, NULL, NULL, &code, &status_message);
9714 if (err) goto leave;
9717 /* Serialize delivery info */
9718 delivery_node = xmlDocGetRootElement(response);
9719 if (!delivery_node) {
9720 char *message_id_locale = _isds_utf82locale((char*) message_id);
9721 isds_printf_message(context,
9722 _("Server did not return any delivery info for ID `%s' "
9723 "on GetDeliveryInfo request"), message_id_locale);
9724 free(message_id_locale);
9725 err = IE_ISDS;
9726 goto leave;
9728 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
9729 if (err) goto leave;
9731 /* Parse delivery info */
9732 /* TODO: Here we parse the response second time. We could single delivery
9733 * parser from isds_load_delivery_info() to make things faster. */
9734 err = isds_load_delivery_info(context,
9735 RAWTYPE_DELIVERYINFO, raw, raw_length,
9736 message, BUFFER_MOVE);
9737 if (err) goto leave;
9739 raw = NULL;
9742 leave:
9743 if (err) {
9744 isds_message_free(message);
9747 free(raw);
9748 free(code);
9749 free(status_message);
9750 xmlFreeDoc(response);
9752 if (!err)
9753 isds_log(ILF_ISDS, ILL_DEBUG,
9754 _("GetDeliveryInfo request processed by server "
9755 "successfully.\n")
9757 #else /* not HAVE_LIBCURL */
9758 err = IE_NOTSUP;
9759 #endif
9760 return err;
9764 /* Download incoming message identified by ID.
9765 * @context is session context
9766 * @message_id is message identifier (you can get them from
9767 * isds_get_list_of_received_messages())
9768 * @message is automatically reallocated message retrieved from ISDS */
9769 isds_error isds_get_received_message(struct isds_ctx *context,
9770 const char *message_id, struct isds_message **message) {
9772 isds_error err = IE_SUCCESS;
9773 #if HAVE_LIBCURL
9774 xmlDocPtr response = NULL;
9775 void *xml_stream = NULL;
9776 size_t xml_stream_length;
9777 xmlChar *code = NULL, *status_message = NULL;
9778 xmlXPathContextPtr xpath_ctx = NULL;
9779 xmlXPathObjectPtr result = NULL;
9780 char *phys_path = NULL;
9781 size_t phys_start, phys_end;
9782 #endif
9784 if (!context) return IE_INVALID_CONTEXT;
9785 zfree(context->long_message);
9787 /* Free former message if any */
9788 if (message) isds_message_free(message);
9790 #if HAVE_LIBCURL
9791 /* Do request and check for success */
9792 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
9793 BAD_CAST "MessageDownload", message_id,
9794 &response, &xml_stream, &xml_stream_length,
9795 &code, &status_message);
9796 if (err) goto leave;
9798 /* Extract data */
9799 xpath_ctx = xmlXPathNewContext(response);
9800 if (!xpath_ctx) {
9801 err = IE_ERROR;
9802 goto leave;
9804 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9805 err = IE_ERROR;
9806 goto leave;
9808 result = xmlXPathEvalExpression(
9809 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
9810 xpath_ctx);
9811 if (!result) {
9812 err = IE_ERROR;
9813 goto leave;
9815 /* Empty response */
9816 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9817 char *message_id_locale = _isds_utf82locale((char*) message_id);
9818 isds_printf_message(context,
9819 _("Server did not return any message for ID `%s' "
9820 "on MessageDownload request"), message_id_locale);
9821 free(message_id_locale);
9822 err = IE_ISDS;
9823 goto leave;
9825 /* More messages */
9826 if (result->nodesetval->nodeNr > 1) {
9827 char *message_id_locale = _isds_utf82locale((char*) message_id);
9828 isds_printf_message(context,
9829 _("Server did return more messages for ID `%s' "
9830 "on MessageDownload request"), message_id_locale);
9831 free(message_id_locale);
9832 err = IE_ISDS;
9833 goto leave;
9835 /* One message */
9836 xpath_ctx->node = result->nodesetval->nodeTab[0];
9838 /* Extract the message */
9839 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
9840 if (err) goto leave;
9842 /* Locate raw XML blob */
9843 phys_path = strdup(
9844 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
9845 PHYSXML_ELEMENT_SEPARATOR
9846 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
9847 PHYSXML_ELEMENT_SEPARATOR
9848 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
9850 if (!phys_path) {
9851 err = IE_NOMEM;
9852 goto leave;
9854 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
9855 phys_path, &phys_start, &phys_end);
9856 zfree(phys_path);
9857 if (err) {
9858 isds_log_message(context,
9859 _("Substring with isds:MessageDownloadResponse element "
9860 "could not be located in raw SOAP message"));
9861 goto leave;
9863 /* Save XML blob */
9864 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9865 &(*message)->raw_length);*/
9866 /* TODO: Store name space declarations from ancestors */
9867 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
9868 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
9869 (*message)->raw_length = phys_end - phys_start + 1;
9870 (*message)->raw = malloc((*message)->raw_length);
9871 if (!(*message)->raw) {
9872 err = IE_NOMEM;
9873 goto leave;
9875 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
9878 leave:
9879 if (err) {
9880 isds_message_free(message);
9883 free(phys_path);
9885 xmlXPathFreeObject(result);
9886 xmlXPathFreeContext(xpath_ctx);
9888 free(code);
9889 free(status_message);
9890 free(xml_stream);
9891 if (!*message || !(*message)->xml) {
9892 xmlFreeDoc(response);
9895 if (!err)
9896 isds_log(ILF_ISDS, ILL_DEBUG,
9897 _("MessageDownload request processed by server "
9898 "successfully.\n")
9900 #else /* not HAVE_LIBCURL */
9901 err = IE_NOTSUP;
9902 #endif
9903 return err;
9907 /* Load message of any type from buffer.
9908 * @context is session context
9909 * @raw_type defines content type of @buffer. Only message types are allowed.
9910 * @buffer is message raw representation. Format (CMS, plain signed,
9911 * message direction) is defined in @raw_type. You can retrieve such data
9912 * from message->raw after calling isds_get_[signed]{received,sent}_message().
9913 * @length is length of buffer in bytes.
9914 * @message is automatically reallocated message parsed from @buffer.
9915 * @strategy selects how buffer will be attached into raw isds_message member.
9916 * */
9917 isds_error isds_load_message(struct isds_ctx *context,
9918 const isds_raw_type raw_type, const void *buffer, const size_t length,
9919 struct isds_message **message, const isds_buffer_strategy strategy) {
9921 isds_error err = IE_SUCCESS;
9922 void *xml_stream = NULL;
9923 size_t xml_stream_length = 0;
9924 message_ns_type message_ns;
9925 xmlDocPtr message_doc = NULL;
9926 xmlXPathContextPtr xpath_ctx = NULL;
9927 xmlXPathObjectPtr result = NULL;
9929 if (!context) return IE_INVALID_CONTEXT;
9930 zfree(context->long_message);
9931 if (!message) return IE_INVAL;
9932 isds_message_free(message);
9933 if (!buffer) return IE_INVAL;
9936 /* Select buffer format and extract XML from CMS*/
9937 switch (raw_type) {
9938 case RAWTYPE_INCOMING_MESSAGE:
9939 message_ns = MESSAGE_NS_UNSIGNED;
9940 xml_stream = (void *) buffer;
9941 xml_stream_length = length;
9942 break;
9944 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
9945 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9946 xml_stream = (void *) buffer;
9947 xml_stream_length = length;
9948 break;
9950 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
9951 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9952 err = _isds_extract_cms_data(context, buffer, length,
9953 &xml_stream, &xml_stream_length);
9954 if (err) goto leave;
9955 break;
9957 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
9958 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9959 xml_stream = (void *) buffer;
9960 xml_stream_length = length;
9961 break;
9963 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
9964 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9965 err = _isds_extract_cms_data(context, buffer, length,
9966 &xml_stream, &xml_stream_length);
9967 if (err) goto leave;
9968 break;
9970 default:
9971 isds_log_message(context, _("Bad raw message representation type"));
9972 return IE_INVAL;
9973 break;
9976 isds_log(ILF_ISDS, ILL_DEBUG,
9977 _("Loading message:\n%.*s\nEnd of message\n"),
9978 xml_stream_length, xml_stream);
9980 /* Convert messages XML stream into XPath context */
9981 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9982 if (!message_doc) {
9983 err = IE_XML;
9984 goto leave;
9986 xpath_ctx = xmlXPathNewContext(message_doc);
9987 if (!xpath_ctx) {
9988 err = IE_ERROR;
9989 goto leave;
9991 /* XXX: Standard name space for unsigned incoming direction:
9992 * http://isds.czechpoint.cz/v20/
9994 * XXX: Name spaces mangled for signed outgoing direction:
9995 * http://isds.czechpoint.cz/v20/SentMessage:
9997 * <q:MessageDownloadResponse
9998 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
9999 * <q:dmReturnedMessage>
10000 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10001 * <p:dmID>151916</p:dmID>
10002 * ...
10003 * </p:dmDm>
10004 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10005 * ...
10006 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10007 * </q:dmReturnedMessage>
10008 * </q:MessageDownloadResponse>
10010 * XXX: Name spaces mangled for signed incoming direction:
10011 * http://isds.czechpoint.cz/v20/message:
10013 * <q:MessageDownloadResponse
10014 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10015 * <q:dmReturnedMessage>
10016 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10017 * <p:dmID>151916</p:dmID>
10018 * ...
10019 * </p:dmDm>
10020 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10021 * ...
10022 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10023 * </q:dmReturnedMessage>
10024 * </q:MessageDownloadResponse>
10026 * Stupidity of ISDS developers is unlimited */
10027 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10028 err = IE_ERROR;
10029 goto leave;
10031 result = xmlXPathEvalExpression(
10032 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10033 xpath_ctx);
10034 if (!result) {
10035 err = IE_ERROR;
10036 goto leave;
10038 /* Empty message */
10039 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10040 isds_printf_message(context,
10041 _("XML document does not contain "
10042 "sisds:dmReturnedMessage element"));
10043 err = IE_ISDS;
10044 goto leave;
10046 /* More messages */
10047 if (result->nodesetval->nodeNr > 1) {
10048 isds_printf_message(context,
10049 _("XML document has more sisds:dmReturnedMessage elements"));
10050 err = IE_ISDS;
10051 goto leave;
10053 /* One message */
10054 xpath_ctx->node = result->nodesetval->nodeTab[0];
10056 /* Extract the message */
10057 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10058 if (err) goto leave;
10060 /* Append raw buffer into message */
10061 (*message)->raw_type = raw_type;
10062 switch (strategy) {
10063 case BUFFER_DONT_STORE:
10064 break;
10065 case BUFFER_COPY:
10066 (*message)->raw = malloc(length);
10067 if (!(*message)->raw) {
10068 err = IE_NOMEM;
10069 goto leave;
10071 memcpy((*message)->raw, buffer, length);
10072 (*message)->raw_length = length;
10073 break;
10074 case BUFFER_MOVE:
10075 (*message)->raw = (void *) buffer;
10076 (*message)->raw_length = length;
10077 break;
10078 default:
10079 err = IE_ENUM;
10080 goto leave;
10084 leave:
10085 if (err) {
10086 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10087 isds_message_free(message);
10090 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10091 xmlXPathFreeObject(result);
10092 xmlXPathFreeContext(xpath_ctx);
10093 if (!*message || !(*message)->xml) {
10094 xmlFreeDoc(message_doc);
10097 if (!err)
10098 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10099 return err;
10103 /* Determine type of raw message or delivery info according some heuristics.
10104 * It does not validate the raw blob.
10105 * @context is session context
10106 * @raw_type returns content type of @buffer. Valid only if exit code of this
10107 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10108 * reallocated memory.
10109 * @buffer is message raw representation.
10110 * @length is length of buffer in bytes. */
10111 isds_error isds_guess_raw_type(struct isds_ctx *context,
10112 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10113 isds_error err;
10114 void *xml_stream = NULL;
10115 size_t xml_stream_length = 0;
10116 xmlDocPtr document = NULL;
10117 xmlNodePtr root = NULL;
10119 if (!context) return IE_INVALID_CONTEXT;
10120 zfree(context->long_message);
10121 if (length == 0 || !buffer) return IE_INVAL;
10122 if (!raw_type) return IE_INVAL;
10124 /* Try CMS */
10125 err = _isds_extract_cms_data(context, buffer, length,
10126 &xml_stream, &xml_stream_length);
10127 if (err) {
10128 xml_stream = (void *) buffer;
10129 xml_stream_length = (size_t) length;
10130 err = IE_SUCCESS;
10133 /* Try XML */
10134 document = xmlParseMemory(xml_stream, xml_stream_length);
10135 if (!document) {
10136 isds_printf_message(context,
10137 _("Could not parse data as XML document"));
10138 err = IE_NOTSUP;
10139 goto leave;
10142 /* Get root element */
10143 root = xmlDocGetRootElement(document);
10144 if (!root) {
10145 isds_printf_message(context,
10146 _("XML document is missing root element"));
10147 err = IE_XML;
10148 goto leave;
10151 if (!root->ns || !root->ns->href) {
10152 isds_printf_message(context,
10153 _("Root element does not belong to any name space"));
10154 err = IE_NOTSUP;
10155 goto leave;
10158 /* Test name space */
10159 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10160 if (xml_stream == buffer)
10161 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10162 else
10163 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10164 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10165 if (xml_stream == buffer)
10166 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10167 else
10168 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10169 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10170 if (xml_stream == buffer)
10171 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10172 else
10173 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10174 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10175 if (xml_stream != buffer) {
10176 isds_printf_message(context,
10177 _("Document in ISDS name space is encapsulated into CMS" ));
10178 err = IE_NOTSUP;
10179 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10180 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10181 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10182 *raw_type = RAWTYPE_DELIVERYINFO;
10183 else {
10184 isds_printf_message(context,
10185 _("Unknown root element in ISDS name space"));
10186 err = IE_NOTSUP;
10188 } else {
10189 isds_printf_message(context,
10190 _("Unknown name space"));
10191 err = IE_NOTSUP;
10194 leave:
10195 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10196 xmlFreeDoc(document);
10197 return err;
10201 /* Download signed incoming/outgoing message identified by ID.
10202 * @context is session context
10203 * @output is true for outgoing message, false for incoming message
10204 * @message_id is message identifier (you can get them from
10205 * isds_get_list_of_{sent,received}_messages())
10206 * @message is automatically reallocated message retrieved from ISDS. The raw
10207 * member will be filled with PKCS#7 structure in DER format. */
10208 static isds_error isds_get_signed_message(struct isds_ctx *context,
10209 const _Bool outgoing, const char *message_id,
10210 struct isds_message **message) {
10212 isds_error err = IE_SUCCESS;
10213 #if HAVE_LIBCURL
10214 xmlDocPtr response = NULL;
10215 xmlChar *code = NULL, *status_message = NULL;
10216 xmlXPathContextPtr xpath_ctx = NULL;
10217 xmlXPathObjectPtr result = NULL;
10218 char *encoded_structure = NULL;
10219 void *raw = NULL;
10220 size_t raw_length = 0;
10221 #endif
10223 if (!context) return IE_INVALID_CONTEXT;
10224 zfree(context->long_message);
10225 if (!message) return IE_INVAL;
10226 isds_message_free(message);
10228 #if HAVE_LIBCURL
10229 /* Do request and check for success */
10230 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10231 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10232 BAD_CAST "SignedMessageDownload",
10233 message_id, &response, NULL, NULL, &code, &status_message);
10234 if (err) goto leave;
10236 /* Find signed message, extract it into raw and maybe free
10237 * response */
10238 err = find_extract_signed_data_free_response(context,
10239 (xmlChar *)message_id, &response,
10240 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10241 BAD_CAST "SignedMessageDownload",
10242 &raw, &raw_length);
10243 if (err) goto leave;
10245 /* Parse message */
10246 err = isds_load_message(context,
10247 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10248 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10249 raw, raw_length, message, BUFFER_MOVE);
10250 if (err) goto leave;
10252 raw = NULL;
10254 leave:
10255 if (err) {
10256 isds_message_free(message);
10259 free(encoded_structure);
10260 xmlXPathFreeObject(result);
10261 xmlXPathFreeContext(xpath_ctx);
10262 free(raw);
10264 free(code);
10265 free(status_message);
10266 xmlFreeDoc(response);
10268 if (!err)
10269 isds_log(ILF_ISDS, ILL_DEBUG,
10270 (outgoing) ?
10271 _("SignedSentMessageDownload request processed by server "
10272 "successfully.\n") :
10273 _("SignedMessageDownload request processed by server "
10274 "successfully.\n")
10276 #else /* not HAVE_LIBCURL */
10277 err = IE_NOTSUP;
10278 #endif
10279 return err;
10283 /* Download signed incoming message identified by ID.
10284 * @context is session context
10285 * @message_id is message identifier (you can get them from
10286 * isds_get_list_of_received_messages())
10287 * @message is automatically reallocated message retrieved from ISDS. The raw
10288 * member will be filled with PKCS#7 structure in DER format. */
10289 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10290 const char *message_id, struct isds_message **message) {
10291 return isds_get_signed_message(context, 0, message_id, message);
10295 /* Download signed outgoing message identified by ID.
10296 * @context is session context
10297 * @message_id is message identifier (you can get them from
10298 * isds_get_list_of_sent_messages())
10299 * @message is automatically reallocated message retrieved from ISDS. The raw
10300 * member will be filled with PKCS#7 structure in DER format. */
10301 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10302 const char *message_id, struct isds_message **message) {
10303 return isds_get_signed_message(context, 1, message_id, message);
10307 /* Get type and name of user who sent a message identified by ID.
10308 * @context is session context
10309 * @message_id is message identifier
10310 * @sender_type is pointer to automatically allocated type of sender detected
10311 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10312 * library or to the server, NULL will be returned. Pass NULL if you don't
10313 * care about it.
10314 * @raw_sender_type is automatically reallocated UTF-8 string describing
10315 * sender type or NULL if not known to server. Pass NULL if you don't care.
10316 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10317 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10318 isds_error isds_get_message_sender(struct isds_ctx *context,
10319 const char *message_id, isds_sender_type **sender_type,
10320 char **raw_sender_type, char **sender_name) {
10321 isds_error err = IE_SUCCESS;
10322 #if HAVE_LIBCURL
10323 xmlDocPtr response = NULL;
10324 xmlChar *code = NULL, *status_message = NULL;
10325 xmlXPathContextPtr xpath_ctx = NULL;
10326 xmlXPathObjectPtr result = NULL;
10327 char *type_string = NULL;
10328 #endif
10330 if (!context) return IE_INVALID_CONTEXT;
10331 zfree(context->long_message);
10332 if (sender_type) zfree(*sender_type);
10333 if (raw_sender_type) zfree(*raw_sender_type);
10334 if (sender_name) zfree(*sender_name);
10335 if (!message_id) return IE_INVAL;
10337 #if HAVE_LIBCURL
10338 /* Do request and check for success */
10339 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10340 BAD_CAST "GetMessageAuthor",
10341 message_id, &response, NULL, NULL, &code, &status_message);
10342 if (err) goto leave;
10344 /* Extract data */
10345 xpath_ctx = xmlXPathNewContext(response);
10346 if (!xpath_ctx) {
10347 err = IE_ERROR;
10348 goto leave;
10350 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10351 err = IE_ERROR;
10352 goto leave;
10354 result = xmlXPathEvalExpression(
10355 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10356 if (!result) {
10357 err = IE_ERROR;
10358 goto leave;
10360 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10361 isds_log_message(context,
10362 _("Missing GetMessageAuthorResponse element"));
10363 err = IE_ISDS;
10364 goto leave;
10366 if (result->nodesetval->nodeNr > 1) {
10367 isds_log_message(context,
10368 _("Multiple GetMessageAuthorResponse element"));
10369 err = IE_ISDS;
10370 goto leave;
10372 xpath_ctx->node = result->nodesetval->nodeTab[0];
10373 xmlXPathFreeObject(result); result = NULL;
10375 /* Fill output arguments in */
10376 EXTRACT_STRING("isds:userType", type_string);
10377 if (type_string) {
10378 *sender_type = calloc(1, sizeof(**sender_type));
10379 if (!*sender_type) {
10380 err = IE_NOMEM;
10381 goto leave;
10384 if (sender_type) {
10385 err = string2isds_sender_type((xmlChar *)type_string,
10386 *sender_type);
10387 if (err) {
10388 zfree(*sender_type);
10389 if (err == IE_ENUM) {
10390 err = IE_SUCCESS;
10391 char *type_string_locale = _isds_utf82locale(type_string);
10392 isds_log(ILF_ISDS, ILL_WARNING,
10393 _("Unknown isds:userType value: %s"),
10394 type_string_locale);
10395 free(type_string_locale);
10400 if (sender_type)
10401 EXTRACT_STRING("isds:authorName", *sender_name);
10403 leave:
10404 if (err) {
10405 if (sender_type) zfree(*sender_type);
10406 zfree(type_string);
10407 if (sender_name) zfree(*sender_name);
10409 if (raw_sender_type) *raw_sender_type = type_string;
10411 xmlXPathFreeObject(result);
10412 xmlXPathFreeContext(xpath_ctx);
10414 free(code);
10415 free(status_message);
10416 xmlFreeDoc(response);
10418 if (!err)
10419 isds_log(ILF_ISDS, ILL_DEBUG,
10420 _("GetMessageAuthor request processed by server "
10421 "successfully.\n"));
10422 #else /* not HAVE_LIBCURL */
10423 err = IE_NOTSUP;
10424 #endif
10425 return err;
10429 /* Retrieve hash of message identified by ID stored in ISDS.
10430 * @context is session context
10431 * @message_id is message identifier
10432 * @hash is automatically reallocated message hash downloaded from ISDS.
10433 * Message must exist in system and must not be deleted. */
10434 isds_error isds_download_message_hash(struct isds_ctx *context,
10435 const char *message_id, struct isds_hash **hash) {
10437 isds_error err = IE_SUCCESS;
10438 #if HAVE_LIBCURL
10439 xmlDocPtr response = NULL;
10440 xmlChar *code = NULL, *status_message = NULL;
10441 xmlXPathContextPtr xpath_ctx = NULL;
10442 xmlXPathObjectPtr result = NULL;
10443 #endif
10445 if (!context) return IE_INVALID_CONTEXT;
10446 zfree(context->long_message);
10448 isds_hash_free(hash);
10450 #if HAVE_LIBCURL
10451 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10452 BAD_CAST "VerifyMessage", message_id,
10453 &response, NULL, NULL, &code, &status_message);
10454 if (err) goto leave;
10457 /* Extract data */
10458 xpath_ctx = xmlXPathNewContext(response);
10459 if (!xpath_ctx) {
10460 err = IE_ERROR;
10461 goto leave;
10463 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10464 err = IE_ERROR;
10465 goto leave;
10467 result = xmlXPathEvalExpression(
10468 BAD_CAST "/isds:VerifyMessageResponse",
10469 xpath_ctx);
10470 if (!result) {
10471 err = IE_ERROR;
10472 goto leave;
10474 /* Empty response */
10475 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10476 char *message_id_locale = _isds_utf82locale((char*) message_id);
10477 isds_printf_message(context,
10478 _("Server did not return any response for ID `%s' "
10479 "on VerifyMessage request"), message_id_locale);
10480 free(message_id_locale);
10481 err = IE_ISDS;
10482 goto leave;
10484 /* More responses */
10485 if (result->nodesetval->nodeNr > 1) {
10486 char *message_id_locale = _isds_utf82locale((char*) message_id);
10487 isds_printf_message(context,
10488 _("Server did return more responses for ID `%s' "
10489 "on VerifyMessage request"), message_id_locale);
10490 free(message_id_locale);
10491 err = IE_ISDS;
10492 goto leave;
10494 /* One response */
10495 xpath_ctx->node = result->nodesetval->nodeTab[0];
10497 /* Extract the hash */
10498 err = find_and_extract_DmHash(context, hash, xpath_ctx);
10500 leave:
10501 if (err) {
10502 isds_hash_free(hash);
10505 xmlXPathFreeObject(result);
10506 xmlXPathFreeContext(xpath_ctx);
10508 free(code);
10509 free(status_message);
10510 xmlFreeDoc(response);
10512 if (!err)
10513 isds_log(ILF_ISDS, ILL_DEBUG,
10514 _("VerifyMessage request processed by server "
10515 "successfully.\n")
10517 #else /* not HAVE_LIBCURL */
10518 err = IE_NOTSUP;
10519 #endif
10520 return err;
10524 /* Erase message specified by @message_id from long term storage. Other
10525 * message cannot be erased on user request.
10526 * @context is session context
10527 * @message_id is message identifier.
10528 * @incoming is true for incoming message, false for outgoing message.
10529 * @return
10530 * IE_SUCCESS if message has ben removed
10531 * IE_INVAL if message does not exist in long term storage or message
10532 * belongs to different box
10533 * TODO: IE_NOEPRM if user has no permission to erase a message */
10534 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
10535 const char *message_id, _Bool incoming) {
10536 isds_error err = IE_SUCCESS;
10537 #if HAVE_LIBCURL
10538 xmlNodePtr request = NULL, node;
10539 xmlNsPtr isds_ns = NULL;
10540 xmlDocPtr response = NULL;
10541 xmlChar *code = NULL, *status_message = NULL;
10542 #endif
10544 if (!context) return IE_INVALID_CONTEXT;
10545 zfree(context->long_message);
10546 if (NULL == message_id) return IE_INVAL;
10548 /* Check if connection is established
10549 * TODO: This check should be done downstairs. */
10550 if (!context->curl) return IE_CONNECTION_CLOSED;
10552 #if HAVE_LIBCURL
10553 /* Build request */
10554 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
10555 if (!request) {
10556 isds_log_message(context,
10557 _("Could build EraseMessage request"));
10558 return IE_ERROR;
10560 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10561 if(!isds_ns) {
10562 isds_log_message(context, _("Could not create ISDS name space"));
10563 xmlFreeNode(request);
10564 return IE_ERROR;
10566 xmlSetNs(request, isds_ns);
10568 err = validate_message_id_length(context, (xmlChar *) message_id);
10569 if (err) goto leave;
10570 INSERT_STRING(request, "dmID", message_id);
10572 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
10575 /* Send request */
10576 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
10577 "message ID %s to ISDS\n"), message_id);
10578 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
10579 xmlFreeNode(request); request = NULL;
10581 if (err) {
10582 isds_log(ILF_ISDS, ILL_DEBUG,
10583 _("Processing ISDS response on EraseMessage request "
10584 "failed\n"));
10585 goto leave;
10588 /* Check for response status */
10589 err = isds_response_status(context, SERVICE_DM_INFO, response,
10590 &code, &status_message, NULL);
10591 if (err) {
10592 isds_log(ILF_ISDS, ILL_DEBUG,
10593 _("ISDS response on EraseMessage request is missing "
10594 "status\n"));
10595 goto leave;
10598 /* Check server status code */
10599 if (!xmlStrcmp(code, BAD_CAST "1211")) {
10600 isds_log_message(context, _("Message to erase belongs to other box"));
10601 err = IE_INVAL;
10602 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
10603 isds_log_message(context, _("Message to erase is not saved in "
10604 "long term storage or the direction does not match"));
10605 err = IE_INVAL;
10606 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
10607 char *code_locale = _isds_utf82locale((char*) code);
10608 char *message_locale = _isds_utf82locale((char*) status_message);
10609 isds_log(ILF_ISDS, ILL_DEBUG,
10610 _("Server refused EraseMessage request "
10611 "(code=%s, message=%s)\n"),
10612 code_locale, message_locale);
10613 isds_log_message(context, message_locale);
10614 free(code_locale);
10615 free(message_locale);
10616 err = IE_ISDS;
10617 goto leave;
10620 leave:
10621 free(code);
10622 free(status_message);
10623 xmlFreeDoc(response);
10624 xmlFreeNode(request);
10626 if (!err)
10627 isds_log(ILF_ISDS, ILL_DEBUG,
10628 _("EraseMessage request processed by server "
10629 "successfully.\n")
10631 #else /* not HAVE_LIBCURL */
10632 err = IE_NOTSUP;
10633 #endif
10634 return err;
10638 /* Mark message as read. This is a transactional commit function to acknowledge
10639 * to ISDS the message has been downloaded and processed by client properly.
10640 * @context is session context
10641 * @message_id is message identifier. */
10642 isds_error isds_mark_message_read(struct isds_ctx *context,
10643 const char *message_id) {
10645 isds_error err = IE_SUCCESS;
10646 #if HAVE_LIBCURL
10647 xmlDocPtr response = NULL;
10648 xmlChar *code = NULL, *status_message = NULL;
10649 #endif
10651 if (!context) return IE_INVALID_CONTEXT;
10652 zfree(context->long_message);
10654 #if HAVE_LIBCURL
10655 /* Do request and check for success */
10656 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10657 BAD_CAST "MarkMessageAsDownloaded", message_id,
10658 &response, NULL, NULL, &code, &status_message);
10660 free(code);
10661 free(status_message);
10662 xmlFreeDoc(response);
10664 if (!err)
10665 isds_log(ILF_ISDS, ILL_DEBUG,
10666 _("MarkMessageAsDownloaded request processed by server "
10667 "successfully.\n")
10669 #else /* not HAVE_LIBCURL */
10670 err = IE_NOTSUP;
10671 #endif
10672 return err;
10676 /* Mark message as received by recipient. This is applicable only to
10677 * commercial message. Use envelope->dmType message member to distinguish
10678 * commercial message from government message. Government message is
10679 * received automatically (by law), commercial message on recipient request.
10680 * @context is session context
10681 * @message_id is message identifier. */
10682 isds_error isds_mark_message_received(struct isds_ctx *context,
10683 const char *message_id) {
10685 isds_error err = IE_SUCCESS;
10686 #if HAVE_LIBCURL
10687 xmlDocPtr response = NULL;
10688 xmlChar *code = NULL, *status_message = NULL;
10689 #endif
10691 if (!context) return IE_INVALID_CONTEXT;
10692 zfree(context->long_message);
10694 #if HAVE_LIBCURL
10695 /* Do request and check for success */
10696 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10697 BAD_CAST "ConfirmDelivery", message_id,
10698 &response, NULL, NULL, &code, &status_message);
10700 free(code);
10701 free(status_message);
10702 xmlFreeDoc(response);
10704 if (!err)
10705 isds_log(ILF_ISDS, ILL_DEBUG,
10706 _("ConfirmDelivery request processed by server "
10707 "successfully.\n")
10709 #else /* not HAVE_LIBCURL */
10710 err = IE_NOTSUP;
10711 #endif
10712 return err;
10716 /* Send document for authorized conversion into Czech POINT system.
10717 * This is public anonymous service, no log-in necessary. Special context is
10718 * used to reuse keep-a-live HTTPS connection.
10719 * @context is Czech POINT session context. DO NOT use context connected to
10720 * ISDS server. Use new context or context used by this function previously.
10721 * @document is document to convert. Only data, data_length, dmFileDescr and
10722 * is_xml members are significant. Be ware that not all document formats can be
10723 * converted (signed PDF 1.3 and higher only (2010-02 state)).
10724 * @id is reallocated identifier assigned by Czech POINT system to
10725 * your document on submit. Use is to tell it to Czech POINT officer.
10726 * @date is reallocated document submit date (submitted documents
10727 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
10728 * value. */
10729 isds_error czp_convert_document(struct isds_ctx *context,
10730 const struct isds_document *document,
10731 char **id, struct tm **date) {
10732 isds_error err = IE_SUCCESS;
10733 #if HAVE_LIBCURL
10734 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
10735 xmlNodePtr request = NULL, node;
10736 xmlDocPtr response = NULL;
10738 xmlXPathContextPtr xpath_ctx = NULL;
10739 xmlXPathObjectPtr result = NULL;
10740 long int status = -1;
10741 long int *status_ptr = &status;
10742 char *string = NULL;
10743 #endif
10746 if (!context) return IE_INVALID_CONTEXT;
10747 zfree(context->long_message);
10748 if (!document || !id || !date) return IE_INVAL;
10750 if (document->is_xml) {
10751 isds_log_message(context,
10752 _("XML documents cannot be submitted to conversion"));
10753 return IE_NOTSUP;
10756 /* Free output arguments */
10757 zfree(*id);
10758 zfree(*date);
10760 #if HAVE_LIBCURL
10761 /* Store configuration */
10762 context->type = CTX_TYPE_CZP;
10763 free(context->url);
10764 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
10765 if (!(context->url))
10766 return IE_NOMEM;
10768 /* Prepare CURL handle if not yet connected */
10769 if (!context->curl) {
10770 context->curl = curl_easy_init();
10771 if (!(context->curl))
10772 return IE_ERROR;
10775 /* Build conversion request */
10776 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
10777 if (!request) {
10778 isds_log_message(context,
10779 _("Could not build Czech POINT conversion request"));
10780 return IE_ERROR;
10782 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
10783 if(!deposit_ns) {
10784 isds_log_message(context,
10785 _("Could not create Czech POINT deposit name space"));
10786 xmlFreeNode(request);
10787 return IE_ERROR;
10789 xmlSetNs(request, deposit_ns);
10791 /* Insert children. They are in empty namespace! */
10792 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
10793 if(!empty_ns) {
10794 isds_log_message(context, _("Could not create empty name space"));
10795 err = IE_ERROR;
10796 goto leave;
10798 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
10799 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
10800 document->dmFileDescr);
10802 /* Document encoded in Base64 */
10803 err = insert_base64_encoded_string(context, request, empty_ns, "document",
10804 document->data, document->data_length);
10805 if (err) goto leave;
10807 isds_log(ILF_ISDS, ILL_DEBUG,
10808 _("Submitting document for conversion into Czech POINT deposit"));
10810 /* Send conversion request */
10811 err = _czp_czpdeposit(context, request, &response);
10812 xmlFreeNode(request); request = NULL;
10814 if (err) {
10815 czp_do_close_connection(context);
10816 goto leave;
10820 /* Extract response */
10821 xpath_ctx = xmlXPathNewContext(response);
10822 if (!xpath_ctx) {
10823 err = IE_ERROR;
10824 goto leave;
10826 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10827 err = IE_ERROR;
10828 goto leave;
10830 result = xmlXPathEvalExpression(
10831 BAD_CAST "/deposit:saveDocumentResponse/return",
10832 xpath_ctx);
10833 if (!result) {
10834 err = IE_ERROR;
10835 goto leave;
10837 /* Empty response */
10838 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10839 isds_printf_message(context,
10840 _("Missing `return' element in Czech POINT deposit response"));
10841 err = IE_ISDS;
10842 goto leave;
10844 /* More responses */
10845 if (result->nodesetval->nodeNr > 1) {
10846 isds_printf_message(context,
10847 _("Multiple `return' element in Czech POINT deposit response"));
10848 err = IE_ISDS;
10849 goto leave;
10851 /* One response */
10852 xpath_ctx->node = result->nodesetval->nodeTab[0];
10854 /* Get status */
10855 EXTRACT_LONGINT("status", status_ptr, 1);
10856 if (status) {
10857 EXTRACT_STRING("statusMsg", string);
10858 char *string_locale = _isds_utf82locale(string);
10859 isds_printf_message(context,
10860 _("Czech POINT deposit refused document for conversion "
10861 "(code=%ld, message=%s)"),
10862 status, string_locale);
10863 free(string_locale);
10864 err = IE_ISDS;
10865 goto leave;
10868 /* Get document ID */
10869 EXTRACT_STRING("documentID", *id);
10871 /* Get submit date */
10872 EXTRACT_STRING("dateInserted", string);
10873 if (string) {
10874 *date = calloc(1, sizeof(**date));
10875 if (!*date) {
10876 err = IE_NOMEM;
10877 goto leave;
10879 err = _isds_datestring2tm((xmlChar *)string, *date);
10880 if (err) {
10881 if (err == IE_NOTSUP) {
10882 err = IE_ISDS;
10883 char *string_locale = _isds_utf82locale(string);
10884 isds_printf_message(context,
10885 _("Invalid dateInserted value: %s"), string_locale);
10886 free(string_locale);
10888 goto leave;
10892 leave:
10893 free(string);
10894 xmlXPathFreeObject(result);
10895 xmlXPathFreeContext(xpath_ctx);
10897 xmlFreeDoc(response);
10898 xmlFreeNode(request);
10900 if (!err) {
10901 char *id_locale = _isds_utf82locale((char *) *id);
10902 isds_log(ILF_ISDS, ILL_DEBUG,
10903 _("Document %s has been submitted for conversion "
10904 "to server successfully\n"), id_locale);
10905 free(id_locale);
10907 #else /* not HAVE_LIBCURL */
10908 err = IE_NOTSUP;
10909 #endif
10910 return err;
10914 /* Close possibly opened connection to Czech POINT document deposit.
10915 * @context is Czech POINT session context. */
10916 isds_error czp_close_connection(struct isds_ctx *context) {
10917 if (!context) return IE_INVALID_CONTEXT;
10918 zfree(context->long_message);
10919 #if HAVE_LIBCURL
10920 return czp_do_close_connection(context);
10921 #else
10922 return IE_NOTSUP;
10923 #endif
10927 /* Send request for new box creation in testing ISDS instance.
10928 * It's not possible to request for a production box currently, as it
10929 * communicates via e-mail.
10930 * XXX: This function does not work either. Server complains about invalid
10931 * e-mail address.
10932 * XXX: Remove context->type hacks in isds.c and validator.c when removing
10933 * this function
10934 * @context is special session context for box creation request. DO NOT use
10935 * standard context as it could reveal your password. Use fresh new context or
10936 * context previously used by this function.
10937 * @box is box description to create including single primary user (in case of
10938 * FO box type). It outputs box ID assigned by ISDS in dbID element.
10939 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
10940 * box, or contact address of PFO box owner). The email member is mandatory as
10941 * it will be used to deliver credentials.
10942 * @former_names is former name of box owner. Pass NULL if you don't care.
10943 * @approval is optional external approval of box manipulation
10944 * @refnumber is reallocated serial number of request assigned by ISDS. Use
10945 * NULL, if you don't care.*/
10946 isds_error isds_request_new_testing_box(struct isds_ctx *context,
10947 struct isds_DbOwnerInfo *box, const struct isds_list *users,
10948 const char *former_names, const struct isds_approval *approval,
10949 char **refnumber) {
10950 isds_error err = IE_SUCCESS;
10951 #if HAVE_LIBCURL
10952 xmlNodePtr request = NULL;
10953 xmlDocPtr response = NULL;
10954 xmlXPathContextPtr xpath_ctx = NULL;
10955 xmlXPathObjectPtr result = NULL;
10956 #endif
10959 if (!context) return IE_INVALID_CONTEXT;
10960 zfree(context->long_message);
10961 if (!box) return IE_INVAL;
10963 #if HAVE_LIBCURL
10964 if (!box->email || box->email[0] == '\0') {
10965 isds_log_message(context, _("E-mail field is mandatory"));
10966 return IE_INVAL;
10969 /* Scratch box ID */
10970 zfree(box->dbID);
10972 /* Store configuration */
10973 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
10974 free(context->url);
10975 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
10976 if (!(context->url))
10977 return IE_NOMEM;
10979 /* Prepare CURL handle if not yet connected */
10980 if (!context->curl) {
10981 context->curl = curl_easy_init();
10982 if (!(context->curl))
10983 return IE_ERROR;
10986 /* Build CreateDataBox request */
10987 err = build_CreateDBInput_request(context,
10988 &request, BAD_CAST "CreateDataBox",
10989 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
10990 if (err) goto leave;
10992 /* Send it to server and process response */
10993 err = send_destroy_request_check_response(context,
10994 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
10995 &response, (xmlChar **) refnumber, NULL);
10996 if (err) goto leave;
10998 /* Extract box ID */
10999 xpath_ctx = xmlXPathNewContext(response);
11000 if (!xpath_ctx) {
11001 err = IE_ERROR;
11002 goto leave;
11004 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11005 err = IE_ERROR;
11006 goto leave;
11008 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
11010 leave:
11011 xmlXPathFreeObject(result);
11012 xmlXPathFreeContext(xpath_ctx);
11013 xmlFreeDoc(response);
11014 xmlFreeNode(request);
11016 if (!err) {
11017 isds_log(ILF_ISDS, ILL_DEBUG,
11018 _("CreateDataBox request processed by server successfully.\n"));
11020 #else /* not HAVE_LIBCURL */
11021 err = IE_NOTSUP;
11022 #endif
11024 return err;
11028 /* Submit CMS signed message to ISDS to verify its originality. This is
11029 * stronger form of isds_verify_message_hash() because ISDS does more checks
11030 * than simple one (potentialy old weak) hash comparison.
11031 * @context is session context
11032 * @message is memory with raw CMS signed message bit stream
11033 * @length is @message size in bytes
11034 * @return
11035 * IE_SUCCESS if message originates in ISDS
11036 * IE_NOTEQUAL if message is unknown to ISDS
11037 * other code for other errors */
11038 isds_error isds_authenticate_message(struct isds_ctx *context,
11039 const void *message, size_t length) {
11040 isds_error err = IE_SUCCESS;
11041 #if HAVE_LIBCURL
11042 xmlNsPtr isds_ns = NULL;
11043 xmlNodePtr request = NULL;
11044 xmlDocPtr response = NULL;
11045 xmlXPathContextPtr xpath_ctx = NULL;
11046 xmlXPathObjectPtr result = NULL;
11047 _Bool *authentic = NULL;
11048 #endif
11050 if (!context) return IE_INVALID_CONTEXT;
11051 zfree(context->long_message);
11052 if (!message || length == 0) return IE_INVAL;
11054 #if HAVE_LIBCURL
11055 /* Check if connection is established
11056 * TODO: This check should be done downstairs. */
11057 if (!context->curl) return IE_CONNECTION_CLOSED;
11060 /* Build AuthenticateMessage request */
11061 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11062 if (!request) {
11063 isds_log_message(context,
11064 _("Could not build AuthenticateMessage request"));
11065 return IE_ERROR;
11067 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11068 if(!isds_ns) {
11069 isds_log_message(context, _("Could not create ISDS name space"));
11070 xmlFreeNode(request);
11071 return IE_ERROR;
11073 xmlSetNs(request, isds_ns);
11075 /* Insert Base64 encoded message */
11076 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11077 message, length);
11078 if (err) goto leave;
11080 /* Send request to server and process response */
11081 err = send_destroy_request_check_response(context,
11082 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11083 &response, NULL, NULL);
11084 if (err) goto leave;
11087 /* ISDS has decided */
11088 xpath_ctx = xmlXPathNewContext(response);
11089 if (!xpath_ctx) {
11090 err = IE_ERROR;
11091 goto leave;
11093 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11094 err = IE_ERROR;
11095 goto leave;
11098 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11100 if (!authentic) {
11101 isds_log_message(context,
11102 _("Server did not return any response on "
11103 "AuthenticateMessage request"));
11104 err = IE_ISDS;
11105 goto leave;
11107 if (*authentic) {
11108 isds_log(ILF_ISDS, ILL_DEBUG,
11109 _("ISDS authenticated the message successfully\n"));
11110 } else {
11111 isds_log_message(context, _("ISDS does not know the message"));
11112 err = IE_NOTEQUAL;
11116 leave:
11117 free(authentic);
11118 xmlXPathFreeObject(result);
11119 xmlXPathFreeContext(xpath_ctx);
11121 xmlFreeDoc(response);
11122 xmlFreeNode(request);
11123 #else /* not HAVE_LIBCURL */
11124 err = IE_NOTSUP;
11125 #endif
11127 return err;
11131 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11132 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11133 * be re-signed.
11134 * @context is session context
11135 * @input_data is memory with raw CMS signed message or delivery info bit
11136 * stream to re-sign
11137 * @input_length is @input_data size in bytes
11138 * @output_data is pointer to auto-allocated memory where to store re-signed
11139 * input data blob. Caller must free it.
11140 * @output_data is pointer where to store @output_data size in bytes
11141 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11142 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11143 * @return
11144 * IE_SUCCESS if CMS blob has been re-signed successfully
11145 * other code for other errors */
11146 isds_error isds_resign_message(struct isds_ctx *context,
11147 const void *input_data, size_t input_length,
11148 void **output_data, size_t *output_length, struct tm **valid_to) {
11149 isds_error err = IE_SUCCESS;
11150 #if HAVE_LIBCURL
11151 xmlNsPtr isds_ns = NULL;
11152 xmlNodePtr request = NULL;
11153 xmlDocPtr response = NULL;
11154 xmlXPathContextPtr xpath_ctx = NULL;
11155 xmlXPathObjectPtr result = NULL;
11156 char *string = NULL;
11157 const xmlChar *codes[] = {
11158 BAD_CAST "2200",
11159 BAD_CAST "2201",
11160 BAD_CAST "2204",
11161 BAD_CAST "2207",
11162 NULL
11164 const char *meanings[] = {
11165 "Message is bad",
11166 "Message is not original",
11167 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11168 "Time stamp could not been generated in time"
11170 const isds_error errors[] = {
11171 IE_INVAL,
11172 IE_NOTUNIQ,
11173 IE_INVAL,
11174 IE_ISDS,
11176 struct code_map_isds_error map = {
11177 .codes = codes,
11178 .meanings = meanings,
11179 .errors = errors
11181 #endif
11183 if (NULL != output_data) *output_data = NULL;
11184 if (NULL != output_length) *output_length = 0;
11185 if (NULL != valid_to) *valid_to = NULL;
11187 if (NULL == context) return IE_INVALID_CONTEXT;
11188 zfree(context->long_message);
11189 if (NULL == input_data || 0 == input_length) {
11190 isds_log_message(context, _("Empty CMS blob on input"));
11191 return IE_INVAL;
11193 if (NULL == output_data || NULL == output_length) {
11194 isds_log_message(context,
11195 _("NULL pointer provided for output CMS blob"));
11196 return IE_INVAL;
11199 #if HAVE_LIBCURL
11200 /* Check if connection is established
11201 * TODO: This check should be done downstairs. */
11202 if (!context->curl) return IE_CONNECTION_CLOSED;
11205 /* Build Re-signISDSDocument request */
11206 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11207 if (!request) {
11208 isds_log_message(context,
11209 _("Could not build Re-signISDSDocument request"));
11210 return IE_ERROR;
11212 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11213 if(!isds_ns) {
11214 isds_log_message(context, _("Could not create ISDS name space"));
11215 xmlFreeNode(request);
11216 return IE_ERROR;
11218 xmlSetNs(request, isds_ns);
11220 /* Insert Base64 encoded CMS blob */
11221 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11222 input_data, input_length);
11223 if (err) goto leave;
11225 /* Send request to server and process response */
11226 err = send_destroy_request_check_response(context,
11227 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11228 &response, NULL, &map);
11229 if (err) goto leave;
11232 /* Extract re-signed data */
11233 xpath_ctx = xmlXPathNewContext(response);
11234 if (!xpath_ctx) {
11235 err = IE_ERROR;
11236 goto leave;
11238 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11239 err = IE_ERROR;
11240 goto leave;
11242 result = xmlXPathEvalExpression(
11243 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11244 if (!result) {
11245 err = IE_ERROR;
11246 goto leave;
11248 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11249 isds_log_message(context,
11250 _("Missing Re-signISDSDocumentResponse element"));
11251 err = IE_ISDS;
11252 goto leave;
11254 if (result->nodesetval->nodeNr > 1) {
11255 isds_log_message(context,
11256 _("Multiple Re-signISDSDocumentResponse element"));
11257 err = IE_ISDS;
11258 goto leave;
11260 xpath_ctx->node = result->nodesetval->nodeTab[0];
11261 xmlXPathFreeObject(result); result = NULL;
11263 EXTRACT_STRING("isds:dmResultDoc", string);
11264 /* Decode non-empty data */
11265 if (NULL != string && string[0] != '\0') {
11266 *output_length = _isds_b64decode(string, output_data);
11267 if (*output_length == (size_t) -1) {
11268 isds_log_message(context,
11269 _("Error while Base64-decoding re-signed data"));
11270 err = IE_ERROR;
11271 goto leave;
11273 } else {
11274 isds_log_message(context, _("Server did not send re-signed data"));
11275 err = IE_ISDS;
11276 goto leave;
11278 zfree(string);
11280 if (NULL != valid_to) {
11281 /* Get time stamp expiration date */
11282 EXTRACT_STRING("isds:dmValidTo", string);
11283 if (NULL != string) {
11284 *valid_to = calloc(1, sizeof(**valid_to));
11285 if (!*valid_to) {
11286 err = IE_NOMEM;
11287 goto leave;
11289 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11290 if (err) {
11291 if (err == IE_NOTSUP) {
11292 err = IE_ISDS;
11293 char *string_locale = _isds_utf82locale(string);
11294 isds_printf_message(context,
11295 _("Invalid dmValidTo value: %s"), string_locale);
11296 free(string_locale);
11298 goto leave;
11303 leave:
11304 free(string);
11306 xmlXPathFreeObject(result);
11307 xmlXPathFreeContext(xpath_ctx);
11309 xmlFreeDoc(response);
11310 xmlFreeNode(request);
11311 #else /* not HAVE_LIBCURL */
11312 err = IE_NOTSUP;
11313 #endif
11315 return err;
11318 #undef INSERT_ELEMENT
11319 #undef CHECK_FOR_STRING_LENGTH
11320 #undef INSERT_STRING_ATTRIBUTE
11321 #undef INSERT_ULONGINTNOPTR
11322 #undef INSERT_ULONGINT
11323 #undef INSERT_LONGINT
11324 #undef INSERT_BOOLEAN
11325 #undef INSERT_SCALAR_BOOLEAN
11326 #undef INSERT_STRING
11327 #undef INSERT_STRING_WITH_NS
11328 #undef EXTRACT_STRING_ATTRIBUTE
11329 #undef EXTRACT_ULONGINT
11330 #undef EXTRACT_LONGINT
11331 #undef EXTRACT_BOOLEAN
11332 #undef EXTRACT_STRING
11335 /* Compute hash of message from raw representation and store it into envelope.
11336 * Original hash structure will be destroyed in envelope.
11337 * @context is session context
11338 * @message is message carrying raw XML message blob
11339 * @algorithm is desired hash algorithm to use */
11340 isds_error isds_compute_message_hash(struct isds_ctx *context,
11341 struct isds_message *message, const isds_hash_algorithm algorithm) {
11342 isds_error err = IE_SUCCESS;
11343 const char *nsuri;
11344 void *xml_stream = NULL;
11345 size_t xml_stream_length;
11346 size_t phys_start, phys_end;
11347 char *phys_path = NULL;
11348 struct isds_hash *new_hash = NULL;
11351 if (!context) return IE_INVALID_CONTEXT;
11352 zfree(context->long_message);
11353 if (!message) return IE_INVAL;
11355 if (!message->raw) {
11356 isds_log_message(context,
11357 _("Message does not carry raw representation"));
11358 return IE_INVAL;
11361 switch (message->raw_type) {
11362 case RAWTYPE_INCOMING_MESSAGE:
11363 nsuri = ISDS_NS;
11364 xml_stream = message->raw;
11365 xml_stream_length = message->raw_length;
11366 break;
11368 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11369 nsuri = SISDS_INCOMING_NS;
11370 xml_stream = message->raw;
11371 xml_stream_length = message->raw_length;
11372 break;
11374 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11375 nsuri = SISDS_INCOMING_NS;
11376 err = _isds_extract_cms_data(context,
11377 message->raw, message->raw_length,
11378 &xml_stream, &xml_stream_length);
11379 if (err) goto leave;
11380 break;
11382 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11383 nsuri = SISDS_OUTGOING_NS;
11384 xml_stream = message->raw;
11385 xml_stream_length = message->raw_length;
11386 break;
11388 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11389 nsuri = SISDS_OUTGOING_NS;
11390 err = _isds_extract_cms_data(context,
11391 message->raw, message->raw_length,
11392 &xml_stream, &xml_stream_length);
11393 if (err) goto leave;
11394 break;
11396 default:
11397 isds_log_message(context, _("Bad raw representation type"));
11398 return IE_INVAL;
11399 break;
11403 /* XXX: Hash is computed from original string representing isds:dmDm
11404 * subtree. That means no encoding, white space, xmlns attributes changes.
11405 * In other words, input for hash can be invalid XML stream. */
11406 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
11407 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11408 PHYSXML_ELEMENT_SEPARATOR,
11409 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
11410 PHYSXML_ELEMENT_SEPARATOR
11411 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
11412 err = IE_NOMEM;
11413 goto leave;
11415 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11416 phys_path, &phys_start, &phys_end);
11417 zfree(phys_path);
11418 if (err) {
11419 isds_log_message(context,
11420 _("Substring with isds:dmDM element could not be located "
11421 "in raw message"));
11422 goto leave;
11426 /* Compute hash */
11427 new_hash = calloc(1, sizeof(*new_hash));
11428 if (!new_hash) {
11429 err = IE_NOMEM;
11430 goto leave;
11432 new_hash->algorithm = algorithm;
11433 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
11434 new_hash);
11435 if (err) {
11436 isds_log_message(context, _("Could not compute message hash"));
11437 goto leave;
11440 /* Save computed hash */
11441 if (!message->envelope) {
11442 message->envelope = calloc(1, sizeof(*message->envelope));
11443 if (!message->envelope) {
11444 err = IE_NOMEM;
11445 goto leave;
11448 isds_hash_free(&message->envelope->hash);
11449 message->envelope->hash = new_hash;
11451 leave:
11452 if (err) {
11453 isds_hash_free(&new_hash);
11456 free(phys_path);
11457 if (xml_stream != message->raw) free(xml_stream);
11458 return err;
11462 /* Compare two hashes.
11463 * @h1 is first hash
11464 * @h2 is another hash
11465 * @return
11466 * IE_SUCCESS if hashes equal
11467 * IE_NOTUNIQ if hashes are comparable, but they don't equal
11468 * IE_ENUM if not comparable, but both structures defined
11469 * IE_INVAL if some of the structures are undefined (NULL)
11470 * IE_ERROR if internal error occurs */
11471 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
11472 if (h1 == NULL || h2 == NULL) return IE_INVAL;
11473 if (h1->algorithm != h2->algorithm) return IE_ENUM;
11474 if (h1->length != h2->length) return IE_ERROR;
11475 if (h1->length > 0 && !h1->value) return IE_ERROR;
11476 if (h2->length > 0 && !h2->value) return IE_ERROR;
11478 for (int i = 0; i < h1->length; i++) {
11479 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
11480 return IE_NOTEQUAL;
11482 return IE_SUCCESS;
11486 /* Check message has gone through ISDS by comparing message hash stored in
11487 * ISDS and locally computed hash. You must provide message with valid raw
11488 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
11489 * This is convenient wrapper for isds_download_message_hash(),
11490 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
11491 * @context is session context
11492 * @message is message with valid raw and envelope member; envelope->hash
11493 * member will be changed during function run. Use envelope on heap only.
11494 * @return
11495 * IE_SUCCESS if message originates in ISDS
11496 * IE_NOTEQUAL if message is unknown to ISDS
11497 * other code for other errors */
11498 isds_error isds_verify_message_hash(struct isds_ctx *context,
11499 struct isds_message *message) {
11500 isds_error err = IE_SUCCESS;
11501 struct isds_hash *downloaded_hash = NULL;
11503 if (!context) return IE_INVALID_CONTEXT;
11504 zfree(context->long_message);
11505 if (!message) return IE_INVAL;
11507 if (!message->envelope) {
11508 isds_log_message(context,
11509 _("Given message structure is missing envelope"));
11510 return IE_INVAL;
11512 if (!message->raw) {
11513 isds_log_message(context,
11514 _("Given message structure is missing raw representation"));
11515 return IE_INVAL;
11518 err = isds_download_message_hash(context, message->envelope->dmID,
11519 &downloaded_hash);
11520 if (err) goto leave;
11522 err = isds_compute_message_hash(context, message,
11523 downloaded_hash->algorithm);
11524 if (err) goto leave;
11526 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
11528 leave:
11529 isds_hash_free(&downloaded_hash);
11530 return err;
11534 /* Search for document by document ID in list of documents. IDs are compared
11535 * as UTF-8 string.
11536 * @documents is list of isds_documents
11537 * @id is document identifier
11538 * @return first matching document or NULL. */
11539 const struct isds_document *isds_find_document_by_id(
11540 const struct isds_list *documents, const char *id) {
11541 const struct isds_list *item;
11542 const struct isds_document *document;
11544 for (item = documents; item; item = item->next) {
11545 document = (struct isds_document *) item->data;
11546 if (!document) continue;
11548 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
11549 return document;
11552 return NULL;
11556 /* Normalize @mime_type to be proper MIME type.
11557 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
11558 * guess regular MIME type (e.g. "application/pdf").
11559 * @mime_type is UTF-8 encoded MIME type to fix
11560 * @return original @mime_type if no better interpretation exists, or
11561 * constant static UTF-8 encoded string with proper MIME type. */
11562 const char *isds_normalize_mime_type(const char *mime_type) {
11563 if (!mime_type) return NULL;
11565 for (int offset = 0;
11566 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
11567 offset += 2) {
11568 if (!xmlStrcasecmp((const xmlChar*) mime_type,
11569 extension_map_mime[offset]))
11570 return (const char *) extension_map_mime[offset + 1];
11573 return mime_type;
11577 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
11578 struct isds_message **message);
11579 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
11580 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
11581 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
11582 struct isds_address **address);
11584 int isds_message_free(struct isds_message **message);
11585 int isds_address_free(struct isds_address **address);
11589 /* Makes known all relevant namespaces to given XPath context
11590 * @xpath_ctx is XPath context
11591 * @message_ns selects proper message name space. Unsigned and signed
11592 * messages and delivery info's differ in prefix and URI. */
11593 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
11594 const message_ns_type message_ns) {
11595 const xmlChar *message_namespace = NULL;
11597 if (!xpath_ctx) return IE_ERROR;
11599 switch(message_ns) {
11600 case MESSAGE_NS_1:
11601 message_namespace = BAD_CAST ISDS1_NS; break;
11602 case MESSAGE_NS_UNSIGNED:
11603 message_namespace = BAD_CAST ISDS_NS; break;
11604 case MESSAGE_NS_SIGNED_INCOMING:
11605 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
11606 case MESSAGE_NS_SIGNED_OUTGOING:
11607 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
11608 case MESSAGE_NS_SIGNED_DELIVERY:
11609 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
11610 default:
11611 return IE_ENUM;
11614 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
11615 return IE_ERROR;
11616 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
11617 return IE_ERROR;
11618 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
11619 return IE_ERROR;
11620 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
11621 return IE_ERROR;
11622 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
11623 return IE_ERROR;
11624 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
11625 return IE_ERROR;
11626 return IE_SUCCESS;