Add support for OVM_MAIN searchScope in isds_find_box_fulltext()
[libisds.git] / src / isds.c
blobd1f482a074cdd66ad52c5208ede976df6fc51ad6
1 #include "isds_priv.h"
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include <stdint.h> /* For uint8_t and intmax_t */
8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
9 #include <inttypes.h> /* For PRIdMAX formatting macro */
10 #include "utils.h"
11 #if HAVE_LIBCURL
12 #include "soap.h"
13 #endif
14 #include "validator.h"
15 #include "crypto.h"
16 #include "physxml.h"
17 #include "system.h"
19 /* Global variables.
20 * Allocated in isds_init() and deallocated in isds_cleanup(). */
21 unsigned int log_facilities;
22 isds_log_level log_level;
23 isds_log_callback log_callback;
24 void *log_callback_data;
25 const char *version_gpgme = N_("n/a");
26 const char *version_gcrypt = N_("n/a");
27 const char *version_openssl = N_("n/a");
28 const char *version_expat = N_("n/a");
30 /* Locators */
31 /* Base URL of production ISDS instance */
32 const char isds_locator[] = "https://ws1.mojedatovaschranka.cz/";
33 const char isds_cert_locator[] = "https://ws1c.mojedatovaschranka.cz/";
34 const char isds_otp_locator[] = "https://www.mojedatovaschranka.cz/";
36 /* Base URL of production ISDS instance */
37 const char isds_testing_locator[] = "https://ws1.czebox.cz/";
38 const char isds_cert_testing_locator[] = "https://ws1c.czebox.cz/";
39 const char isds_otp_testing_locator[] = "https://www.czebox.cz/";
41 /* Extension to MIME type map */
42 static const xmlChar *extension_map_mime[] = {
43 BAD_CAST "cer", BAD_CAST "application/x-x509-ca-cert",
44 BAD_CAST "crt", BAD_CAST "application/x-x509-ca-cert",
45 BAD_CAST "der", BAD_CAST "application/x-x509-ca-cert",
46 BAD_CAST "doc", BAD_CAST "application/msword",
47 BAD_CAST "docx", BAD_CAST "application/vnd.openxmlformats-officedocument."
48 "wordprocessingml.document",
49 BAD_CAST "dbf", BAD_CAST "application/octet-stream",
50 BAD_CAST "prj", BAD_CAST "application/octet-stream",
51 BAD_CAST "qix", BAD_CAST "application/octet-stream",
52 BAD_CAST "sbn", BAD_CAST "application/octet-stream",
53 BAD_CAST "sbx", BAD_CAST "application/octet-stream",
54 BAD_CAST "shp", BAD_CAST "application/octet-stream",
55 BAD_CAST "shx", BAD_CAST "application/octet-stream",
56 BAD_CAST "dgn", BAD_CAST "application/octet-stream",
57 BAD_CAST "dwg", BAD_CAST "image/vnd.dwg",
58 BAD_CAST "edi", BAD_CAST "application/edifact",
59 BAD_CAST "fo", BAD_CAST "application/vnd.software602.filler.form+xml",
60 BAD_CAST "gfs", BAD_CAST "application/xml",
61 BAD_CAST "gml", BAD_CAST "application/xml",
62 BAD_CAST "gif", BAD_CAST "image/gif",
63 BAD_CAST "htm", BAD_CAST "text/html",
64 BAD_CAST "html", BAD_CAST "text/html",
65 BAD_CAST "isdoc", BAD_CAST "text/isdoc",
66 BAD_CAST "isdocx", BAD_CAST "text/isdocx",
67 BAD_CAST "jfif", BAD_CAST "image/jpeg",
68 BAD_CAST "jpg", BAD_CAST "image/jpeg",
69 BAD_CAST "jpeg", BAD_CAST "image/jpeg",
70 BAD_CAST "mpeg", BAD_CAST "video/mpeg",
71 BAD_CAST "mpeg1", BAD_CAST "video/mpeg",
72 BAD_CAST "mpeg2", BAD_CAST "video/mpeg",
73 BAD_CAST "mpg", BAD_CAST "video/mpeg",
74 BAD_CAST "mp2", BAD_CAST "audio/mpeg",
75 BAD_CAST "mp3", BAD_CAST "audio/mpeg",
76 BAD_CAST "odp", BAD_CAST "application/vnd.oasis.opendocument.presentation",
77 BAD_CAST "ods", BAD_CAST "application/vnd.oasis.opendocument.spreadsheet",
78 BAD_CAST "odt", BAD_CAST "application/vnd.oasis.opendocument.text",
79 BAD_CAST "pdf", BAD_CAST "application/pdf",
80 BAD_CAST "p7b", BAD_CAST "application/pkcs7-certificates",
81 BAD_CAST "p7c", BAD_CAST "application/pkcs7-mime",
82 BAD_CAST "p7m", BAD_CAST "application/pkcs7-mime",
83 BAD_CAST "p7f", BAD_CAST "application/pkcs7-signature",
84 BAD_CAST "p7s", BAD_CAST "application/pkcs7-signature",
85 BAD_CAST "pk7", BAD_CAST "application/pkcs7-mime",
86 BAD_CAST "png", BAD_CAST "image/png",
87 BAD_CAST "ppt", BAD_CAST "application/vnd.ms-powerpoint",
88 BAD_CAST "pptx", BAD_CAST "application/vnd.openxmlformats-officedocument."
89 "presentationml.presentation",
90 BAD_CAST "rtf", BAD_CAST "application/rtf",
91 BAD_CAST "tif", BAD_CAST "image/tiff",
92 BAD_CAST "tiff", BAD_CAST "image/tiff",
93 BAD_CAST "tsr", BAD_CAST "application/timestamp-reply",
94 BAD_CAST "tst", BAD_CAST "application/timestamp-reply",
95 BAD_CAST "txt", BAD_CAST "text/plain",
96 BAD_CAST "wav", BAD_CAST "audio/wav",
97 BAD_CAST "xls", BAD_CAST "application/vnd.ms-excel",
98 BAD_CAST "xlsx", BAD_CAST "application/vnd.openxmlformats-officedocument."
99 "spreadsheetml.sheet",
100 BAD_CAST "xml", BAD_CAST "application/xml",
101 BAD_CAST "xsd", BAD_CAST "application/xml",
102 BAD_CAST "zfo", BAD_CAST "application/vnd.software602.filler.form-xml-zip"
105 /* Structure type to hold conversion table from status code to isds_error and
106 * long message */
107 struct code_map_isds_error {
108 const xmlChar **codes; /* NULL terminated array of status codes */
109 const char **meanings; /* Mapping to non-localized long messages */
110 const isds_error *errors; /* Mapping to isds_error code */
113 /* Deallocate structure isds_pki_credentials and NULL it.
114 * Pass-phrase is discarded.
115 * @pki credentials to to free */
116 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
117 if(!pki || !*pki) return;
119 free((*pki)->engine);
120 free((*pki)->certificate);
121 free((*pki)->key);
123 if ((*pki)->passphrase) {
124 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
125 free((*pki)->passphrase);
128 zfree((*pki));
132 /* Free isds_list with all member data.
133 * @list list to free, on return will be NULL */
134 void isds_list_free(struct isds_list **list) {
135 struct isds_list *item, *next_item;
137 if (!list || !*list) return;
139 for(item = *list; item; item = next_item) {
140 if (item->destructor) (item->destructor)(&(item->data));
141 next_item = item->next;
142 free(item);
145 *list = NULL;
149 /* Deallocate structure isds_hash and NULL it.
150 * @hash hash to to free */
151 void isds_hash_free(struct isds_hash **hash) {
152 if(!hash || !*hash) return;
153 free((*hash)->value);
154 zfree((*hash));
158 /* Deallocate structure isds_PersonName recursively and NULL it */
159 void isds_PersonName_free(struct isds_PersonName **person_name) {
160 if (!person_name || !*person_name) return;
162 free((*person_name)->pnFirstName);
163 free((*person_name)->pnMiddleName);
164 free((*person_name)->pnLastName);
165 free((*person_name)->pnLastNameAtBirth);
167 free(*person_name);
168 *person_name = NULL;
172 /* Deallocate structure isds_BirthInfo recursively and NULL it */
173 void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
174 if (!birth_info || !*birth_info) return;
176 free((*birth_info)->biDate);
177 free((*birth_info)->biCity);
178 free((*birth_info)->biCounty);
179 free((*birth_info)->biState);
181 free(*birth_info);
182 *birth_info = NULL;
186 /* Deallocate structure isds_Address recursively and NULL it */
187 void isds_Address_free(struct isds_Address **address) {
188 if (!address || !*address) return;
190 free((*address)->adCity);
191 free((*address)->adStreet);
192 free((*address)->adNumberInStreet);
193 free((*address)->adNumberInMunicipality);
194 free((*address)->adZipCode);
195 free((*address)->adState);
197 free(*address);
198 *address = NULL;
202 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
203 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
204 if (!db_owner_info || !*db_owner_info) return;
206 free((*db_owner_info)->dbID);
207 free((*db_owner_info)->dbType);
208 free((*db_owner_info)->ic);
209 isds_PersonName_free(&((*db_owner_info)->personName));
210 free((*db_owner_info)->firmName);
211 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
212 isds_Address_free(&((*db_owner_info)->address));
213 free((*db_owner_info)->nationality);
214 free((*db_owner_info)->email);
215 free((*db_owner_info)->telNumber);
216 free((*db_owner_info)->identifier);
217 free((*db_owner_info)->registryCode);
218 free((*db_owner_info)->dbState);
219 free((*db_owner_info)->dbEffectiveOVM);
220 free((*db_owner_info)->dbOpenAddressing);
222 free(*db_owner_info);
223 *db_owner_info = NULL;
226 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
227 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
228 if (!db_user_info || !*db_user_info) return;
230 free((*db_user_info)->userID);
231 free((*db_user_info)->userType);
232 free((*db_user_info)->userPrivils);
233 isds_PersonName_free(&((*db_user_info)->personName));
234 isds_Address_free(&((*db_user_info)->address));
235 free((*db_user_info)->biDate);
236 free((*db_user_info)->ic);
237 free((*db_user_info)->firmName);
238 free((*db_user_info)->caStreet);
239 free((*db_user_info)->caCity);
240 free((*db_user_info)->caZipCode);
241 free((*db_user_info)->caState);
242 free((*db_user_info)->aifo_ticket);
244 zfree(*db_user_info);
248 /* Deallocate struct isds_event recursively and NULL it */
249 void isds_event_free(struct isds_event **event) {
250 if (!event || !*event) return;
252 free((*event)->time);
253 free((*event)->type);
254 free((*event)->description);
255 zfree(*event);
259 /* Deallocate struct isds_envelope recursively and NULL it */
260 void isds_envelope_free(struct isds_envelope **envelope) {
261 if (!envelope || !*envelope) return;
263 free((*envelope)->dmID);
264 free((*envelope)->dbIDSender);
265 free((*envelope)->dmSender);
266 free((*envelope)->dmSenderAddress);
267 free((*envelope)->dmSenderType);
268 free((*envelope)->dmRecipient);
269 free((*envelope)->dmRecipientAddress);
270 free((*envelope)->dmAmbiguousRecipient);
271 free((*envelope)->dmType);
273 free((*envelope)->dmOrdinal);
274 free((*envelope)->dmMessageStatus);
275 free((*envelope)->dmDeliveryTime);
276 free((*envelope)->dmAcceptanceTime);
277 isds_hash_free(&(*envelope)->hash);
278 free((*envelope)->timestamp);
279 isds_list_free(&(*envelope)->events);
281 free((*envelope)->dmSenderOrgUnit);
282 free((*envelope)->dmSenderOrgUnitNum);
283 free((*envelope)->dbIDRecipient);
284 free((*envelope)->dmRecipientOrgUnit);
285 free((*envelope)->dmRecipientOrgUnitNum);
286 free((*envelope)->dmToHands);
287 free((*envelope)->dmAnnotation);
288 free((*envelope)->dmRecipientRefNumber);
289 free((*envelope)->dmSenderRefNumber);
290 free((*envelope)->dmRecipientIdent);
291 free((*envelope)->dmSenderIdent);
293 free((*envelope)->dmLegalTitleLaw);
294 free((*envelope)->dmLegalTitleYear);
295 free((*envelope)->dmLegalTitleSect);
296 free((*envelope)->dmLegalTitlePar);
297 free((*envelope)->dmLegalTitlePoint);
299 free((*envelope)->dmPersonalDelivery);
300 free((*envelope)->dmAllowSubstDelivery);
302 free((*envelope)->dmOVM);
303 free((*envelope)->dmPublishOwnID);
305 free(*envelope);
306 *envelope = NULL;
310 /* Deallocate struct isds_message recursively and NULL it */
311 void isds_message_free(struct isds_message **message) {
312 if (!message || !*message) return;
314 free((*message)->raw);
315 isds_envelope_free(&((*message)->envelope));
316 isds_list_free(&((*message)->documents));
317 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
319 free(*message);
320 *message = NULL;
324 /* Deallocate struct isds_document recursively and NULL it */
325 void isds_document_free(struct isds_document **document) {
326 if (!document || !*document) return;
328 if (!(*document)->is_xml) {
329 free((*document)->data);
331 free((*document)->dmMimeType);
332 free((*document)->dmFileGuid);
333 free((*document)->dmUpFileGuid);
334 free((*document)->dmFileDescr);
335 free((*document)->dmFormat);
337 free(*document);
338 *document = NULL;
342 /* Deallocate struct isds_message_copy recursively and NULL it */
343 void isds_message_copy_free(struct isds_message_copy **copy) {
344 if (!copy || !*copy) return;
346 free((*copy)->dbIDRecipient);
347 free((*copy)->dmRecipientOrgUnit);
348 free((*copy)->dmRecipientOrgUnitNum);
349 free((*copy)->dmToHands);
351 free((*copy)->dmStatus);
352 free((*copy)->dmID);
354 zfree(*copy);
358 /* Deallocate struct isds_message_status_change recursively and NULL it */
359 void isds_message_status_change_free(
360 struct isds_message_status_change **message_status_change) {
361 if (!message_status_change || !*message_status_change) return;
363 free((*message_status_change)->dmID);
364 free((*message_status_change)->time);
365 free((*message_status_change)->dmMessageStatus);
367 zfree(*message_status_change);
371 /* Deallocate struct isds_approval recursively and NULL it */
372 void isds_approval_free(struct isds_approval **approval) {
373 if (!approval || !*approval) return;
375 free((*approval)->refference);
377 zfree(*approval);
381 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
382 * The email string is deallocated too. */
383 void isds_credentials_delivery_free(
384 struct isds_credentials_delivery **credentials_delivery) {
385 if (!credentials_delivery || !*credentials_delivery) return;
387 free((*credentials_delivery)->email);
388 free((*credentials_delivery)->token);
389 free((*credentials_delivery)->new_user_name);
391 zfree(*credentials_delivery);
395 /* Deallocate struct isds_commercial_permission recursively and NULL it */
396 void isds_commercial_permission_free(
397 struct isds_commercial_permission **permission) {
398 if (NULL == permission || NULL == *permission) return;
400 free((*permission)->recipient);
401 free((*permission)->payer);
402 free((*permission)->expiration);
403 free((*permission)->count);
404 free((*permission)->reply_identifier);
406 zfree(*permission);
410 /* Deallocate struct isds_credit_event recursively and NULL it */
411 void isds_credit_event_free(struct isds_credit_event **event) {
412 if (NULL == event || NULL == *event) return;
414 free((*event)->time);
415 switch ((*event)->type) {
416 case ISDS_CREDIT_CHARGED:
417 free((*event)->details.charged.transaction);
418 break;
419 case ISDS_CREDIT_DISCHARGED:
420 free((*event)->details.discharged.transaction);
421 break;
422 case ISDS_CREDIT_MESSAGE_SENT:
423 free((*event)->details.message_sent.recipient);
424 free((*event)->details.message_sent.message_id);
425 break;
426 case ISDS_CREDIT_STORAGE_SET:
427 free((*event)->details.storage_set.new_valid_from);
428 free((*event)->details.storage_set.new_valid_to);
429 free((*event)->details.storage_set.old_capacity);
430 free((*event)->details.storage_set.old_valid_from);
431 free((*event)->details.storage_set.old_valid_to);
432 free((*event)->details.storage_set.initiator);
433 break;
434 case ISDS_CREDIT_EXPIRED:
435 break;
438 zfree(*event);
442 /* Deallocate struct isds_fulltext_result recursively and NULL it */
443 void isds_fulltext_result_free(
444 struct isds_fulltext_result **result) {
445 if (NULL == result || NULL == *result) return;
447 free((*result)->dbID);
448 free((*result)->name);
449 isds_list_free(&((*result)->name_match_start));
450 isds_list_free(&((*result)->name_match_end));
451 free((*result)->address);
452 isds_list_free(&((*result)->address_match_start));
453 isds_list_free(&((*result)->address_match_end));
454 free((*result)->ic);
455 free((*result)->biDate);
457 zfree(*result);
461 /* *DUP_OR_ERROR macros needs error label */
462 #define STRDUP_OR_ERROR(new, template) { \
463 if (!template) { \
464 (new) = NULL; \
465 } else { \
466 (new) = strdup(template); \
467 if (!new) goto error; \
471 #define FLATDUP_OR_ERROR(new, template) { \
472 if (!template) { \
473 (new) = NULL; \
474 } else { \
475 (new) = malloc(sizeof(*(new))); \
476 if (!new) goto error; \
477 memcpy((new), (template), sizeof(*(template))); \
481 /* Copy structure isds_pki_credentials recursively. */
482 struct isds_pki_credentials *isds_pki_credentials_duplicate(
483 const struct isds_pki_credentials *template) {
484 struct isds_pki_credentials *new = NULL;
486 if(!template) return NULL;
488 new = calloc(1, sizeof(*new));
489 if (!new) return NULL;
491 STRDUP_OR_ERROR(new->engine, template->engine);
492 new->certificate_format = template->certificate_format;
493 STRDUP_OR_ERROR(new->certificate, template->certificate);
494 new->key_format = template->key_format;
495 STRDUP_OR_ERROR(new->key, template->key);
496 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
498 return new;
500 error:
501 isds_pki_credentials_free(&new);
502 return NULL;
506 /* Copy structure isds_PersonName recursively */
507 struct isds_PersonName *isds_PersonName_duplicate(
508 const struct isds_PersonName *src) {
509 struct isds_PersonName *new = NULL;
511 if (!src) return NULL;
513 new = calloc(1, sizeof(*new));
514 if (!new) return NULL;
516 STRDUP_OR_ERROR(new->pnFirstName, src->pnFirstName);
517 STRDUP_OR_ERROR(new->pnMiddleName, src->pnMiddleName);
518 STRDUP_OR_ERROR(new->pnLastName, src->pnLastName);
519 STRDUP_OR_ERROR(new->pnLastNameAtBirth, src->pnLastNameAtBirth);
521 return new;
523 error:
524 isds_PersonName_free(&new);
525 return NULL;
529 /* Copy structure isds_BirthInfo recursively */
530 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
531 const struct isds_BirthInfo *template) {
532 struct isds_BirthInfo *new = NULL;
534 if (!template) return NULL;
536 new = calloc(1, sizeof(*new));
537 if (!new) return NULL;
539 FLATDUP_OR_ERROR(new->biDate, template->biDate);
540 STRDUP_OR_ERROR(new->biCity, template->biCity);
541 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
542 STRDUP_OR_ERROR(new->biState, template->biState);
544 return new;
546 error:
547 isds_BirthInfo_free(&new);
548 return NULL;
552 /* Copy structure isds_Address recursively */
553 struct isds_Address *isds_Address_duplicate(
554 const struct isds_Address *src) {
555 struct isds_Address *new = NULL;
557 if (!src) return NULL;
559 new = calloc(1, sizeof(*new));
560 if (!new) return NULL;
562 STRDUP_OR_ERROR(new->adCity, src->adCity);
563 STRDUP_OR_ERROR(new->adStreet, src->adStreet);
564 STRDUP_OR_ERROR(new->adNumberInStreet, src->adNumberInStreet);
565 STRDUP_OR_ERROR(new->adNumberInMunicipality,
566 src->adNumberInMunicipality);
567 STRDUP_OR_ERROR(new->adZipCode, src->adZipCode);
568 STRDUP_OR_ERROR(new->adState, src->adState);
570 return new;
572 error:
573 isds_Address_free(&new);
574 return NULL;
578 /* Copy structure isds_DbOwnerInfo recursively */
579 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
580 const struct isds_DbOwnerInfo *src) {
581 struct isds_DbOwnerInfo *new = NULL;
582 if (!src) return NULL;
584 new = calloc(1, sizeof(*new));
585 if (!new) return NULL;
587 STRDUP_OR_ERROR(new->dbID, src->dbID);
588 FLATDUP_OR_ERROR(new->dbType, src->dbType);
589 STRDUP_OR_ERROR(new->ic, src->ic);
591 if (src->personName) {
592 if (!(new->personName =
593 isds_PersonName_duplicate(src->personName)))
594 goto error;
597 STRDUP_OR_ERROR(new->firmName, src->firmName);
599 if (src->birthInfo) {
600 if (!(new->birthInfo =
601 isds_BirthInfo_duplicate(src->birthInfo)))
602 goto error;
605 if (src->address) {
606 if (!(new->address = isds_Address_duplicate(src->address)))
607 goto error;
610 STRDUP_OR_ERROR(new->nationality, src->nationality);
611 STRDUP_OR_ERROR(new->email, src->email);
612 STRDUP_OR_ERROR(new->telNumber, src->telNumber);
613 STRDUP_OR_ERROR(new->identifier, src->identifier);
614 STRDUP_OR_ERROR(new->registryCode, src->registryCode);
615 FLATDUP_OR_ERROR(new->dbState, src->dbState);
616 FLATDUP_OR_ERROR(new->dbEffectiveOVM, src->dbEffectiveOVM);
617 FLATDUP_OR_ERROR(new->dbOpenAddressing, src->dbOpenAddressing);
619 return new;
621 error:
622 isds_DbOwnerInfo_free(&new);
623 return NULL;
627 /* Copy structure isds_DbUserInfo recursively */
628 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
629 const struct isds_DbUserInfo *src) {
630 struct isds_DbUserInfo *new = NULL;
631 if (!src) return NULL;
633 new = calloc(1, sizeof(*new));
634 if (!new) return NULL;
636 STRDUP_OR_ERROR(new->userID, src->userID);
637 FLATDUP_OR_ERROR(new->userType, src->userType);
638 FLATDUP_OR_ERROR(new->userPrivils, src->userPrivils);
640 if (src->personName) {
641 if (!(new->personName =
642 isds_PersonName_duplicate(src->personName)))
643 goto error;
646 if (src->address) {
647 if (!(new->address = isds_Address_duplicate(src->address)))
648 goto error;
651 FLATDUP_OR_ERROR(new->biDate, src->biDate);
652 STRDUP_OR_ERROR(new->ic, src->ic);
653 STRDUP_OR_ERROR(new->firmName, src->firmName);
654 STRDUP_OR_ERROR(new->caStreet, src->caStreet);
655 STRDUP_OR_ERROR(new->caCity, src->caCity);
656 STRDUP_OR_ERROR(new->caZipCode, src->caZipCode);
657 STRDUP_OR_ERROR(new->caState, src->caState);
658 STRDUP_OR_ERROR(new->aifo_ticket, src->aifo_ticket);
660 return new;
662 error:
663 isds_DbUserInfo_free(&new);
664 return NULL;
667 #undef FLATDUP_OR_ERROR
668 #undef STRDUP_OR_ERROR
671 /* Logs libxml2 errors. Should be registered to libxml2 library.
672 * @ctx is unused currently
673 * @msg is printf-like formated message from libxml2 (UTF-8?)
674 * @... are variadic arguments for @msg */
675 static void log_xml(void *ctx, const char *msg, ...) {
676 va_list ap;
677 char *text = NULL;
679 /* Silent warning for unused function argument.
680 * The prototype is an API of libxml2's xmlSetGenericErrorFunc(). */
681 (void)ctx;
683 if (!msg) return;
685 va_start(ap, msg);
686 isds_vasprintf(&text, msg, ap);
687 va_end(ap);
689 if (text)
690 isds_log(ILF_XML, ILL_ERR, "%s", text);
691 free(text);
695 /* Initialize ISDS library.
696 * Global function, must be called before other functions.
697 * If it fails you can not use ISDS library and must call isds_cleanup() to
698 * free partially initialized global variables. */
699 isds_error isds_init(void) {
700 /* NULL global variables */
701 log_facilities = ILF_ALL;
702 log_level = ILL_WARNING;
703 log_callback = NULL;
704 log_callback_data = NULL;
706 #if ENABLE_NLS
707 /* Initialize gettext */
708 bindtextdomain(PACKAGE, LOCALEDIR);
709 #endif
711 #if HAVE_LIBCURL
712 /* Initialize CURL */
713 if (curl_global_init(CURL_GLOBAL_ALL)) {
714 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
715 return IE_ERROR;
717 #endif /* HAVE_LIBCURL */
719 /* Initialise cryptographic back-ends. */
720 if (IE_SUCCESS != _isds_init_crypto()) {
721 isds_log(ILF_ISDS, ILL_CRIT,
722 _("Initialization of cryptographic back-end failed\n"));
723 return IE_ERROR;
726 /* This can _exit() current program. Find not so assertive check. */
727 LIBXML_TEST_VERSION;
728 xmlSetGenericErrorFunc(NULL, log_xml);
730 /* Check expat */
731 if (_isds_init_expat(&version_expat)) {
732 isds_log(ILF_ISDS, ILL_CRIT,
733 _("expat library initialization failed\n"));
734 return IE_ERROR;
737 /* Allocate global variables */
740 return IE_SUCCESS;
744 /* Deinitialize ISDS library.
745 * Global function, must be called as last library function. */
746 isds_error isds_cleanup(void) {
747 /* XML */
748 xmlCleanupParser();
750 #if HAVE_LIBCURL
751 /* Curl */
752 curl_global_cleanup();
753 #endif
755 return IE_SUCCESS;
759 /* Return version string of this library. Version of dependencies can be
760 * embedded. Do no try to parse it. You must free it. */
761 char *isds_version(void) {
762 char *buffer = NULL;
764 isds_asprintf(&buffer,
765 #if HAVE_LIBCURL
766 # ifndef USE_OPENSSL_BACKEND
767 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
768 # else
769 _("%s (%s, %s, %s, libxml2 %s)"),
770 # endif
771 #else
772 # ifndef USE_OPENSSL_BACKEND
773 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
774 # else
775 _("%s (%s, %s, libxml2 %s)"),
776 # endif
777 #endif
778 PACKAGE_VERSION,
779 #if HAVE_LIBCURL
780 curl_version(),
781 #endif
782 #ifndef USE_OPENSSL_BACKEND
783 version_gpgme, version_gcrypt,
784 #else
785 version_openssl,
786 #endif
787 version_expat, xmlParserVersion);
788 return buffer;
792 /* Return text description of ISDS error */
793 const char *isds_strerror(const isds_error error) {
794 switch (error) {
795 case IE_SUCCESS:
796 return(_("Success")); break;
797 case IE_ERROR:
798 return(_("Unspecified error")); break;
799 case IE_NOTSUP:
800 return(_("Not supported")); break;
801 case IE_INVAL:
802 return(_("Invalid value")); break;
803 case IE_INVALID_CONTEXT:
804 return(_("Invalid context")); break;
805 case IE_NOT_LOGGED_IN:
806 return(_("Not logged in")); break;
807 case IE_CONNECTION_CLOSED:
808 return(_("Connection closed")); break;
809 case IE_TIMED_OUT:
810 return(_("Timed out")); break;
811 case IE_NOEXIST:
812 return(_("Not exist")); break;
813 case IE_NOMEM:
814 return(_("Out of memory")); break;
815 case IE_NETWORK:
816 return(_("Network problem")); break;
817 case IE_HTTP:
818 return(_("HTTP problem")); break;
819 case IE_SOAP:
820 return(_("SOAP problem")); break;
821 case IE_XML:
822 return(_("XML problem")); break;
823 case IE_ISDS:
824 return(_("ISDS server problem")); break;
825 case IE_ENUM:
826 return(_("Invalid enum value")); break;
827 case IE_DATE:
828 return(_("Invalid date value")); break;
829 case IE_2BIG:
830 return(_("Too big")); break;
831 case IE_2SMALL:
832 return(_("Too small")); break;
833 case IE_NOTUNIQ:
834 return(_("Value not unique")); break;
835 case IE_NOTEQUAL:
836 return(_("Values not equal")); break;
837 case IE_PARTIAL_SUCCESS:
838 return(_("Some suboperations failed")); break;
839 case IE_ABORTED:
840 return(_("Operation aborted")); break;
841 case IE_SECURITY:
842 return(_("Security problem")); break;
843 default:
844 return(_("Unknown error"));
849 /* Create ISDS context.
850 * Each context can be used for different sessions to (possibly) different
851 * ISDS server with different credentials. */
852 struct isds_ctx *isds_ctx_create(void) {
853 struct isds_ctx *context;
854 context = malloc(sizeof(*context));
855 if (context) memset(context, 0, sizeof(*context));
856 return context;
859 #if HAVE_LIBCURL
860 /* Close possibly opened connection to Czech POINT document deposit without
861 * resetting long_message buffer.
862 * XXX: Do not use czp_close_connection() if you do not want to destroy log
863 * message.
864 * @context is Czech POINT session context. */
865 static isds_error czp_do_close_connection(struct isds_ctx *context) {
866 if (!context) return IE_INVALID_CONTEXT;
867 _isds_close_connection(context);
868 return IE_SUCCESS;
872 /* Discard credentials.
873 * @context is ISDS context
874 * @discard_saved_username is true for removing saved username, false for
875 * keeping it.
876 * Only that. It does not cause log out, connection close or similar. */
877 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context,
878 _Bool discard_saved_username) {
879 if(!context) return IE_INVALID_CONTEXT;
881 if (context->username) {
882 memset(context->username, 0, strlen(context->username));
883 zfree(context->username);
885 if (context->password) {
886 memset(context->password, 0, strlen(context->password));
887 zfree(context->password);
889 isds_pki_credentials_free(&context->pki_credentials);
890 if (discard_saved_username && context->saved_username) {
891 memset(context->saved_username, 0, strlen(context->saved_username));
892 zfree(context->saved_username);
895 return IE_SUCCESS;
897 #endif /* HAVE_LIBCURL */
900 /* Destroy ISDS context and free memory.
901 * @context will be NULLed on success. */
902 isds_error isds_ctx_free(struct isds_ctx **context) {
903 if (!context || !*context) {
904 return IE_INVALID_CONTEXT;
907 #if HAVE_LIBCURL
908 /* Discard credentials and close connection */
909 switch ((*context)->type) {
910 case CTX_TYPE_NONE: break;
911 case CTX_TYPE_ISDS: isds_logout(*context); break;
912 case CTX_TYPE_CZP:
913 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
914 czp_do_close_connection(*context); break;
917 /* For sure */
918 _isds_discard_credentials(*context, 1);
920 /* Free other structures */
921 free((*context)->url);
922 free((*context)->tls_verify_server);
923 free((*context)->tls_ca_file);
924 free((*context)->tls_ca_dir);
925 free((*context)->tls_crl_file);
926 #endif /* HAVE_LIBCURL */
927 free((*context)->long_message);
929 free(*context);
930 *context = NULL;
931 return IE_SUCCESS;
935 /* Return long message text produced by library function, e.g. detailed error
936 * message. Returned pointer is only valid until new library function is
937 * called for the same context. Could be NULL, especially if NULL context is
938 * supplied. Return string is locale encoded. */
939 char *isds_long_message(const struct isds_ctx *context) {
940 if (!context) return NULL;
941 return context->long_message;
945 /* Stores message into context' long_message buffer.
946 * Application can pick the message up using isds_long_message().
947 * NULL @message truncates the buffer but does not deallocate it.
948 * @message is coded in locale encoding */
949 _hidden isds_error isds_log_message(struct isds_ctx *context,
950 const char *message) {
951 char *buffer;
952 size_t length;
954 if (!context) return IE_INVALID_CONTEXT;
956 /* FIXME: Check for integer overflow */
957 length = 1 + ((message) ? strlen(message) : 0);
958 buffer = realloc(context->long_message, length);
959 if (!buffer) return IE_NOMEM;
961 if (message)
962 strcpy(buffer, message);
963 else
964 *buffer = '\0';
966 context->long_message = buffer;
967 return IE_SUCCESS;
971 /* Appends message into context' long_message buffer.
972 * Application can pick the message up using isds_long_message().
973 * NULL message has void effect. */
974 _hidden isds_error isds_append_message(struct isds_ctx *context,
975 const char *message) {
976 char *buffer;
977 size_t old_length, length;
979 if (!context) return IE_INVALID_CONTEXT;
980 if (!message) return IE_SUCCESS;
981 if (!context->long_message)
982 return isds_log_message(context, message);
984 old_length = strlen(context->long_message);
985 /* FIXME: Check for integer overflow */
986 length = 1 + old_length + strlen(message);
987 buffer = realloc(context->long_message, length);
988 if (!buffer) return IE_NOMEM;
990 strcpy(buffer + old_length, message);
992 context->long_message = buffer;
993 return IE_SUCCESS;
997 /* Stores formatted message into context' long_message buffer.
998 * Application can pick the message up using isds_long_message(). */
999 _hidden isds_error isds_printf_message(struct isds_ctx *context,
1000 const char *format, ...) {
1001 va_list ap;
1002 int length;
1004 if (!context) return IE_INVALID_CONTEXT;
1005 va_start(ap, format);
1006 length = isds_vasprintf(&(context->long_message), format, ap);
1007 va_end(ap);
1009 return (length < 0) ? IE_ERROR: IE_SUCCESS;
1013 /* Set logging up.
1014 * @facilities is bit mask of isds_log_facility values,
1015 * @level is verbosity level. */
1016 void isds_set_logging(const unsigned int facilities,
1017 const isds_log_level level) {
1018 log_facilities = facilities;
1019 log_level = level;
1023 /* Register callback function libisds calls when new global log message is
1024 * produced by library. Library logs to stderr by default.
1025 * @callback is function provided by application libisds will call. See type
1026 * definition for @callback argument explanation. Pass NULL to revert logging to
1027 * default behaviour.
1028 * @data is application specific data @callback gets as last argument */
1029 void isds_set_log_callback(isds_log_callback callback, void *data) {
1030 log_callback = callback;
1031 log_callback_data = data;
1035 /* Log @message in class @facility with log @level into global log. @message
1036 * is printf(3) formatting string, variadic arguments may be necessary.
1037 * For debugging purposes. */
1038 _hidden isds_error isds_log(const isds_log_facility facility,
1039 const isds_log_level level, const char *message, ...) {
1040 va_list ap;
1041 char *buffer = NULL;
1042 int length;
1044 if (level > log_level) return IE_SUCCESS;
1045 if (!(log_facilities & facility)) return IE_SUCCESS;
1046 if (!message) return IE_INVAL;
1048 if (log_callback) {
1049 /* Pass message to application supplied callback function */
1050 va_start(ap, message);
1051 length = isds_vasprintf(&buffer, message, ap);
1052 va_end(ap);
1054 if (length == -1) {
1055 return IE_ERROR;
1057 if (length > 0) {
1058 log_callback(facility, level, buffer, length, log_callback_data);
1060 free(buffer);
1061 } else {
1062 /* Default: Log it to stderr */
1063 va_start(ap, message);
1064 vfprintf(stderr, message, ap);
1065 va_end(ap);
1066 /* Line buffered printf is default.
1067 * fflush(stderr);*/
1070 return IE_SUCCESS;
1074 /* Set timeout in milliseconds for each network job like connecting to server
1075 * or sending message. Use 0 to disable timeout limits. */
1076 isds_error isds_set_timeout(struct isds_ctx *context,
1077 const unsigned int timeout) {
1078 if (!context) return IE_INVALID_CONTEXT;
1079 zfree(context->long_message);
1081 #if HAVE_LIBCURL
1082 context->timeout = timeout;
1084 if (context->curl) {
1085 CURLcode curl_err;
1087 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
1088 if (!curl_err)
1089 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1090 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
1091 context->timeout);
1092 #else
1093 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
1094 context->timeout / 1000);
1095 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1096 if (curl_err) return IE_ERROR;
1099 return IE_SUCCESS;
1100 #else /* not HAVE_LIBCURL */
1101 return IE_NOTSUP;
1102 #endif
1106 /* Register callback function libisds calls periodically during HTTP data
1107 * transfer.
1108 * @context is session context
1109 * @callback is function provided by application libisds will call. See type
1110 * definition for @callback argument explanation.
1111 * @data is application specific data @callback gets as last argument */
1112 isds_error isds_set_progress_callback(struct isds_ctx *context,
1113 isds_progress_callback callback, void *data) {
1114 if (!context) return IE_INVALID_CONTEXT;
1115 zfree(context->long_message);
1117 #if HAVE_LIBCURL
1118 context->progress_callback = callback;
1119 context->progress_callback_data = data;
1121 return IE_SUCCESS;
1122 #else /* not HAVE_LIBCURL */
1123 return IE_NOTSUP;
1124 #endif
1128 /* Change context settings.
1129 * @context is context which setting will be applied to
1130 * @option is name of option. It determines the type of last argument. See
1131 * isds_option definition for more info.
1132 * @... is value of new setting. Type is determined by @option
1133 * */
1134 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1135 ...) {
1136 isds_error err = IE_SUCCESS;
1137 va_list ap;
1138 #if HAVE_LIBCURL
1139 char *pointer, *string;
1140 #endif
1142 if (!context) return IE_INVALID_CONTEXT;
1143 zfree(context->long_message);
1145 va_start(ap, option);
1147 #define REPLACE_VA_BOOLEAN(destination) { \
1148 if (!(destination)) { \
1149 (destination) = malloc(sizeof(*(destination))); \
1150 if (!(destination)) { \
1151 err = IE_NOMEM; goto leave; \
1154 *(destination) = (_Bool) !!va_arg(ap, int); \
1157 #define REPLACE_VA_STRING(destination) { \
1158 string = va_arg(ap, char *); \
1159 if (string) { \
1160 pointer = realloc((destination), 1 + strlen(string)); \
1161 if (!pointer) { err = IE_NOMEM; goto leave; } \
1162 strcpy(pointer, string); \
1163 (destination) = pointer; \
1164 } else { \
1165 free(destination); \
1166 (destination) = NULL; \
1170 switch (option) {
1171 case IOPT_TLS_VERIFY_SERVER:
1172 #if HAVE_LIBCURL
1173 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1174 #else
1175 err = IE_NOTSUP; goto leave;
1176 #endif
1177 break;
1178 case IOPT_TLS_CA_FILE:
1179 #if HAVE_LIBCURL
1180 REPLACE_VA_STRING(context->tls_ca_file);
1181 #else
1182 err = IE_NOTSUP; goto leave;
1183 #endif
1184 break;
1185 case IOPT_TLS_CA_DIRECTORY:
1186 #if HAVE_LIBCURL
1187 REPLACE_VA_STRING(context->tls_ca_dir);
1188 #else
1189 err = IE_NOTSUP; goto leave;
1190 #endif
1191 break;
1192 case IOPT_TLS_CRL_FILE:
1193 #if HAVE_LIBCURL
1194 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1195 REPLACE_VA_STRING(context->tls_crl_file);
1196 #else
1197 isds_log_message(context,
1198 _("Curl library does not support CRL definition"));
1199 err = IE_NOTSUP;
1200 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1201 #else
1202 err = IE_NOTSUP; goto leave;
1203 #endif /* not HAVE_LIBCURL */
1204 break;
1205 case IOPT_NORMALIZE_MIME_TYPE:
1206 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1207 break;
1209 default:
1210 err = IE_ENUM; goto leave;
1213 #undef REPLACE_VA_STRING
1214 #undef REPLACE_VA_BOOLEAN
1216 leave:
1217 va_end(ap);
1218 return err;
1222 #if HAVE_LIBCURL
1223 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1224 * Destination for NULL argument will not be touched.
1225 * Destination pointers must be freed before calling this function.
1226 * If @username is @context->saved_username, the saved_username will not be
1227 * replaced. The saved_username is clobbered only if context has set otp
1228 * member.
1229 * Return IE_SUCCESS on success. */
1230 static isds_error _isds_store_credentials(struct isds_ctx *context,
1231 const char *username, const char *password,
1232 const struct isds_pki_credentials *pki_credentials) {
1233 if (NULL == context) return IE_INVALID_CONTEXT;
1235 /* FIXME: mlock password
1236 * (I have a library) */
1238 if (username) {
1239 context->username = strdup(username);
1240 if (context->otp && context->saved_username != username)
1241 context->saved_username = strdup(username);
1243 if (password) {
1244 if (NULL == context->otp_credentials)
1245 context->password = strdup(password);
1246 else
1247 context->password = _isds_astrcat(password,
1248 context->otp_credentials->otp_code);
1250 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1252 if ((NULL != username && NULL == context->username) ||
1253 (NULL != password && NULL == context->password) ||
1254 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1255 (context->otp && NULL != context->username &&
1256 NULL == context->saved_username)) {
1257 return IE_NOMEM;
1260 return IE_SUCCESS;
1262 #endif
1265 /* Connect and log into ISDS server.
1266 * All required arguments will be copied, you do not have to keep them after
1267 * that.
1268 * ISDS supports six different authentication methods. Exact method is
1269 * selected on @username, @password, @pki_credentials, and @otp arguments:
1270 * - If @pki_credentials == NULL, @username and @password must be supplied
1271 * and then
1272 * - If @otp == NULL, simple authentication by username and password will
1273 * be proceeded.
1274 * - If @otp != NULL, authentication by username and password and OTP
1275 * will be used.
1276 * - If @pki_credentials != NULL, then
1277 * - If @username == NULL, only certificate will be used
1278 * - If @username != NULL, then
1279 * - If @password == NULL, then certificate will be used and
1280 * @username shifts meaning to box ID. This is used for hosted
1281 * services.
1282 * - Otherwise all three arguments will be used.
1283 * Please note, that different cases require different certificate type
1284 * (system qualified one or commercial non qualified one). This library
1285 * does not check such political issues. Please see ISDS Specification
1286 * for more details.
1287 * @url is base address of ISDS web service. Pass extern isds_locator
1288 * variable to use production ISDS instance without client certificate
1289 * authentication (or extern isds_cert_locator with client certificate
1290 * authentication or extern isds_otp_locators with OTP authentication).
1291 * Passing NULL has the same effect, autoselection between isds_locator,
1292 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1293 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1294 * isds_otp_testing_locator) variable to select testing instance.
1295 * @username is user name of ISDS user or box ID
1296 * @password is user's secret password
1297 * @pki_credentials defines public key cryptographic material to use in client
1298 * authentication.
1299 * @otp selects one-time password authentication method to use, defines OTP
1300 * code (if known) and returns fine grade resolution of OTP procedure.
1301 * @return:
1302 * IE_SUCCESS if authentication succeeds
1303 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1304 * requested, fine grade reason will be set into @otp->resolution. Error
1305 * message from server can be obtained by isds_long_message() call.
1306 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1307 * server has sent OTP code through side channel. Application is expected to
1308 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1309 * this call to complete second phase of TOTP authentication;
1310 * or other appropriate error. */
1311 isds_error isds_login(struct isds_ctx *context, const char *url,
1312 const char *username, const char *password,
1313 const struct isds_pki_credentials *pki_credentials,
1314 struct isds_otp *otp) {
1315 #if HAVE_LIBCURL
1316 isds_error err = IE_NOT_LOGGED_IN;
1317 isds_error soap_err;
1318 xmlNsPtr isds_ns = NULL;
1319 xmlNodePtr request = NULL;
1320 #endif /* HAVE_LIBCURL */
1322 if (!context) return IE_INVALID_CONTEXT;
1323 zfree(context->long_message);
1325 #if HAVE_LIBCURL
1326 /* Close connection if already logged in */
1327 if (context->curl) {
1328 _isds_close_connection(context);
1331 /* Store configuration */
1332 context->type = CTX_TYPE_ISDS;
1333 zfree(context->url);
1335 /* Mangle base URI according to requested authentication method */
1336 if (NULL == pki_credentials) {
1337 isds_log(ILF_SEC, ILL_INFO,
1338 _("Selected authentication method: no certificate, "
1339 "username and password\n"));
1340 if (!username || !password) {
1341 isds_log_message(context,
1342 _("Both username and password must be supplied"));
1343 return IE_INVAL;
1345 context->otp_credentials = otp;
1346 context->otp = (NULL != context->otp_credentials);
1348 if (!context->otp) {
1349 /* Default locator is official system (without certificate or
1350 * OTP) */
1351 context->url = strdup((NULL != url) ? url : isds_locator);
1352 } else {
1353 const char *authenticator_uri = NULL;
1354 if (!url) url = isds_otp_locator;
1355 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1356 switch (context->otp_credentials->method) {
1357 case OTP_HMAC:
1358 isds_log(ILF_SEC, ILL_INFO,
1359 _("Selected authentication method: "
1360 "HMAC-based one-time password\n"));
1361 authenticator_uri =
1362 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1363 break;
1364 case OTP_TIME:
1365 isds_log(ILF_SEC, ILL_INFO,
1366 _("Selected authentication method: "
1367 "Time-based one-time password\n"));
1368 if (context->otp_credentials->otp_code == NULL) {
1369 isds_log(ILF_SEC, ILL_INFO,
1370 _("OTP code has not been provided by "
1371 "application, requesting server for "
1372 "new one.\n"));
1373 authenticator_uri =
1374 "%1$sas/processLogin?type=totp&sendSms=true&"
1375 "uri=%1$sapps/";
1376 } else {
1377 isds_log(ILF_SEC, ILL_INFO,
1378 _("OTP code has been provided by "
1379 "application, not requesting server "
1380 "for new one.\n"));
1381 authenticator_uri =
1382 "%1$sas/processLogin?type=totp&"
1383 "uri=%1$sapps/";
1385 break;
1386 default:
1387 isds_log_message(context,
1388 _("Unknown one-time password authentication "
1389 "method requested by application"));
1390 return IE_ENUM;
1392 if (-1 == isds_asprintf(&context->url, authenticator_uri, url))
1393 return IE_NOMEM;
1395 } else {
1396 /* Default locator is official system (with client certificate) */
1397 context->otp = 0;
1398 context->otp_credentials = NULL;
1399 if (!url) url = isds_cert_locator;
1401 if (!username) {
1402 isds_log(ILF_SEC, ILL_INFO,
1403 _("Selected authentication method: system certificate, "
1404 "no username and no password\n"));
1405 password = NULL;
1406 context->url = _isds_astrcat(url, "cert/");
1407 } else {
1408 if (!password) {
1409 isds_log(ILF_SEC, ILL_INFO,
1410 _("Selected authentication method: system certificate, "
1411 "box ID and no password\n"));
1412 context->url = _isds_astrcat(url, "hspis/");
1413 } else {
1414 isds_log(ILF_SEC, ILL_INFO,
1415 _("Selected authentication method: commercial "
1416 "certificate, username and password\n"));
1417 context->url = _isds_astrcat(url, "certds/");
1421 if (!(context->url))
1422 return IE_NOMEM;
1424 /* Prepare CURL handle */
1425 context->curl = curl_easy_init();
1426 if (!(context->curl))
1427 return IE_ERROR;
1429 /* Build log-in request */
1430 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1431 if (!request) {
1432 isds_log_message(context, _("Could not build ISDS log-in request"));
1433 return IE_ERROR;
1435 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1436 if(!isds_ns) {
1437 isds_log_message(context, _("Could not create ISDS name space"));
1438 xmlFreeNode(request);
1439 return IE_ERROR;
1441 xmlSetNs(request, isds_ns);
1443 /* Store credentials */
1444 _isds_discard_credentials(context, 1);
1445 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1446 _isds_discard_credentials(context, 1);
1447 xmlFreeNode(request);
1448 return IE_NOMEM;
1451 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1452 username, url);
1454 /* XXX: ISDS documentation does not specify response body for
1455 * DummyOperation request. However real server sends back
1456 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1457 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1458 * SOAP body content, e.g. the dmStatus element. */
1460 /* Send log-in request */
1461 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1463 if (context->otp) {
1464 /* Revert context URL from OTP authentication service URL to OTP web
1465 * service base URL for subsequent calls. Potenial isds_login() retry
1466 * will re-set context URL again. */
1467 zfree(context->url);
1468 context->url = _isds_astrcat(url, "apps/");
1469 if (context->url == NULL) {
1470 soap_err = IE_NOMEM;
1472 /* Detach pointer to OTP credentials from context */
1473 context->otp_credentials = NULL;
1476 /* Remove credentials */
1477 _isds_discard_credentials(context, 0);
1479 /* Destroy log-in request */
1480 xmlFreeNode(request);
1482 if (soap_err) {
1483 _isds_close_connection(context);
1484 return soap_err;
1487 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1488 * authentication succeeded if soap_err == IE_SUCCESS */
1489 err = IE_SUCCESS;
1491 if (!err)
1492 isds_log(ILF_ISDS, ILL_DEBUG,
1493 _("User %s has been logged into server %s successfully\n"),
1494 username, url);
1495 return err;
1496 #else /* not HAVE_LIBCURL */
1497 return IE_NOTSUP;
1498 #endif
1502 /* Log out from ISDS server discards credentials and connection configuration. */
1503 isds_error isds_logout(struct isds_ctx *context) {
1504 if (!context) return IE_INVALID_CONTEXT;
1505 zfree(context->long_message);
1507 #if HAVE_LIBCURL
1508 if (context->curl) {
1509 if (context->otp) {
1510 isds_error err = _isds_invalidate_otp_cookie(context);
1511 if (err) return err;
1514 /* Close connection */
1515 _isds_close_connection(context);
1517 /* Discard credentials for sure. They should not survive isds_login(),
1518 * even successful .*/
1519 _isds_discard_credentials(context, 1);
1521 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1522 } else {
1523 _isds_discard_credentials(context, 1);
1525 zfree(context->url);
1526 return IE_SUCCESS;
1527 #else /* not HAVE_LIBCURL */
1528 return IE_NOTSUP;
1529 #endif
1533 /* Verify connection to ISDS is alive and server is responding.
1534 * Send dummy request to ISDS and expect dummy response. */
1535 isds_error isds_ping(struct isds_ctx *context) {
1536 #if HAVE_LIBCURL
1537 isds_error soap_err;
1538 xmlNsPtr isds_ns = NULL;
1539 xmlNodePtr request = NULL;
1540 #endif /* HAVE_LIBCURL */
1542 if (!context) return IE_INVALID_CONTEXT;
1543 zfree(context->long_message);
1545 #if HAVE_LIBCURL
1546 /* Check if connection is established */
1547 if (!context->curl) return IE_CONNECTION_CLOSED;
1550 /* Build dummy request */
1551 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1552 if (!request) {
1553 isds_log_message(context, _("Could build ISDS dummy request"));
1554 return IE_ERROR;
1556 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1557 if(!isds_ns) {
1558 isds_log_message(context, _("Could not create ISDS name space"));
1559 xmlFreeNode(request);
1560 return IE_ERROR;
1562 xmlSetNs(request, isds_ns);
1564 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1566 /* XXX: ISDS documentation does not specify response body for
1567 * DummyOperation request. However real server sends back
1568 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1569 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1570 * SOAP body content, e.g. the dmStatus element. */
1572 /* Send dummy request */
1573 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1575 /* Destroy log-in request */
1576 xmlFreeNode(request);
1578 if (soap_err) {
1579 isds_log(ILF_ISDS, ILL_DEBUG,
1580 _("ISDS server could not be contacted\n"));
1581 return soap_err;
1584 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1585 * authentication succeeded if soap_err == IE_SUCCESS */
1588 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1590 return IE_SUCCESS;
1591 #else /* not HAVE_LIBCURL */
1592 return IE_NOTSUP;
1593 #endif
1597 /* Send bogus request to ISDS.
1598 * Just for test purposes */
1599 isds_error isds_bogus_request(struct isds_ctx *context) {
1600 #if HAVE_LIBCURL
1601 isds_error err;
1602 xmlNsPtr isds_ns = NULL;
1603 xmlNodePtr request = NULL;
1604 xmlDocPtr response = NULL;
1605 xmlChar *code = NULL, *message = NULL;
1606 #endif
1608 if (!context) return IE_INVALID_CONTEXT;
1609 zfree(context->long_message);
1611 #if HAVE_LIBCURL
1612 /* Check if connection is established */
1613 if (!context->curl) {
1614 /* Testing printf message */
1615 isds_printf_message(context, "%s", _("I said connection closed"));
1616 return IE_CONNECTION_CLOSED;
1620 /* Build dummy request */
1621 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1622 if (!request) {
1623 isds_log_message(context, _("Could build ISDS bogus request"));
1624 return IE_ERROR;
1626 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1627 if(!isds_ns) {
1628 isds_log_message(context, _("Could not create ISDS name space"));
1629 xmlFreeNode(request);
1630 return IE_ERROR;
1632 xmlSetNs(request, isds_ns);
1634 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1636 /* Sent bogus request */
1637 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1639 /* Destroy request */
1640 xmlFreeNode(request);
1642 if (err) {
1643 isds_log(ILF_ISDS, ILL_DEBUG,
1644 _("Processing ISDS response on bogus request failed\n"));
1645 xmlFreeDoc(response);
1646 return err;
1649 /* Check for response status */
1650 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1651 &code, &message, NULL);
1652 if (err) {
1653 isds_log(ILF_ISDS, ILL_DEBUG,
1654 _("ISDS response on bogus request is missing status\n"));
1655 free(code);
1656 free(message);
1657 xmlFreeDoc(response);
1658 return err;
1660 if (xmlStrcmp(code, BAD_CAST "0000")) {
1661 char *code_locale = _isds_utf82locale((char*)code);
1662 char *message_locale = _isds_utf82locale((char*)message);
1663 isds_log(ILF_ISDS, ILL_DEBUG,
1664 _("Server refused bogus request (code=%s, message=%s)\n"),
1665 code_locale, message_locale);
1666 /* XXX: Literal error messages from ISDS are Czech messages
1667 * (English sometimes) in UTF-8. It's hard to catch them for
1668 * translation. Successfully gettextized would return in locale
1669 * encoding, unsuccessfully translated would pass in UTF-8. */
1670 isds_log_message(context, message_locale);
1671 free(code_locale);
1672 free(message_locale);
1673 free(code);
1674 free(message);
1675 xmlFreeDoc(response);
1676 return IE_ISDS;
1680 free(code);
1681 free(message);
1682 xmlFreeDoc(response);
1684 isds_log(ILF_ISDS, ILL_DEBUG,
1685 _("Bogus message accepted by server. This should not happen.\n"));
1687 return IE_SUCCESS;
1688 #else /* not HAVE_LIBCURL */
1689 return IE_NOTSUP;
1690 #endif
1694 #if HAVE_LIBCURL
1695 /* Serialize XML subtree to buffer preserving XML indentation.
1696 * @context is session context
1697 * @subtree is XML element to be serialized (with children)
1698 * @buffer is automatically reallocated buffer where serialize to
1699 * @length is size of serialized stream in bytes
1700 * @return standard error code, free @buffer in case of error */
1701 static isds_error serialize_subtree(struct isds_ctx *context,
1702 xmlNodePtr subtree, void **buffer, size_t *length) {
1703 isds_error err = IE_SUCCESS;
1704 xmlBufferPtr xml_buffer = NULL;
1705 xmlSaveCtxtPtr save_ctx = NULL;
1706 xmlDocPtr subtree_doc = NULL;
1707 xmlNodePtr subtree_copy;
1708 xmlNsPtr isds_ns;
1709 void *new_buffer;
1711 if (!context) return IE_INVALID_CONTEXT;
1712 if (!buffer) return IE_INVAL;
1713 zfree(*buffer);
1714 if (!subtree || !length) return IE_INVAL;
1716 /* Make temporary XML document with @subtree root element */
1717 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1718 * It can result in not well-formed on invalid XML tree (e.g. name space
1719 * prefix definition can miss. */
1720 /*FIXME */
1722 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1723 if (!subtree_doc) {
1724 isds_log_message(context, _("Could not build temporary document"));
1725 err = IE_ERROR;
1726 goto leave;
1729 /* XXX: Copy subtree and attach the copy to document.
1730 * One node can not bee attached into more document at the same time.
1731 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1732 * automatically.
1733 * XXX: Check xmlSaveTree() too. */
1734 subtree_copy = xmlCopyNodeList(subtree);
1735 if (!subtree_copy) {
1736 isds_log_message(context, _("Could not copy subtree"));
1737 err = IE_ERROR;
1738 goto leave;
1740 xmlDocSetRootElement(subtree_doc, subtree_copy);
1742 /* Only this way we get namespace definition as @xmlns:isds,
1743 * otherwise we get namespace prefix without definition */
1744 /* FIXME: Don't overwrite original default namespace */
1745 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1746 if(!isds_ns) {
1747 isds_log_message(context, _("Could not create ISDS name space"));
1748 err = IE_ERROR;
1749 goto leave;
1751 xmlSetNs(subtree_copy, isds_ns);
1754 /* Serialize the document into buffer */
1755 xml_buffer = xmlBufferCreate();
1756 if (!xml_buffer) {
1757 isds_log_message(context, _("Could not create xmlBuffer"));
1758 err = IE_ERROR;
1759 goto leave;
1761 /* Last argument 0 means to not format the XML tree */
1762 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1763 if (!save_ctx) {
1764 isds_log_message(context, _("Could not create XML serializer"));
1765 err = IE_ERROR;
1766 goto leave;
1768 /* XXX: According LibXML documentation, this function does not return
1769 * meaningful value yet */
1770 xmlSaveDoc(save_ctx, subtree_doc);
1771 if (-1 == xmlSaveFlush(save_ctx)) {
1772 isds_log_message(context,
1773 _("Could not serialize XML subtree"));
1774 err = IE_ERROR;
1775 goto leave;
1777 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1778 * even after xmlSaveFlush(). Thus close it here */
1779 xmlSaveClose(save_ctx); save_ctx = NULL;
1782 /* Store and detach buffer from xml_buffer */
1783 *buffer = xml_buffer->content;
1784 *length = xml_buffer->use;
1785 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1787 /* Shrink buffer */
1788 new_buffer = realloc(*buffer, *length);
1789 if (new_buffer) *buffer = new_buffer;
1791 leave:
1792 if (err) {
1793 zfree(*buffer);
1794 *length = 0;
1797 xmlSaveClose(save_ctx);
1798 xmlBufferFree(xml_buffer);
1799 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1800 return err;
1802 #endif /* HAVE_LIBCURL */
1805 #if 0
1806 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1807 * @context is session context
1808 * @document is original document where @nodeset points to
1809 * @nodeset is XPath node set to dump (recursively)
1810 * @buffer is automatically reallocated buffer where serialize to
1811 * @length is size of serialized stream in bytes
1812 * @return standard error code, free @buffer in case of error */
1813 static isds_error dump_nodeset(struct isds_ctx *context,
1814 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1815 void **buffer, size_t *length) {
1816 isds_error err = IE_SUCCESS;
1817 xmlBufferPtr xml_buffer = NULL;
1818 void *new_buffer;
1820 if (!context) return IE_INVALID_CONTEXT;
1821 if (!buffer) return IE_INVAL;
1822 zfree(*buffer);
1823 if (!document || !nodeset || !length) return IE_INVAL;
1824 *length = 0;
1826 /* Empty node set results into NULL buffer */
1827 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1828 goto leave;
1831 /* Resulting the document into buffer */
1832 xml_buffer = xmlBufferCreate();
1833 if (!xml_buffer) {
1834 isds_log_message(context, _("Could not create xmlBuffer"));
1835 err = IE_ERROR;
1836 goto leave;
1839 /* Iterate over all nodes */
1840 for (int i = 0; i < nodeset->nodeNr; i++) {
1841 /* Serialize node.
1842 * XXX: xmlNodeDump() appends to xml_buffer. */
1843 if (-1 ==
1844 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1845 isds_log_message(context, _("Could not dump XML node"));
1846 err = IE_ERROR;
1847 goto leave;
1851 /* Store and detach buffer from xml_buffer */
1852 *buffer = xml_buffer->content;
1853 *length = xml_buffer->use;
1854 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1856 /* Shrink buffer */
1857 new_buffer = realloc(*buffer, *length);
1858 if (new_buffer) *buffer = new_buffer;
1861 leave:
1862 if (err) {
1863 zfree(*buffer);
1864 *length = 0;
1867 xmlBufferFree(xml_buffer);
1868 return err;
1870 #endif
1872 #if 0
1873 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1874 * @context is session context
1875 * @document is original document where @nodeset points to
1876 * @nodeset is XPath node set to dump (recursively)
1877 * @buffer is automatically reallocated buffer where serialize to
1878 * @length is size of serialized stream in bytes
1879 * @return standard error code, free @buffer in case of error */
1880 static isds_error dump_nodeset(struct isds_ctx *context,
1881 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1882 void **buffer, size_t *length) {
1883 isds_error err = IE_SUCCESS;
1884 xmlBufferPtr xml_buffer = NULL;
1885 xmlSaveCtxtPtr save_ctx = NULL;
1886 void *new_buffer;
1888 if (!context) return IE_INVALID_CONTEXT;
1889 if (!buffer) return IE_INVAL;
1890 zfree(*buffer);
1891 if (!document || !nodeset || !length) return IE_INVAL;
1892 *length = 0;
1894 /* Empty node set results into NULL buffer */
1895 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1896 goto leave;
1899 /* Resulting the document into buffer */
1900 xml_buffer = xmlBufferCreate();
1901 if (!xml_buffer) {
1902 isds_log_message(context, _("Could not create xmlBuffer"));
1903 err = IE_ERROR;
1904 goto leave;
1906 if (xmlSubstituteEntitiesDefault(1)) {
1907 isds_log_message(context, _("Could not disable attribute escaping"));
1908 err = IE_ERROR;
1909 goto leave;
1911 /* Last argument means:
1912 * 0 to not format the XML tree
1913 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1914 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1915 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1916 if (!save_ctx) {
1917 isds_log_message(context, _("Could not create XML serializer"));
1918 err = IE_ERROR;
1919 goto leave;
1921 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1922 isds_log_message(context, _("Could not disable attribute escaping"));
1923 err = IE_ERROR;
1924 goto leave;
1928 /* Iterate over all nodes */
1929 for (int i = 0; i < nodeset->nodeNr; i++) {
1930 /* Serialize node.
1931 * XXX: xmlNodeDump() appends to xml_buffer. */
1932 /*if (-1 ==
1933 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1935 /* XXX: According LibXML documentation, this function does not return
1936 * meaningful value yet */
1937 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1938 if (-1 == xmlSaveFlush(save_ctx)) {
1939 isds_log_message(context,
1940 _("Could not serialize XML subtree"));
1941 err = IE_ERROR;
1942 goto leave;
1946 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1947 * even after xmlSaveFlush(). Thus close it here */
1948 xmlSaveClose(save_ctx); save_ctx = NULL;
1950 /* Store and detach buffer from xml_buffer */
1951 *buffer = xml_buffer->content;
1952 *length = xml_buffer->use;
1953 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1955 /* Shrink buffer */
1956 new_buffer = realloc(*buffer, *length);
1957 if (new_buffer) *buffer = new_buffer;
1959 leave:
1960 if (err) {
1961 zfree(*buffer);
1962 *length = 0;
1965 xmlSaveClose(save_ctx);
1966 xmlBufferFree(xml_buffer);
1967 return err;
1969 #endif
1972 #if HAVE_LIBCURL
1973 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1974 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1975 if (!string || !type) return IE_INVAL;
1977 if (!xmlStrcmp(string, BAD_CAST "FO"))
1978 *type = DBTYPE_FO;
1979 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1980 *type = DBTYPE_PFO;
1981 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1982 *type = DBTYPE_PFO_ADVOK;
1983 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1984 *type = DBTYPE_PFO_DANPOR;
1985 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1986 *type = DBTYPE_PFO_INSSPR;
1987 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1988 *type = DBTYPE_PO;
1989 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1990 *type = DBTYPE_PO_ZAK;
1991 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1992 *type = DBTYPE_PO_REQ;
1993 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1994 *type = DBTYPE_OVM;
1995 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1996 *type = DBTYPE_OVM_NOTAR;
1997 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1998 *type = DBTYPE_OVM_EXEKUT;
1999 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
2000 *type = DBTYPE_OVM_REQ;
2001 else
2002 return IE_ENUM;
2003 return IE_SUCCESS;
2007 /* Convert ISDS dbType enum @type to UTF-8 string.
2008 * @Return pointer to static string, or NULL if unknown enum value */
2009 static const xmlChar *isds_DbType2string(const isds_DbType type) {
2010 switch(type) {
2011 /* DBTYPE_SYSTEM and DBTYPE_OVM_MAIN are invalid values from point
2012 * of view of generic public SOAP interface. */
2013 case DBTYPE_FO: return(BAD_CAST "FO"); break;
2014 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
2015 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
2016 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
2017 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
2018 case DBTYPE_PO: return(BAD_CAST "PO"); break;
2019 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
2020 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
2021 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
2022 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
2023 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
2024 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
2025 default: return NULL; break;
2030 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2031 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
2032 if (!string || !type) return IE_INVAL;
2034 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2035 *type = USERTYPE_PRIMARY;
2036 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2037 *type = USERTYPE_ENTRUSTED;
2038 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2039 *type = USERTYPE_ADMINISTRATOR;
2040 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2041 *type = USERTYPE_OFFICIAL;
2042 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2043 *type = USERTYPE_OFFICIAL_CERT;
2044 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2045 *type = USERTYPE_LIQUIDATOR;
2046 else
2047 return IE_ENUM;
2048 return IE_SUCCESS;
2052 /* Convert ISDS userType enum @type to UTF-8 string.
2053 * @Return pointer to static string, or NULL if unknown enum value */
2054 static const xmlChar *isds_UserType2string(const isds_UserType type) {
2055 switch(type) {
2056 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
2057 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
2058 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
2059 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
2060 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
2061 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
2062 default: return NULL; break;
2067 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2068 static isds_error string2isds_sender_type(const xmlChar *string,
2069 isds_sender_type *type) {
2070 if (!string || !type) return IE_INVAL;
2072 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2073 *type = SENDERTYPE_PRIMARY;
2074 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2075 *type = SENDERTYPE_ENTRUSTED;
2076 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2077 *type = SENDERTYPE_ADMINISTRATOR;
2078 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2079 *type = SENDERTYPE_OFFICIAL;
2080 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2081 *type = SENDERTYPE_VIRTUAL;
2082 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2083 *type = SENDERTYPE_OFFICIAL_CERT;
2084 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2085 *type = SENDERTYPE_LIQUIDATOR;
2086 else
2087 return IE_ENUM;
2088 return IE_SUCCESS;
2092 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2093 static isds_error string2isds_payment_type(const xmlChar *string,
2094 isds_payment_type *type) {
2095 if (!string || !type) return IE_INVAL;
2097 if (!xmlStrcmp(string, BAD_CAST "K"))
2098 *type = PAYMENT_SENDER;
2099 else if (!xmlStrcmp(string, BAD_CAST "O"))
2100 *type = PAYMENT_RESPONSE;
2101 else if (!xmlStrcmp(string, BAD_CAST "G"))
2102 *type = PAYMENT_SPONSOR;
2103 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2104 *type = PAYMENT_SPONSOR_LIMITED;
2105 else if (!xmlStrcmp(string, BAD_CAST "D"))
2106 *type = PAYMENT_SPONSOR_EXTERNAL;
2107 else if (!xmlStrcmp(string, BAD_CAST "E"))
2108 *type = PAYMENT_STAMP;
2109 else
2110 return IE_ENUM;
2111 return IE_SUCCESS;
2115 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2116 * ciEventType is integer but we convert it from string representation
2117 * directly. */
2118 static isds_error string2isds_credit_event_type(const xmlChar *string,
2119 isds_credit_event_type *type) {
2120 if (!string || !type) return IE_INVAL;
2122 if (!xmlStrcmp(string, BAD_CAST "1"))
2123 *type = ISDS_CREDIT_CHARGED;
2124 else if (!xmlStrcmp(string, BAD_CAST "2"))
2125 *type = ISDS_CREDIT_DISCHARGED;
2126 else if (!xmlStrcmp(string, BAD_CAST "3"))
2127 *type = ISDS_CREDIT_MESSAGE_SENT;
2128 else if (!xmlStrcmp(string, BAD_CAST "4"))
2129 *type = ISDS_CREDIT_STORAGE_SET;
2130 else if (!xmlStrcmp(string, BAD_CAST "5"))
2131 *type = ISDS_CREDIT_EXPIRED;
2132 else
2133 return IE_ENUM;
2134 return IE_SUCCESS;
2138 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2139 * @Return pointer to static string, or NULL if unknown enum value */
2140 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2141 switch(type) {
2142 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2143 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2144 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2145 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2146 default: return NULL; break;
2151 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2152 * ISDSSearch2/searchType value.
2153 * @Return pointer to static string, or NULL if unknown enum value */
2154 static const xmlChar *isds_fulltext_target2string(
2155 const isds_fulltext_target type) {
2156 switch(type) {
2157 case FULLTEXT_ALL: return(BAD_CAST "GENERAL"); break;
2158 case FULLTEXT_ADDRESS: return(BAD_CAST "ADDRESS"); break;
2159 case FULLTEXT_IC: return(BAD_CAST "ICO"); break;
2160 case FULLTEXT_BOX_ID: return(BAD_CAST "DBID"); break;
2161 default: return NULL; break;
2164 #endif /* HAVE_LIBCURL */
2167 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2168 * @Return IE_ENUM if @string is not valid enum member */
2169 static isds_error string2isds_FileMetaType(const xmlChar *string,
2170 isds_FileMetaType *type) {
2171 if (!string || !type) return IE_INVAL;
2173 if (!xmlStrcmp(string, BAD_CAST "main"))
2174 *type = FILEMETATYPE_MAIN;
2175 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2176 *type = FILEMETATYPE_ENCLOSURE;
2177 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2178 *type = FILEMETATYPE_SIGNATURE;
2179 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2180 *type = FILEMETATYPE_META;
2181 else
2182 return IE_ENUM;
2183 return IE_SUCCESS;
2187 /* Convert UTF-8 @string to ISDS hash @algorithm.
2188 * @Return IE_ENUM if @string is not valid enum member */
2189 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2190 isds_hash_algorithm *algorithm) {
2191 if (!string || !algorithm) return IE_INVAL;
2193 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2194 *algorithm = HASH_ALGORITHM_MD5;
2195 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2196 *algorithm = HASH_ALGORITHM_SHA_1;
2197 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2198 *algorithm = HASH_ALGORITHM_SHA_224;
2199 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2200 *algorithm = HASH_ALGORITHM_SHA_256;
2201 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2202 *algorithm = HASH_ALGORITHM_SHA_384;
2203 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2204 *algorithm = HASH_ALGORITHM_SHA_512;
2205 else
2206 return IE_ENUM;
2207 return IE_SUCCESS;
2211 #if HAVE_LIBCURL
2212 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2213 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2214 if (!time || !string) return IE_INVAL;
2216 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2217 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2218 return IE_ERROR;
2220 return IE_SUCCESS;
2224 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2225 * respects the @time microseconds too. */
2226 static isds_error timeval2timestring(const struct timeval *time,
2227 xmlChar **string) {
2228 struct tm broken;
2229 time_t seconds_as_time_t;
2231 if (!time || !string) return IE_INVAL;
2233 /* MinGW32 GCC 4.8+ uses 64-bit time_t but time->tv_sec is defined as
2234 * 32-bit long in Microsoft API. Convert value to the type expected by
2235 * gmtime_r(). */
2236 seconds_as_time_t = time->tv_sec;
2237 if (!gmtime_r(&seconds_as_time_t, &broken)) return IE_DATE;
2238 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2240 /* TODO: small negative year should be formatted as "-0012". This is not
2241 * true for glibc "%04d". We should implement it.
2242 * time->tv_usec type is su_seconds_t which is required to be signed
2243 * integer to accomodate values from range [-1, 1000000].
2244 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2245 /* XXX: Do not format time->tv_usec as intmax_t because %jd is not
2246 * supported and PRIdMAX is broken on MingGW. We can use int32_t because
2247 * of the range check above. */
2248 if (-1 == isds_asprintf((char **) string,
2249 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRId32,
2250 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2251 broken.tm_hour, broken.tm_min, broken.tm_sec,
2252 (int32_t)time->tv_usec))
2253 return IE_ERROR;
2255 return IE_SUCCESS;
2257 #endif /* HAVE_LIBCURL */
2260 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2261 * It respects microseconds too. Microseconds are rounded half up.
2262 * In case of error, @time will be freed. */
2263 static isds_error timestring2timeval(const xmlChar *string,
2264 struct timeval **time) {
2265 struct tm broken;
2266 char *offset, *delim, *endptr;
2267 const int subsecond_resolution = 6;
2268 char subseconds[subsecond_resolution + 1];
2269 _Bool round_up = 0;
2270 int offset_hours, offset_minutes;
2271 int i;
2272 long int long_number;
2273 #ifdef _WIN32
2274 int tmp;
2275 #endif
2277 if (!time) return IE_INVAL;
2278 if (!string) {
2279 zfree(*time);
2280 return IE_INVAL;
2283 memset(&broken, 0, sizeof(broken));
2285 if (!*time) {
2286 *time = calloc(1, sizeof(**time));
2287 if (!*time) return IE_NOMEM;
2288 } else {
2289 memset(*time, 0, sizeof(**time));
2293 /* xsd:date is ISO 8601 string, thus ASCII */
2294 /*TODO: negative year */
2296 #ifdef _WIN32
2297 i = 0;
2298 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2299 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2300 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2301 &i)) < 6) {
2302 zfree(*time);
2303 return IE_DATE;
2306 broken.tm_year -= 1900;
2307 broken.tm_mon--;
2308 broken.tm_isdst = -1;
2309 offset = (char*)string + i;
2310 #else
2311 /* Parse date and time without subseconds and offset */
2312 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2313 if (!offset) {
2314 zfree(*time);
2315 return IE_DATE;
2317 #endif
2319 /* Get subseconds */
2320 if (*offset == '.' ) {
2321 offset++;
2323 /* Copy first 6 digits, pad it with zeros.
2324 * Current server implementation uses only millisecond resolution. */
2325 /* TODO: isdigit() is locale sensitive */
2326 for (i = 0;
2327 i < subsecond_resolution && isdigit(*offset);
2328 i++, offset++) {
2329 subseconds[i] = *offset;
2331 if (subsecond_resolution == i && isdigit(*offset)) {
2332 /* Check 7th digit for rounding */
2333 if (*offset >= '5') round_up = 1;
2334 offset++;
2336 for (; i < subsecond_resolution; i++) {
2337 subseconds[i] = '0';
2339 subseconds[subsecond_resolution] = '\0';
2341 /* Convert it into integer */
2342 long_number = strtol(subseconds, &endptr, 10);
2343 if (*endptr != '\0' || long_number == LONG_MIN ||
2344 long_number == LONG_MAX) {
2345 zfree(*time);
2346 return IE_DATE;
2348 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2349 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2350 * microseconds" and "the type shall be a signed integer capable of
2351 * storing values at least in the range [-1, 1000000]. */
2352 if (long_number < -1 || long_number >= 1000000) {
2353 zfree(*time);
2354 return IE_DATE;
2356 (*time)->tv_usec = long_number;
2358 /* Round the subseconds */
2359 if (round_up) {
2360 if (999999 == (*time)->tv_usec) {
2361 (*time)->tv_usec = 0;
2362 broken.tm_sec++;
2363 } else {
2364 (*time)->tv_usec++;
2368 /* move to the zone offset delimiter or signal NULL*/
2369 delim = strchr(offset, '-');
2370 if (!delim)
2371 delim = strchr(offset, '+');
2372 if (!delim)
2373 delim = strchr(offset, 'Z');
2374 offset = delim;
2377 /* Get zone offset */
2378 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2379 * "" equals to "Z" and it means UTC zone. */
2380 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2381 * colon separator */
2382 if (offset && (*offset == '-' || *offset == '+')) {
2383 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2384 zfree(*time);
2385 return IE_DATE;
2387 if (*offset == '+') {
2388 broken.tm_hour -= offset_hours;
2389 broken.tm_min -= offset_minutes;
2390 } else {
2391 broken.tm_hour += offset_hours;
2392 broken.tm_min += offset_minutes;
2396 /* Convert to time_t */
2397 (*time)->tv_sec = _isds_timegm(&broken);
2398 if ((*time)->tv_sec == (time_t) -1) {
2399 zfree(*time);
2400 return IE_DATE;
2403 return IE_SUCCESS;
2407 /* Convert unsigned int into isds_message_status.
2408 * @context is session context
2409 * @number is pointer to number value. NULL will be treated as invalid value.
2410 * @status is automatically reallocated status
2411 * @return IE_SUCCESS, or error code and free status */
2412 static isds_error uint2isds_message_status(struct isds_ctx *context,
2413 const unsigned long int *number, isds_message_status **status) {
2414 if (!context) return IE_INVALID_CONTEXT;
2415 if (!status) return IE_INVAL;
2417 free(*status); *status = NULL;
2418 if (!number) return IE_INVAL;
2420 if (*number < 1 || *number > 10) {
2421 isds_printf_message(context, _("Invalid message status value: %lu"),
2422 *number);
2423 return IE_ENUM;
2426 *status = malloc(sizeof(**status));
2427 if (!*status) return IE_NOMEM;
2429 **status = 1 << *number;
2430 return IE_SUCCESS;
2434 /* Convert event description string into isds_event members type and
2435 * description
2436 * @string is raw event description starting with event prefix
2437 * @event is structure where to store type and stripped description to
2438 * @return standard error code, unknown prefix is not classified as an error.
2439 * */
2440 static isds_error eventstring2event(const xmlChar *string,
2441 struct isds_event* event) {
2442 const xmlChar *known_prefixes[] = {
2443 BAD_CAST "EV0:",
2444 BAD_CAST "EV1:",
2445 BAD_CAST "EV2:",
2446 BAD_CAST "EV3:",
2447 BAD_CAST "EV4:",
2448 BAD_CAST "EV5:",
2449 BAD_CAST "EV11:",
2450 BAD_CAST "EV12:",
2451 BAD_CAST "EV13:"
2453 const isds_event_type types[] = {
2454 EVENT_ENTERED_SYSTEM,
2455 EVENT_ACCEPTED_BY_RECIPIENT,
2456 EVENT_ACCEPTED_BY_FICTION,
2457 EVENT_UNDELIVERABLE,
2458 EVENT_COMMERCIAL_ACCEPTED,
2459 EVENT_DELIVERED,
2460 EVENT_PRIMARY_LOGIN,
2461 EVENT_ENTRUSTED_LOGIN,
2462 EVENT_SYSCERT_LOGIN
2464 unsigned int index;
2465 size_t length;
2467 if (!string || !event) return IE_INVAL;
2469 if (!event->type) {
2470 event->type = malloc(sizeof(*event->type));
2471 if (!(event->type)) return IE_NOMEM;
2473 zfree(event->description);
2475 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2476 index++) {
2477 length = xmlUTF8Strlen(known_prefixes[index]);
2479 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2480 /* Prefix is known */
2481 *event->type = types[index];
2483 /* Strip prefix from description and spaces */
2484 /* TODO: Recognize all white spaces from UCS blank class and
2485 * operate on UTF-8 chars. */
2486 for (; string[length] != '\0' && string[length] == ' '; length++);
2487 event->description = strdup((char *) (string + length));
2488 if (!(event->description)) return IE_NOMEM;
2490 return IE_SUCCESS;
2494 /* Unknown event prefix.
2495 * XSD allows any string */
2496 char *string_locale = _isds_utf82locale((char *) string);
2497 isds_log(ILF_ISDS, ILL_WARNING,
2498 _("Unknown delivery info event prefix: %s\n"), string_locale);
2499 free(string_locale);
2501 *event->type = EVENT_UKNOWN;
2502 event->description = strdup((char *) string);
2503 if (!(event->description)) return IE_NOMEM;
2505 return IE_SUCCESS;
2509 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2510 * and leave label */
2511 #define EXTRACT_STRING(element, string) { \
2512 xmlXPathFreeObject(result); \
2513 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2514 if (NULL == (result)) { \
2515 err = IE_ERROR; \
2516 goto leave; \
2518 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2519 if (result->nodesetval->nodeNr > 1) { \
2520 isds_printf_message(context, _("Multiple %s element"), element); \
2521 err = IE_ERROR; \
2522 goto leave; \
2524 (string) = (char *) \
2525 xmlXPathCastNodeSetToString(result->nodesetval); \
2526 if (NULL == (string)) { \
2527 err = IE_ERROR; \
2528 goto leave; \
2533 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2535 char *string = NULL; \
2536 EXTRACT_STRING(element, string); \
2538 if (string) { \
2539 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2540 if (!(booleanPtr)) { \
2541 free(string); \
2542 err = IE_NOMEM; \
2543 goto leave; \
2546 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2547 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2548 *(booleanPtr) = 1; \
2549 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2550 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2551 *(booleanPtr) = 0; \
2552 else { \
2553 char *string_locale = _isds_utf82locale((char*)string); \
2554 isds_printf_message(context, \
2555 _("%s value is not valid boolean: %s"), \
2556 element, string_locale); \
2557 free(string_locale); \
2558 free(string); \
2559 err = IE_ERROR; \
2560 goto leave; \
2563 free(string); \
2567 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2569 char *string = NULL; \
2570 EXTRACT_STRING(element, string); \
2572 if (NULL == string) { \
2573 isds_printf_message(context, _("%s element is empty"), element); \
2574 err = IE_ERROR; \
2575 goto leave; \
2577 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2578 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2579 (boolean) = 1; \
2580 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2581 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2582 (boolean) = 0; \
2583 else { \
2584 char *string_locale = _isds_utf82locale((char*)string); \
2585 isds_printf_message(context, \
2586 _("%s value is not valid boolean: %s"), \
2587 element, string_locale); \
2588 free(string_locale); \
2589 free(string); \
2590 err = IE_ERROR; \
2591 goto leave; \
2594 free(string); \
2597 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2599 char *string = NULL; \
2600 EXTRACT_STRING(element, string); \
2601 if (string) { \
2602 long int number; \
2603 char *endptr; \
2605 number = strtol((char*)string, &endptr, 10); \
2607 if (*endptr != '\0') { \
2608 char *string_locale = _isds_utf82locale((char *)string); \
2609 isds_printf_message(context, \
2610 _("%s is not valid integer: %s"), \
2611 element, string_locale); \
2612 free(string_locale); \
2613 free(string); \
2614 err = IE_ISDS; \
2615 goto leave; \
2618 if (number == LONG_MIN || number == LONG_MAX) { \
2619 char *string_locale = _isds_utf82locale((char *)string); \
2620 isds_printf_message(context, \
2621 _("%s value out of range of long int: %s"), \
2622 element, string_locale); \
2623 free(string_locale); \
2624 free(string); \
2625 err = IE_ERROR; \
2626 goto leave; \
2629 free(string); string = NULL; \
2631 if (!(preallocated)) { \
2632 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2633 if (!(longintPtr)) { \
2634 err = IE_NOMEM; \
2635 goto leave; \
2638 *(longintPtr) = number; \
2642 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2644 char *string = NULL; \
2645 EXTRACT_STRING(element, string); \
2646 if (string) { \
2647 long int number; \
2648 char *endptr; \
2650 number = strtol((char*)string, &endptr, 10); \
2652 if (*endptr != '\0') { \
2653 char *string_locale = _isds_utf82locale((char *)string); \
2654 isds_printf_message(context, \
2655 _("%s is not valid integer: %s"), \
2656 element, string_locale); \
2657 free(string_locale); \
2658 free(string); \
2659 err = IE_ISDS; \
2660 goto leave; \
2663 if (number == LONG_MIN || number == LONG_MAX) { \
2664 char *string_locale = _isds_utf82locale((char *)string); \
2665 isds_printf_message(context, \
2666 _("%s value out of range of long int: %s"), \
2667 element, string_locale); \
2668 free(string_locale); \
2669 free(string); \
2670 err = IE_ERROR; \
2671 goto leave; \
2674 free(string); string = NULL; \
2675 if (number < 0) { \
2676 isds_printf_message(context, \
2677 _("%s value is negative: %ld"), element, number); \
2678 err = IE_ERROR; \
2679 goto leave; \
2682 if (!(preallocated)) { \
2683 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2684 if (!(ulongintPtr)) { \
2685 err = IE_NOMEM; \
2686 goto leave; \
2689 *(ulongintPtr) = number; \
2693 #define EXTRACT_DATE(element, tmPtr) { \
2694 char *string = NULL; \
2695 EXTRACT_STRING(element, string); \
2696 if (NULL != string) { \
2697 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2698 if (NULL == (tmPtr)) { \
2699 free(string); \
2700 err = IE_NOMEM; \
2701 goto leave; \
2703 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2704 if (err) { \
2705 if (err == IE_NOTSUP) { \
2706 err = IE_ISDS; \
2707 char *string_locale = _isds_utf82locale(string); \
2708 char *element_locale = _isds_utf82locale(element); \
2709 isds_printf_message(context, _("Invalid %s value: %s"), \
2710 element_locale, string_locale); \
2711 free(string_locale); \
2712 free(element_locale); \
2714 free(string); \
2715 goto leave; \
2717 free(string); \
2721 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2722 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2723 NULL); \
2724 if ((required) && (!string)) { \
2725 char *attribute_locale = _isds_utf82locale(attribute); \
2726 char *element_locale = \
2727 _isds_utf82locale((char *)xpath_ctx->node->name); \
2728 isds_printf_message(context, \
2729 _("Could not extract required %s attribute value from " \
2730 "%s element"), attribute_locale, element_locale); \
2731 free(element_locale); \
2732 free(attribute_locale); \
2733 err = IE_ERROR; \
2734 goto leave; \
2739 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2741 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2742 (xmlChar *) (string)); \
2743 if (!node) { \
2744 isds_printf_message(context, \
2745 _("Could not add %s child to %s element"), \
2746 element, (parent)->name); \
2747 err = IE_ERROR; \
2748 goto leave; \
2752 #define INSERT_STRING(parent, element, string) \
2753 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2755 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2757 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2758 else { INSERT_STRING(parent, element, "false"); } \
2761 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2763 if (booleanPtr) { \
2764 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2765 } else { \
2766 INSERT_STRING(parent, element, NULL); \
2770 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2771 if ((longintPtr)) { \
2772 /* FIXME: locale sensitive */ \
2773 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2774 err = IE_NOMEM; \
2775 goto leave; \
2777 INSERT_STRING(parent, element, buffer) \
2778 free(buffer); (buffer) = NULL; \
2779 } else { INSERT_STRING(parent, element, NULL) } \
2782 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2783 if ((ulongintPtr)) { \
2784 /* FIXME: locale sensitive */ \
2785 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2786 err = IE_NOMEM; \
2787 goto leave; \
2789 INSERT_STRING(parent, element, buffer) \
2790 free(buffer); (buffer) = NULL; \
2791 } else { INSERT_STRING(parent, element, NULL) } \
2794 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2796 /* FIXME: locale sensitive */ \
2797 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2798 err = IE_NOMEM; \
2799 goto leave; \
2801 INSERT_STRING(parent, element, buffer) \
2802 free(buffer); (buffer) = NULL; \
2805 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2806 * new attribute. */
2807 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2809 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2810 (xmlChar *) (string)); \
2811 if (!attribute_node) { \
2812 isds_printf_message(context, _("Could not add %s " \
2813 "attribute to %s element"), \
2814 (attribute), (parent)->name); \
2815 err = IE_ERROR; \
2816 goto leave; \
2820 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2821 if (string) { \
2822 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2823 if (length > (maximum)) { \
2824 isds_printf_message(context, \
2825 ngettext("%s has more than %d characters", \
2826 "%s has more than %d characters", (maximum)), \
2827 (name), (maximum)); \
2828 err = IE_2BIG; \
2829 goto leave; \
2831 if (length < (minimum)) { \
2832 isds_printf_message(context, \
2833 ngettext("%s has less than %d characters", \
2834 "%s has less than %d characters", (minimum)), \
2835 (name), (minimum)); \
2836 err = IE_2SMALL; \
2837 goto leave; \
2842 #define INSERT_ELEMENT(child, parent, element) \
2844 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2845 if (!(child)) { \
2846 isds_printf_message(context, \
2847 _("Could not add %s child to %s element"), \
2848 (element), (parent)->name); \
2849 err = IE_ERROR; \
2850 goto leave; \
2855 /* Find child element by name in given XPath context and switch context onto
2856 * it. The child must be uniq and must exist. Otherwise fails.
2857 * @context is ISDS context
2858 * @child is child element name
2859 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2860 * into it child. In error case, the @xpath_ctx keeps original value. */
2861 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2862 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2863 isds_error err = IE_SUCCESS;
2864 xmlXPathObjectPtr result = NULL;
2866 if (!context) return IE_INVALID_CONTEXT;
2867 if (!child || !xpath_ctx) return IE_INVAL;
2869 /* Find child */
2870 result = xmlXPathEvalExpression(child, xpath_ctx);
2871 if (!result) {
2872 err = IE_XML;
2873 goto leave;
2876 /* No match */
2877 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2878 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2879 char *child_locale = _isds_utf82locale((char*) child);
2880 isds_printf_message(context,
2881 _("%s element does not contain %s child"),
2882 parent_locale, child_locale);
2883 free(child_locale);
2884 free(parent_locale);
2885 err = IE_NOEXIST;
2886 goto leave;
2889 /* More matches */
2890 if (result->nodesetval->nodeNr > 1) {
2891 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2892 char *child_locale = _isds_utf82locale((char*) child);
2893 isds_printf_message(context,
2894 _("%s element contains multiple %s children"),
2895 parent_locale, child_locale);
2896 free(child_locale);
2897 free(parent_locale);
2898 err = IE_NOTUNIQ;
2899 goto leave;
2902 /* Switch context */
2903 xpath_ctx->node = result->nodesetval->nodeTab[0];
2905 leave:
2906 xmlXPathFreeObject(result);
2907 return err;
2912 #if HAVE_LIBCURL
2913 /* Find and convert XSD:gPersonName group in current node into structure
2914 * @context is ISDS context
2915 * @personName is automatically reallocated person name structure. If no member
2916 * value is found, will be freed.
2917 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2918 * elements
2919 * In case of error @personName will be freed. */
2920 static isds_error extract_gPersonName(struct isds_ctx *context,
2921 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2922 isds_error err = IE_SUCCESS;
2923 xmlXPathObjectPtr result = NULL;
2925 if (!context) return IE_INVALID_CONTEXT;
2926 if (!personName) return IE_INVAL;
2927 isds_PersonName_free(personName);
2928 if (!xpath_ctx) return IE_INVAL;
2931 *personName = calloc(1, sizeof(**personName));
2932 if (!*personName) {
2933 err = IE_NOMEM;
2934 goto leave;
2937 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2938 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2939 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2940 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2942 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2943 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2944 isds_PersonName_free(personName);
2946 leave:
2947 if (err) isds_PersonName_free(personName);
2948 xmlXPathFreeObject(result);
2949 return err;
2953 /* Find and convert XSD:gAddress group in current node into structure
2954 * @context is ISDS context
2955 * @address is automatically reallocated address structure. If no member
2956 * value is found, will be freed.
2957 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2958 * elements
2959 * In case of error @address will be freed. */
2960 static isds_error extract_gAddress(struct isds_ctx *context,
2961 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2962 isds_error err = IE_SUCCESS;
2963 xmlXPathObjectPtr result = NULL;
2965 if (!context) return IE_INVALID_CONTEXT;
2966 if (!address) return IE_INVAL;
2967 isds_Address_free(address);
2968 if (!xpath_ctx) return IE_INVAL;
2971 *address = calloc(1, sizeof(**address));
2972 if (!*address) {
2973 err = IE_NOMEM;
2974 goto leave;
2977 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2978 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2979 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2980 EXTRACT_STRING("isds:adNumberInMunicipality",
2981 (*address)->adNumberInMunicipality);
2982 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2983 EXTRACT_STRING("isds:adState", (*address)->adState);
2985 if (!(*address)->adCity && !(*address)->adStreet &&
2986 !(*address)->adNumberInStreet &&
2987 !(*address)->adNumberInMunicipality &&
2988 !(*address)->adZipCode && !(*address)->adState)
2989 isds_Address_free(address);
2991 leave:
2992 if (err) isds_Address_free(address);
2993 xmlXPathFreeObject(result);
2994 return err;
2998 /* Find and convert isds:biDate element in current node into structure
2999 * @context is ISDS context
3000 * @biDate is automatically reallocated birth date structure. If no member
3001 * value is found, will be freed.
3002 * @xpath_ctx is XPath context with current node as parent for isds:biDate
3003 * element
3004 * In case of error @biDate will be freed. */
3005 static isds_error extract_BiDate(struct isds_ctx *context,
3006 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
3007 isds_error err = IE_SUCCESS;
3008 xmlXPathObjectPtr result = NULL;
3009 char *string = NULL;
3011 if (!context) return IE_INVALID_CONTEXT;
3012 if (!biDate) return IE_INVAL;
3013 zfree(*biDate);
3014 if (!xpath_ctx) return IE_INVAL;
3016 EXTRACT_STRING("isds:biDate", string);
3017 if (string) {
3018 *biDate = calloc(1, sizeof(**biDate));
3019 if (!*biDate) {
3020 err = IE_NOMEM;
3021 goto leave;
3023 err = _isds_datestring2tm((xmlChar *)string, *biDate);
3024 if (err) {
3025 if (err == IE_NOTSUP) {
3026 err = IE_ISDS;
3027 char *string_locale = _isds_utf82locale(string);
3028 isds_printf_message(context,
3029 _("Invalid isds:biDate value: %s"), string_locale);
3030 free(string_locale);
3032 goto leave;
3036 leave:
3037 if (err) zfree(*biDate);
3038 free(string);
3039 xmlXPathFreeObject(result);
3040 return err;
3044 /* Convert isds:dBOwnerInfo XML tree into structure
3045 * @context is ISDS context
3046 * @db_owner_info is automatically reallocated box owner info structure
3047 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
3048 * In case of error @db_owner_info will be freed. */
3049 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
3050 struct isds_DbOwnerInfo **db_owner_info,
3051 xmlXPathContextPtr xpath_ctx) {
3052 isds_error err = IE_SUCCESS;
3053 xmlXPathObjectPtr result = NULL;
3054 char *string = NULL;
3056 if (!context) return IE_INVALID_CONTEXT;
3057 if (!db_owner_info) return IE_INVAL;
3058 isds_DbOwnerInfo_free(db_owner_info);
3059 if (!xpath_ctx) return IE_INVAL;
3062 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3063 if (!*db_owner_info) {
3064 err = IE_NOMEM;
3065 goto leave;
3068 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
3070 EXTRACT_STRING("isds:dbType", string);
3071 if (string) {
3072 (*db_owner_info)->dbType =
3073 calloc(1, sizeof(*((*db_owner_info)->dbType)));
3074 if (!(*db_owner_info)->dbType) {
3075 err = IE_NOMEM;
3076 goto leave;
3078 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
3079 if (err) {
3080 zfree((*db_owner_info)->dbType);
3081 if (err == IE_ENUM) {
3082 err = IE_ISDS;
3083 char *string_locale = _isds_utf82locale(string);
3084 isds_printf_message(context, _("Unknown isds:dbType: %s"),
3085 string_locale);
3086 free(string_locale);
3088 goto leave;
3090 zfree(string);
3093 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
3095 err = extract_gPersonName(context, &(*db_owner_info)->personName,
3096 xpath_ctx);
3097 if (err) goto leave;
3099 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
3101 (*db_owner_info)->birthInfo =
3102 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
3103 if (!(*db_owner_info)->birthInfo) {
3104 err = IE_NOMEM;
3105 goto leave;
3107 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
3108 xpath_ctx);
3109 if (err) goto leave;
3110 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3111 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3112 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3113 if (!(*db_owner_info)->birthInfo->biDate &&
3114 !(*db_owner_info)->birthInfo->biCity &&
3115 !(*db_owner_info)->birthInfo->biCounty &&
3116 !(*db_owner_info)->birthInfo->biState)
3117 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3119 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3120 if (err) goto leave;
3122 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3123 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3124 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3125 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3126 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3128 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3130 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3131 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3132 (*db_owner_info)->dbOpenAddressing);
3134 leave:
3135 if (err) isds_DbOwnerInfo_free(db_owner_info);
3136 free(string);
3137 xmlXPathFreeObject(result);
3138 return err;
3142 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3143 * @context is session context
3144 * @owner is libisds structure with box description
3145 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3146 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3147 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
3149 isds_error err = IE_SUCCESS;
3150 xmlNodePtr node;
3151 xmlChar *string = NULL;
3153 if (!context) return IE_INVALID_CONTEXT;
3154 if (!owner || !db_owner_info) return IE_INVAL;
3157 /* Build XSD:tDbOwnerInfo */
3158 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3159 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3161 /* dbType */
3162 if (owner->dbType) {
3163 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
3164 if (!type_string) {
3165 isds_printf_message(context, _("Invalid dbType value: %d"),
3166 *(owner->dbType));
3167 err = IE_ENUM;
3168 goto leave;
3170 INSERT_STRING(db_owner_info, "dbType", type_string);
3172 INSERT_STRING(db_owner_info, "ic", owner->ic);
3173 if (owner->personName) {
3174 INSERT_STRING(db_owner_info, "pnFirstName",
3175 owner->personName->pnFirstName);
3176 INSERT_STRING(db_owner_info, "pnMiddleName",
3177 owner->personName->pnMiddleName);
3178 INSERT_STRING(db_owner_info, "pnLastName",
3179 owner->personName->pnLastName);
3180 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3181 owner->personName->pnLastNameAtBirth);
3183 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3184 if (owner->birthInfo) {
3185 if (owner->birthInfo->biDate) {
3186 if (!tm2datestring(owner->birthInfo->biDate, &string))
3187 INSERT_STRING(db_owner_info, "biDate", string);
3188 free(string); string = NULL;
3190 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
3191 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
3192 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
3194 if (owner->address) {
3195 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
3196 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
3197 INSERT_STRING(db_owner_info, "adNumberInStreet",
3198 owner->address->adNumberInStreet);
3199 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3200 owner->address->adNumberInMunicipality);
3201 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
3202 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
3204 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3205 INSERT_STRING(db_owner_info, "email", owner->email);
3206 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3208 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3209 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3211 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3212 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3214 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3216 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3217 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3218 owner->dbOpenAddressing);
3220 leave:
3221 free(string);
3222 return err;
3226 /* Convert XSD:tDbUserInfo XML tree into structure
3227 * @context is ISDS context
3228 * @db_user_info is automatically reallocated user info structure
3229 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3230 * In case of error @db_user_info will be freed. */
3231 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3232 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3233 isds_error err = IE_SUCCESS;
3234 xmlXPathObjectPtr result = NULL;
3235 char *string = NULL;
3237 if (!context) return IE_INVALID_CONTEXT;
3238 if (!db_user_info) return IE_INVAL;
3239 isds_DbUserInfo_free(db_user_info);
3240 if (!xpath_ctx) return IE_INVAL;
3243 *db_user_info = calloc(1, sizeof(**db_user_info));
3244 if (!*db_user_info) {
3245 err = IE_NOMEM;
3246 goto leave;
3249 EXTRACT_STRING_ATTRIBUTE("AIFOTicket", (*db_user_info)->aifo_ticket, 0);
3251 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3253 EXTRACT_STRING("isds:userType", string);
3254 if (string) {
3255 (*db_user_info)->userType =
3256 calloc(1, sizeof(*((*db_user_info)->userType)));
3257 if (!(*db_user_info)->userType) {
3258 err = IE_NOMEM;
3259 goto leave;
3261 err = string2isds_UserType((xmlChar *)string,
3262 (*db_user_info)->userType);
3263 if (err) {
3264 zfree((*db_user_info)->userType);
3265 if (err == IE_ENUM) {
3266 err = IE_ISDS;
3267 char *string_locale = _isds_utf82locale(string);
3268 isds_printf_message(context,
3269 _("Unknown isds:userType value: %s"), string_locale);
3270 free(string_locale);
3272 goto leave;
3274 zfree(string);
3277 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3279 (*db_user_info)->personName =
3280 calloc(1, sizeof(*((*db_user_info)->personName)));
3281 if (!(*db_user_info)->personName) {
3282 err = IE_NOMEM;
3283 goto leave;
3286 err = extract_gPersonName(context, &(*db_user_info)->personName,
3287 xpath_ctx);
3288 if (err) goto leave;
3290 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3291 if (err) goto leave;
3293 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3294 if (err) goto leave;
3296 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3297 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3299 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3300 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3301 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3303 /* ???: Default value is "CZ" according specification. Should we provide
3304 * it? */
3305 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3307 leave:
3308 if (err) isds_DbUserInfo_free(db_user_info);
3309 free(string);
3310 xmlXPathFreeObject(result);
3311 return err;
3315 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3316 * @context is session context
3317 * @user is libisds structure with user description
3318 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
3319 * @db_user_info is XML element of XSD:tDbUserInfo */
3320 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3321 const struct isds_DbUserInfo *user, _Bool honor_aifo_ticket,
3322 xmlNodePtr db_user_info) {
3324 isds_error err = IE_SUCCESS;
3325 xmlNodePtr node;
3326 xmlAttrPtr attribute_node;
3327 xmlChar *string = NULL;
3329 if (!context) return IE_INVALID_CONTEXT;
3330 if (!user || !db_user_info) return IE_INVAL;
3332 /* Build XSD:tDbUserInfo */
3334 /* XXX: Insert AIFOTicket attribute only when allowed. XML schema does not
3335 * allow it everywhere. */
3336 if (honor_aifo_ticket && user->aifo_ticket) {
3337 INSERT_STRING_ATTRIBUTE(db_user_info, "AIFOTicket", user->aifo_ticket);
3340 if (user->personName) {
3341 INSERT_STRING(db_user_info, "pnFirstName",
3342 user->personName->pnFirstName);
3343 INSERT_STRING(db_user_info, "pnMiddleName",
3344 user->personName->pnMiddleName);
3345 INSERT_STRING(db_user_info, "pnLastName",
3346 user->personName->pnLastName);
3347 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3348 user->personName->pnLastNameAtBirth);
3350 if (user->address) {
3351 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3352 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3353 INSERT_STRING(db_user_info, "adNumberInStreet",
3354 user->address->adNumberInStreet);
3355 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3356 user->address->adNumberInMunicipality);
3357 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3358 INSERT_STRING(db_user_info, "adState", user->address->adState);
3360 if (user->biDate) {
3361 if (!tm2datestring(user->biDate, &string))
3362 INSERT_STRING(db_user_info, "biDate", string);
3363 zfree(string);
3365 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3366 INSERT_STRING(db_user_info, "userID", user->userID);
3368 /* userType */
3369 if (user->userType) {
3370 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3371 if (!type_string) {
3372 isds_printf_message(context, _("Invalid userType value: %d"),
3373 *(user->userType));
3374 err = IE_ENUM;
3375 goto leave;
3377 INSERT_STRING(db_user_info, "userType", type_string);
3380 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3381 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3382 INSERT_STRING(db_user_info, "ic", user->ic);
3383 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3384 INSERT_STRING(db_user_info, "firmName", user->firmName);
3385 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3386 INSERT_STRING(db_user_info, "caCity", user->caCity);
3387 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3388 INSERT_STRING(db_user_info, "caState", user->caState);
3390 leave:
3391 free(string);
3392 return err;
3396 /* Convert XSD:tPDZRec XML tree into structure
3397 * @context is ISDS context
3398 * @permission is automatically reallocated commercial permission structure
3399 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3400 * In case of error @permission will be freed. */
3401 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3402 struct isds_commercial_permission **permission,
3403 xmlXPathContextPtr xpath_ctx) {
3404 isds_error err = IE_SUCCESS;
3405 xmlXPathObjectPtr result = NULL;
3406 char *string = NULL;
3408 if (!context) return IE_INVALID_CONTEXT;
3409 if (!permission) return IE_INVAL;
3410 isds_commercial_permission_free(permission);
3411 if (!xpath_ctx) return IE_INVAL;
3414 *permission = calloc(1, sizeof(**permission));
3415 if (!*permission) {
3416 err = IE_NOMEM;
3417 goto leave;
3420 EXTRACT_STRING("isds:PDZType", string);
3421 if (string) {
3422 err = string2isds_payment_type((xmlChar *)string,
3423 &(*permission)->type);
3424 if (err) {
3425 if (err == IE_ENUM) {
3426 err = IE_ISDS;
3427 char *string_locale = _isds_utf82locale(string);
3428 isds_printf_message(context,
3429 _("Unknown isds:PDZType value: %s"), string_locale);
3430 free(string_locale);
3432 goto leave;
3434 zfree(string);
3437 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3438 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3440 EXTRACT_STRING("isds:PDZExpire", string);
3441 if (string) {
3442 err = timestring2timeval((xmlChar *) string,
3443 &((*permission)->expiration));
3444 if (err) {
3445 char *string_locale = _isds_utf82locale(string);
3446 if (err == IE_DATE) err = IE_ISDS;
3447 isds_printf_message(context,
3448 _("Could not convert PDZExpire as ISO time: %s"),
3449 string_locale);
3450 free(string_locale);
3451 goto leave;
3453 zfree(string);
3456 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3457 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3459 leave:
3460 if (err) isds_commercial_permission_free(permission);
3461 free(string);
3462 xmlXPathFreeObject(result);
3463 return err;
3467 /* Convert XSD:tCiRecord XML tree into structure
3468 * @context is ISDS context
3469 * @event is automatically reallocated commercial credit event structure
3470 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3471 * In case of error @event will be freed. */
3472 static isds_error extract_CiRecord(struct isds_ctx *context,
3473 struct isds_credit_event **event,
3474 xmlXPathContextPtr xpath_ctx) {
3475 isds_error err = IE_SUCCESS;
3476 xmlXPathObjectPtr result = NULL;
3477 char *string = NULL;
3478 long int *number_ptr;
3480 if (!context) return IE_INVALID_CONTEXT;
3481 if (!event) return IE_INVAL;
3482 isds_credit_event_free(event);
3483 if (!xpath_ctx) return IE_INVAL;
3486 *event = calloc(1, sizeof(**event));
3487 if (!*event) {
3488 err = IE_NOMEM;
3489 goto leave;
3492 EXTRACT_STRING("isds:ciEventTime", string);
3493 if (string) {
3494 err = timestring2timeval((xmlChar *) string,
3495 &(*event)->time);
3496 if (err) {
3497 char *string_locale = _isds_utf82locale(string);
3498 if (err == IE_DATE) err = IE_ISDS;
3499 isds_printf_message(context,
3500 _("Could not convert ciEventTime as ISO time: %s"),
3501 string_locale);
3502 free(string_locale);
3503 goto leave;
3505 zfree(string);
3508 EXTRACT_STRING("isds:ciEventType", string);
3509 if (string) {
3510 err = string2isds_credit_event_type((xmlChar *)string,
3511 &(*event)->type);
3512 if (err) {
3513 if (err == IE_ENUM) {
3514 err = IE_ISDS;
3515 char *string_locale = _isds_utf82locale(string);
3516 isds_printf_message(context,
3517 _("Unknown isds:ciEventType value: %s"), string_locale);
3518 free(string_locale);
3520 goto leave;
3522 zfree(string);
3525 number_ptr = &((*event)->credit_change);
3526 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3527 number_ptr = &(*event)->new_credit;
3528 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3530 switch((*event)->type) {
3531 case ISDS_CREDIT_CHARGED:
3532 EXTRACT_STRING("isds:ciTransID",
3533 (*event)->details.charged.transaction);
3534 break;
3535 case ISDS_CREDIT_DISCHARGED:
3536 EXTRACT_STRING("isds:ciTransID",
3537 (*event)->details.discharged.transaction);
3538 break;
3539 case ISDS_CREDIT_MESSAGE_SENT:
3540 EXTRACT_STRING("isds:ciRecipientID",
3541 (*event)->details.message_sent.recipient);
3542 EXTRACT_STRING("isds:ciPDZID",
3543 (*event)->details.message_sent.message_id);
3544 break;
3545 case ISDS_CREDIT_STORAGE_SET:
3546 number_ptr = &((*event)->details.storage_set.new_capacity);
3547 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3548 EXTRACT_DATE("isds:ciNewFrom",
3549 (*event)->details.storage_set.new_valid_from);
3550 EXTRACT_DATE("isds:ciNewTo",
3551 (*event)->details.storage_set.new_valid_to);
3552 EXTRACT_LONGINT("isds:ciOldCapacity",
3553 (*event)->details.storage_set.old_capacity, 0);
3554 EXTRACT_DATE("isds:ciOldFrom",
3555 (*event)->details.storage_set.old_valid_from);
3556 EXTRACT_DATE("isds:ciOldTo",
3557 (*event)->details.storage_set.old_valid_to);
3558 EXTRACT_STRING("isds:ciDoneBy",
3559 (*event)->details.storage_set.initiator);
3560 break;
3561 case ISDS_CREDIT_EXPIRED:
3562 break;
3565 leave:
3566 if (err) isds_credit_event_free(event);
3567 free(string);
3568 xmlXPathFreeObject(result);
3569 return err;
3573 #endif /* HAVE_LIBCURL */
3576 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3577 * isds_envelope structure. The envelope is automatically allocated but not
3578 * reallocated. The date are just appended into envelope structure.
3579 * @context is ISDS context
3580 * @envelope is automatically allocated message envelope structure
3581 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3582 * In case of error @envelope will be freed. */
3583 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3584 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3585 isds_error err = IE_SUCCESS;
3586 xmlXPathObjectPtr result = NULL;
3588 if (!context) return IE_INVALID_CONTEXT;
3589 if (!envelope) return IE_INVAL;
3590 if (!xpath_ctx) return IE_INVAL;
3593 if (!*envelope) {
3594 /* Allocate envelope */
3595 *envelope = calloc(1, sizeof(**envelope));
3596 if (!*envelope) {
3597 err = IE_NOMEM;
3598 goto leave;
3600 } else {
3601 /* Else free former data */
3602 zfree((*envelope)->dmSenderOrgUnit);
3603 zfree((*envelope)->dmSenderOrgUnitNum);
3604 zfree((*envelope)->dbIDRecipient);
3605 zfree((*envelope)->dmRecipientOrgUnit);
3606 zfree((*envelope)->dmRecipientOrgUnitNum);
3607 zfree((*envelope)->dmToHands);
3608 zfree((*envelope)->dmAnnotation);
3609 zfree((*envelope)->dmRecipientRefNumber);
3610 zfree((*envelope)->dmSenderRefNumber);
3611 zfree((*envelope)->dmRecipientIdent);
3612 zfree((*envelope)->dmSenderIdent);
3613 zfree((*envelope)->dmLegalTitleLaw);
3614 zfree((*envelope)->dmLegalTitleYear);
3615 zfree((*envelope)->dmLegalTitleSect);
3616 zfree((*envelope)->dmLegalTitlePar);
3617 zfree((*envelope)->dmLegalTitlePoint);
3618 zfree((*envelope)->dmPersonalDelivery);
3619 zfree((*envelope)->dmAllowSubstDelivery);
3622 /* Extract envelope elements added by sender or ISDS
3623 * (XSD: gMessageEnvelopeSub type) */
3624 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3625 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3626 (*envelope)->dmSenderOrgUnitNum, 0);
3627 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3628 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3629 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3630 (*envelope)->dmRecipientOrgUnitNum, 0);
3631 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3632 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3633 EXTRACT_STRING("isds:dmRecipientRefNumber",
3634 (*envelope)->dmRecipientRefNumber);
3635 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3636 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3637 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3639 /* Extract envelope elements regarding law reference */
3640 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3641 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3642 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3643 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3644 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3646 /* Extract envelope other elements */
3647 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3648 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3649 (*envelope)->dmAllowSubstDelivery);
3651 leave:
3652 if (err) isds_envelope_free(envelope);
3653 xmlXPathFreeObject(result);
3654 return err;
3659 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3660 * isds_envelope structure. The envelope is automatically allocated but not
3661 * reallocated. The date are just appended into envelope structure.
3662 * @context is ISDS context
3663 * @envelope is automatically allocated message envelope structure
3664 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3665 * In case of error @envelope will be freed. */
3666 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3667 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3668 isds_error err = IE_SUCCESS;
3669 xmlXPathObjectPtr result = NULL;
3671 if (!context) return IE_INVALID_CONTEXT;
3672 if (!envelope) return IE_INVAL;
3673 if (!xpath_ctx) return IE_INVAL;
3676 if (!*envelope) {
3677 /* Allocate envelope */
3678 *envelope = calloc(1, sizeof(**envelope));
3679 if (!*envelope) {
3680 err = IE_NOMEM;
3681 goto leave;
3683 } else {
3684 /* Else free former data */
3685 zfree((*envelope)->dmID);
3686 zfree((*envelope)->dbIDSender);
3687 zfree((*envelope)->dmSender);
3688 zfree((*envelope)->dmSenderAddress);
3689 zfree((*envelope)->dmSenderType);
3690 zfree((*envelope)->dmRecipient);
3691 zfree((*envelope)->dmRecipientAddress);
3692 zfree((*envelope)->dmAmbiguousRecipient);
3695 /* Extract envelope elements added by ISDS
3696 * (XSD: gMessageEnvelope type) */
3697 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3698 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3699 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3700 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3701 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3702 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3703 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3704 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3705 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3706 (*envelope)->dmAmbiguousRecipient);
3708 /* Extract envelope elements added by sender and ISDS
3709 * (XSD: gMessageEnvelope type) */
3710 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3711 if (err) goto leave;
3713 leave:
3714 if (err) isds_envelope_free(envelope);
3715 xmlXPathFreeObject(result);
3716 return err;
3720 /* Convert other envelope elements from XML tree into isds_envelope structure:
3721 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3722 * The envelope is automatically allocated but not reallocated.
3723 * The data are just appended into envelope structure.
3724 * @context is ISDS context
3725 * @envelope is automatically allocated message envelope structure
3726 * @xpath_ctx is XPath context with current node as parent desired elements
3727 * In case of error @envelope will be freed. */
3728 static isds_error append_status_size_times(struct isds_ctx *context,
3729 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3730 isds_error err = IE_SUCCESS;
3731 xmlXPathObjectPtr result = NULL;
3732 char *string = NULL;
3733 unsigned long int *unumber = NULL;
3735 if (!context) return IE_INVALID_CONTEXT;
3736 if (!envelope) return IE_INVAL;
3737 if (!xpath_ctx) return IE_INVAL;
3740 if (!*envelope) {
3741 /* Allocate new */
3742 *envelope = calloc(1, sizeof(**envelope));
3743 if (!*envelope) {
3744 err = IE_NOMEM;
3745 goto leave;
3747 } else {
3748 /* Free old data */
3749 zfree((*envelope)->dmMessageStatus);
3750 zfree((*envelope)->dmAttachmentSize);
3751 zfree((*envelope)->dmDeliveryTime);
3752 zfree((*envelope)->dmAcceptanceTime);
3756 /* dmMessageStatus element is mandatory */
3757 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3758 if (!unumber) {
3759 isds_log_message(context,
3760 _("Missing mandatory sisds:dmMessageStatus integer"));
3761 err = IE_ISDS;
3762 goto leave;
3764 err = uint2isds_message_status(context, unumber,
3765 &((*envelope)->dmMessageStatus));
3766 if (err) {
3767 if (err == IE_ENUM) err = IE_ISDS;
3768 goto leave;
3770 free(unumber); unumber = NULL;
3772 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3775 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3776 if (string) {
3777 err = timestring2timeval((xmlChar *) string,
3778 &((*envelope)->dmDeliveryTime));
3779 if (err) {
3780 char *string_locale = _isds_utf82locale(string);
3781 if (err == IE_DATE) err = IE_ISDS;
3782 isds_printf_message(context,
3783 _("Could not convert dmDeliveryTime as ISO time: %s"),
3784 string_locale);
3785 free(string_locale);
3786 goto leave;
3788 zfree(string);
3791 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3792 if (string) {
3793 err = timestring2timeval((xmlChar *) string,
3794 &((*envelope)->dmAcceptanceTime));
3795 if (err) {
3796 char *string_locale = _isds_utf82locale(string);
3797 if (err == IE_DATE) err = IE_ISDS;
3798 isds_printf_message(context,
3799 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3800 string_locale);
3801 free(string_locale);
3802 goto leave;
3804 zfree(string);
3807 leave:
3808 if (err) isds_envelope_free(envelope);
3809 free(unumber);
3810 free(string);
3811 xmlXPathFreeObject(result);
3812 return err;
3816 /* Convert message type attribute of current element into isds_envelope
3817 * structure.
3818 * TODO: This function can be incorporated into append_status_size_times() as
3819 * they are called always together.
3820 * The envelope is automatically allocated but not reallocated.
3821 * The data are just appended into envelope structure.
3822 * @context is ISDS context
3823 * @envelope is automatically allocated message envelope structure
3824 * @xpath_ctx is XPath context with current node as parent of attribute
3825 * carrying message type
3826 * In case of error @envelope will be freed. */
3827 static isds_error append_message_type(struct isds_ctx *context,
3828 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3829 isds_error err = IE_SUCCESS;
3831 if (!context) return IE_INVALID_CONTEXT;
3832 if (!envelope) return IE_INVAL;
3833 if (!xpath_ctx) return IE_INVAL;
3836 if (!*envelope) {
3837 /* Allocate new */
3838 *envelope = calloc(1, sizeof(**envelope));
3839 if (!*envelope) {
3840 err = IE_NOMEM;
3841 goto leave;
3843 } else {
3844 /* Free old data */
3845 zfree((*envelope)->dmType);
3849 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3851 if (!(*envelope)->dmType) {
3852 /* Use default value */
3853 (*envelope)->dmType = strdup("V");
3854 if (!(*envelope)->dmType) {
3855 err = IE_NOMEM;
3856 goto leave;
3858 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3859 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3860 isds_printf_message(context,
3861 _("Message type in dmType attribute is not 1 character long: "
3862 "%s"),
3863 type_locale);
3864 free(type_locale);
3865 err = IE_ISDS;
3866 goto leave;
3869 leave:
3870 if (err) isds_envelope_free(envelope);
3871 return err;
3875 #if HAVE_LIBCURL
3876 /* Convert dmType isds_envelope member into XML attribute and append it to
3877 * current node.
3878 * @context is ISDS context
3879 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3880 * @dm_envelope is XML element the resulting attribute will be appended to.
3881 * @return error code, in case of error context' message is filled. */
3882 static isds_error insert_message_type(struct isds_ctx *context,
3883 const char *type, xmlNodePtr dm_envelope) {
3884 isds_error err = IE_SUCCESS;
3885 xmlAttrPtr attribute_node;
3887 if (!context) return IE_INVALID_CONTEXT;
3888 if (!dm_envelope) return IE_INVAL;
3890 /* Insert optional message type */
3891 if (type) {
3892 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3893 char *type_locale = _isds_utf82locale(type);
3894 isds_printf_message(context,
3895 _("Message type in envelope is not 1 character long: %s"),
3896 type_locale);
3897 free(type_locale);
3898 err = IE_INVAL;
3899 goto leave;
3901 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3904 leave:
3905 return err;
3907 #endif /* HAVE_LIBCURL */
3910 /* Extract message document into reallocated document structure
3911 * @context is ISDS context
3912 * @document is automatically reallocated message documents structure
3913 * @xpath_ctx is XPath context with current node as isds:dmFile
3914 * In case of error @document will be freed. */
3915 static isds_error extract_document(struct isds_ctx *context,
3916 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3917 isds_error err = IE_SUCCESS;
3918 xmlXPathObjectPtr result = NULL;
3919 xmlNodePtr file_node;
3920 char *string = NULL;
3922 if (!context) return IE_INVALID_CONTEXT;
3923 if (!document) return IE_INVAL;
3924 isds_document_free(document);
3925 if (!xpath_ctx) return IE_INVAL;
3926 file_node = xpath_ctx->node;
3928 *document = calloc(1, sizeof(**document));
3929 if (!*document) {
3930 err = IE_NOMEM;
3931 goto leave;
3934 /* Extract document meta data */
3935 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3936 if (context->normalize_mime_type) {
3937 const char *normalized_type =
3938 isds_normalize_mime_type((*document)->dmMimeType);
3939 if (NULL != normalized_type &&
3940 normalized_type != (*document)->dmMimeType) {
3941 char *new_type = strdup(normalized_type);
3942 if (NULL == new_type) {
3943 isds_printf_message(context,
3944 _("Not enough memory to normalize document MIME type"));
3945 err = IE_NOMEM;
3946 goto leave;
3948 free((*document)->dmMimeType);
3949 (*document)->dmMimeType = new_type;
3953 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3954 err = string2isds_FileMetaType((xmlChar*)string,
3955 &((*document)->dmFileMetaType));
3956 if (err) {
3957 char *meta_type_locale = _isds_utf82locale(string);
3958 isds_printf_message(context,
3959 _("Document has invalid dmFileMetaType attribute value: %s"),
3960 meta_type_locale);
3961 free(meta_type_locale);
3962 err = IE_ISDS;
3963 goto leave;
3965 zfree(string);
3967 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3968 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3969 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3970 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3973 /* Extract document data.
3974 * Base64 encoded blob or XML subtree must be presented. */
3976 /* Check for dmEncodedContent */
3977 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3978 xpath_ctx);
3979 if (!result) {
3980 err = IE_XML;
3981 goto leave;
3984 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3985 /* Here we have Base64 blob */
3986 (*document)->is_xml = 0;
3988 if (result->nodesetval->nodeNr > 1) {
3989 isds_printf_message(context,
3990 _("Document has more dmEncodedContent elements"));
3991 err = IE_ISDS;
3992 goto leave;
3995 xmlXPathFreeObject(result); result = NULL;
3996 EXTRACT_STRING("isds:dmEncodedContent", string);
3998 /* Decode non-empty document */
3999 if (string && string[0] != '\0') {
4000 (*document)->data_length =
4001 _isds_b64decode(string, &((*document)->data));
4002 if ((*document)->data_length == (size_t) -1) {
4003 isds_printf_message(context,
4004 _("Error while Base64-decoding document content"));
4005 err = IE_ERROR;
4006 goto leave;
4009 } else {
4010 /* No Base64 blob, try XML document */
4011 xmlXPathFreeObject(result); result = NULL;
4012 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
4013 xpath_ctx);
4014 if (!result) {
4015 err = IE_XML;
4016 goto leave;
4019 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4020 /* Here we have XML document */
4021 (*document)->is_xml = 1;
4023 if (result->nodesetval->nodeNr > 1) {
4024 isds_printf_message(context,
4025 _("Document has more dmXMLContent elements"));
4026 err = IE_ISDS;
4027 goto leave;
4030 /* XXX: We cannot serialize the content simply because:
4031 * - XML document may point out of its scope (e.g. to message
4032 * envelope)
4033 * - isds:dmXMLContent can contain more elements, no element,
4034 * a text node only
4035 * - it's not the XML way
4036 * Thus we provide the only right solution: XML DOM. Let's
4037 * application to cope with this hot potato :) */
4038 (*document)->xml_node_list =
4039 result->nodesetval->nodeTab[0]->children;
4040 } else {
4041 /* No base64 blob, nor XML document */
4042 isds_printf_message(context,
4043 _("Document has no dmEncodedContent, nor dmXMLContent "
4044 "element"));
4045 err = IE_ISDS;
4046 goto leave;
4051 leave:
4052 if (err) isds_document_free(document);
4053 free(string);
4054 xmlXPathFreeObject(result);
4055 xpath_ctx->node = file_node;
4056 return err;
4061 /* Extract message documents into reallocated list of documents
4062 * @context is ISDS context
4063 * @documents is automatically reallocated message documents list structure
4064 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4065 * In case of error @documents will be freed. */
4066 static isds_error extract_documents(struct isds_ctx *context,
4067 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
4068 isds_error err = IE_SUCCESS;
4069 xmlXPathObjectPtr result = NULL;
4070 xmlNodePtr files_node;
4071 struct isds_list *document, *prev_document = NULL;
4073 if (!context) return IE_INVALID_CONTEXT;
4074 if (!documents) return IE_INVAL;
4075 isds_list_free(documents);
4076 if (!xpath_ctx) return IE_INVAL;
4077 files_node = xpath_ctx->node;
4079 /* Find documents */
4080 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
4081 if (!result) {
4082 err = IE_XML;
4083 goto leave;
4086 /* No match */
4087 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4088 isds_printf_message(context,
4089 _("Message does not contain any document"));
4090 err = IE_ISDS;
4091 goto leave;
4095 /* Iterate over documents */
4096 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4098 /* Allocate and append list item */
4099 document = calloc(1, sizeof(*document));
4100 if (!document) {
4101 err = IE_NOMEM;
4102 goto leave;
4104 document->destructor = (void (*)(void **))isds_document_free;
4105 if (i == 0) *documents = document;
4106 else prev_document->next = document;
4107 prev_document = document;
4109 /* Extract document */
4110 xpath_ctx->node = result->nodesetval->nodeTab[i];
4111 err = extract_document(context,
4112 (struct isds_document **) &(document->data), xpath_ctx);
4113 if (err) goto leave;
4117 leave:
4118 if (err) isds_list_free(documents);
4119 xmlXPathFreeObject(result);
4120 xpath_ctx->node = files_node;
4121 return err;
4125 #if HAVE_LIBCURL
4126 /* Convert isds:dmRecord XML tree into structure
4127 * @context is ISDS context
4128 * @envelope is automatically reallocated message envelope structure
4129 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4130 * In case of error @envelope will be freed. */
4131 static isds_error extract_DmRecord(struct isds_ctx *context,
4132 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4133 isds_error err = IE_SUCCESS;
4134 xmlXPathObjectPtr result = NULL;
4136 if (!context) return IE_INVALID_CONTEXT;
4137 if (!envelope) return IE_INVAL;
4138 isds_envelope_free(envelope);
4139 if (!xpath_ctx) return IE_INVAL;
4142 *envelope = calloc(1, sizeof(**envelope));
4143 if (!*envelope) {
4144 err = IE_NOMEM;
4145 goto leave;
4149 /* Extract tRecord data */
4150 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4152 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4153 * dmAcceptanceTime. */
4154 err = append_status_size_times(context, envelope, xpath_ctx);
4155 if (err) goto leave;
4157 /* Extract envelope elements added by sender and ISDS
4158 * (XSD: gMessageEnvelope type) */
4159 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4160 if (err) goto leave;
4162 /* Get message type */
4163 err = append_message_type(context, envelope, xpath_ctx);
4164 if (err) goto leave;
4167 leave:
4168 if (err) isds_envelope_free(envelope);
4169 xmlXPathFreeObject(result);
4170 return err;
4174 /* Convert XSD:tStateChangesRecord type XML tree into structure
4175 * @context is ISDS context
4176 * @changed_status is automatically reallocated message state change structure
4177 * @xpath_ctx is XPath context with current node as element of
4178 * XSD:tStateChangesRecord type
4179 * In case of error @changed_status will be freed. */
4180 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4181 struct isds_message_status_change **changed_status,
4182 xmlXPathContextPtr xpath_ctx) {
4183 isds_error err = IE_SUCCESS;
4184 xmlXPathObjectPtr result = NULL;
4185 unsigned long int *unumber = NULL;
4186 char *string = NULL;
4188 if (!context) return IE_INVALID_CONTEXT;
4189 if (!changed_status) return IE_INVAL;
4190 isds_message_status_change_free(changed_status);
4191 if (!xpath_ctx) return IE_INVAL;
4194 *changed_status = calloc(1, sizeof(**changed_status));
4195 if (!*changed_status) {
4196 err = IE_NOMEM;
4197 goto leave;
4201 /* Extract tGetStateChangesInput data */
4202 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4204 /* dmEventTime is mandatory */
4205 EXTRACT_STRING("isds:dmEventTime", string);
4206 if (string) {
4207 err = timestring2timeval((xmlChar *) string,
4208 &((*changed_status)->time));
4209 if (err) {
4210 char *string_locale = _isds_utf82locale(string);
4211 if (err == IE_DATE) err = IE_ISDS;
4212 isds_printf_message(context,
4213 _("Could not convert dmEventTime as ISO time: %s"),
4214 string_locale);
4215 free(string_locale);
4216 goto leave;
4218 zfree(string);
4221 /* dmMessageStatus element is mandatory */
4222 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4223 if (!unumber) {
4224 isds_log_message(context,
4225 _("Missing mandatory isds:dmMessageStatus integer"));
4226 err = IE_ISDS;
4227 goto leave;
4229 err = uint2isds_message_status(context, unumber,
4230 &((*changed_status)->dmMessageStatus));
4231 if (err) {
4232 if (err == IE_ENUM) err = IE_ISDS;
4233 goto leave;
4235 zfree(unumber);
4238 leave:
4239 free(unumber);
4240 free(string);
4241 if (err) isds_message_status_change_free(changed_status);
4242 xmlXPathFreeObject(result);
4243 return err;
4245 #endif /* HAVE_LIBCURL */
4248 /* Find and convert isds:dmHash XML tree into structure
4249 * @context is ISDS context
4250 * @envelope is automatically reallocated message hash structure
4251 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4252 * In case of error @hash will be freed. */
4253 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4254 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4255 isds_error err = IE_SUCCESS;
4256 xmlNodePtr old_ctx_node;
4257 xmlXPathObjectPtr result = NULL;
4258 char *string = NULL;
4260 if (!context) return IE_INVALID_CONTEXT;
4261 if (!hash) return IE_INVAL;
4262 isds_hash_free(hash);
4263 if (!xpath_ctx) return IE_INVAL;
4265 old_ctx_node = xpath_ctx->node;
4267 *hash = calloc(1, sizeof(**hash));
4268 if (!*hash) {
4269 err = IE_NOMEM;
4270 goto leave;
4273 /* Locate dmHash */
4274 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4275 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4276 err = IE_ISDS;
4277 goto leave;
4279 if (err) {
4280 err = IE_ERROR;
4281 goto leave;
4284 /* Get hash algorithm */
4285 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4286 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4287 if (err) {
4288 if (err == IE_ENUM) {
4289 char *string_locale = _isds_utf82locale(string);
4290 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4291 string_locale);
4292 free(string_locale);
4294 goto leave;
4296 zfree(string);
4298 /* Get hash value */
4299 EXTRACT_STRING(".", string);
4300 if (!string) {
4301 isds_printf_message(context,
4302 _("sisds:dmHash element is missing hash value"));
4303 err = IE_ISDS;
4304 goto leave;
4306 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4307 if ((*hash)->length == (size_t) -1) {
4308 isds_printf_message(context,
4309 _("Error while Base64-decoding hash value"));
4310 err = IE_ERROR;
4311 goto leave;
4314 leave:
4315 if (err) isds_hash_free(hash);
4316 free(string);
4317 xmlXPathFreeObject(result);
4318 xpath_ctx->node = old_ctx_node;
4319 return err;
4323 /* Find and append isds:dmQTimestamp XML tree into envelope.
4324 * Because one service is allowed to miss time-stamp content, and we think
4325 * other could too (flaw in specification), this function is deliberated and
4326 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4327 * @context is ISDS context
4328 * @envelope is automatically allocated envelope structure
4329 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4330 * child
4331 * In case of error @envelope will be freed. */
4332 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4333 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4334 isds_error err = IE_SUCCESS;
4335 xmlXPathObjectPtr result = NULL;
4336 char *string = NULL;
4338 if (!context) return IE_INVALID_CONTEXT;
4339 if (!envelope) return IE_INVAL;
4340 if (!xpath_ctx) {
4341 isds_envelope_free(envelope);
4342 return IE_INVAL;
4345 if (!*envelope) {
4346 *envelope = calloc(1, sizeof(**envelope));
4347 if (!*envelope) {
4348 err = IE_NOMEM;
4349 goto leave;
4351 } else {
4352 zfree((*envelope)->timestamp);
4353 (*envelope)->timestamp_length = 0;
4356 /* Get dmQTimestamp */
4357 EXTRACT_STRING("sisds:dmQTimestamp", string);
4358 if (!string) {
4359 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4360 goto leave;
4362 (*envelope)->timestamp_length =
4363 _isds_b64decode(string, &((*envelope)->timestamp));
4364 if ((*envelope)->timestamp_length == (size_t) -1) {
4365 isds_printf_message(context,
4366 _("Error while Base64-decoding time stamp value"));
4367 err = IE_ERROR;
4368 goto leave;
4371 leave:
4372 if (err) isds_envelope_free(envelope);
4373 free(string);
4374 xmlXPathFreeObject(result);
4375 return err;
4379 /* Convert XSD tReturnedMessage XML tree into message structure.
4380 * It does not store serialized XML tree into message->raw.
4381 * It does store (pointer to) parsed XML tree into message->xml if needed.
4382 * @context is ISDS context
4383 * @include_documents Use true if documents must be extracted
4384 * (tReturnedMessage XSD type), use false if documents shall be omitted
4385 * (tReturnedMessageEnvelope).
4386 * @message is automatically reallocated message structure
4387 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4388 * type
4389 * In case of error @message will be freed. */
4390 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4391 const _Bool include_documents, struct isds_message **message,
4392 xmlXPathContextPtr xpath_ctx) {
4393 isds_error err = IE_SUCCESS;
4394 xmlNodePtr message_node;
4396 if (!context) return IE_INVALID_CONTEXT;
4397 if (!message) return IE_INVAL;
4398 isds_message_free(message);
4399 if (!xpath_ctx) return IE_INVAL;
4402 *message = calloc(1, sizeof(**message));
4403 if (!*message) {
4404 err = IE_NOMEM;
4405 goto leave;
4408 /* Save message XPATH context node */
4409 message_node = xpath_ctx->node;
4412 /* Extract dmDM */
4413 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4414 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4415 if (err) { err = IE_ERROR; goto leave; }
4416 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4417 if (err) goto leave;
4419 if (include_documents) {
4420 struct isds_list *item;
4422 /* Extract dmFiles */
4423 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4424 xpath_ctx);
4425 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4426 err = IE_ISDS; goto leave;
4428 if (err) { err = IE_ERROR; goto leave; }
4429 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4430 if (err) goto leave;
4432 /* Store xmlDoc of this message if needed */
4433 /* Only if we got a XML document in all the documents. */
4434 for (item = (*message)->documents; item; item = item->next) {
4435 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4436 (*message)->xml = xpath_ctx->doc;
4437 break;
4443 /* Restore context to message */
4444 xpath_ctx->node = message_node;
4446 /* Extract dmHash */
4447 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4448 xpath_ctx);
4449 if (err) goto leave;
4451 /* Extract dmQTimestamp, */
4452 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4453 xpath_ctx);
4454 if (err) goto leave;
4456 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4457 * dmAcceptanceTime. */
4458 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4459 if (err) goto leave;
4461 /* Get message type */
4462 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4463 if (err) goto leave;
4465 leave:
4466 if (err) isds_message_free(message);
4467 return err;
4471 /* Extract message event into reallocated isds_event structure
4472 * @context is ISDS context
4473 * @event is automatically reallocated message event structure
4474 * @xpath_ctx is XPath context with current node as isds:dmEvent
4475 * In case of error @event will be freed. */
4476 static isds_error extract_event(struct isds_ctx *context,
4477 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4478 isds_error err = IE_SUCCESS;
4479 xmlXPathObjectPtr result = NULL;
4480 xmlNodePtr event_node;
4481 char *string = NULL;
4483 if (!context) return IE_INVALID_CONTEXT;
4484 if (!event) return IE_INVAL;
4485 isds_event_free(event);
4486 if (!xpath_ctx) return IE_INVAL;
4487 event_node = xpath_ctx->node;
4489 *event = calloc(1, sizeof(**event));
4490 if (!*event) {
4491 err = IE_NOMEM;
4492 goto leave;
4495 /* Extract event data.
4496 * All elements are optional according XSD. That's funny. */
4497 EXTRACT_STRING("sisds:dmEventTime", string);
4498 if (string) {
4499 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4500 if (err) {
4501 char *string_locale = _isds_utf82locale(string);
4502 if (err == IE_DATE) err = IE_ISDS;
4503 isds_printf_message(context,
4504 _("Could not convert dmEventTime as ISO time: %s"),
4505 string_locale);
4506 free(string_locale);
4507 goto leave;
4509 zfree(string);
4512 /* dmEventDescr element has prefix and the rest */
4513 EXTRACT_STRING("sisds:dmEventDescr", string);
4514 if (string) {
4515 err = eventstring2event((xmlChar *) string, *event);
4516 if (err) goto leave;
4517 zfree(string);
4520 leave:
4521 if (err) isds_event_free(event);
4522 free(string);
4523 xmlXPathFreeObject(result);
4524 xpath_ctx->node = event_node;
4525 return err;
4529 /* Convert element of XSD tEventsArray type from XML tree into
4530 * isds_list of isds_event's structure. The list is automatically reallocated.
4531 * @context is ISDS context
4532 * @events is automatically reallocated list of event structures
4533 * @xpath_ctx is XPath context with current node as tEventsArray
4534 * In case of error @events will be freed. */
4535 static isds_error extract_events(struct isds_ctx *context,
4536 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4537 isds_error err = IE_SUCCESS;
4538 xmlXPathObjectPtr result = NULL;
4539 xmlNodePtr events_node;
4540 struct isds_list *event, *prev_event = NULL;
4542 if (!context) return IE_INVALID_CONTEXT;
4543 if (!events) return IE_INVAL;
4544 if (!xpath_ctx) return IE_INVAL;
4545 events_node = xpath_ctx->node;
4547 /* Free old list */
4548 isds_list_free(events);
4550 /* Find events */
4551 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4552 if (!result) {
4553 err = IE_XML;
4554 goto leave;
4557 /* No match */
4558 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4559 isds_printf_message(context,
4560 _("Delivery info does not contain any event"));
4561 err = IE_ISDS;
4562 goto leave;
4566 /* Iterate over events */
4567 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4569 /* Allocate and append list item */
4570 event = calloc(1, sizeof(*event));
4571 if (!event) {
4572 err = IE_NOMEM;
4573 goto leave;
4575 event->destructor = (void (*)(void **))isds_event_free;
4576 if (i == 0) *events = event;
4577 else prev_event->next = event;
4578 prev_event = event;
4580 /* Extract event */
4581 xpath_ctx->node = result->nodesetval->nodeTab[i];
4582 err = extract_event(context,
4583 (struct isds_event **) &(event->data), xpath_ctx);
4584 if (err) goto leave;
4588 leave:
4589 if (err) isds_list_free(events);
4590 xmlXPathFreeObject(result);
4591 xpath_ctx->node = events_node;
4592 return err;
4596 #if HAVE_LIBCURL
4597 /* Insert Base64 encoded data as element with text child.
4598 * @context is session context
4599 * @parent is XML node to append @element with @data as child
4600 * @ns is XML namespace of @element, use NULL to inherit from @parent
4601 * @element is UTF-8 encoded name of new element
4602 * @data is bit stream to encode into @element
4603 * @length is size of @data in bytes
4604 * @return standard error code and fill long error message if needed */
4605 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4606 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4607 const void *data, size_t length) {
4608 isds_error err = IE_SUCCESS;
4609 xmlNodePtr node;
4611 if (!context) return IE_INVALID_CONTEXT;
4612 if (!data && length > 0) return IE_INVAL;
4613 if (!parent || !element) return IE_INVAL;
4615 xmlChar *base64data = NULL;
4616 base64data = (xmlChar *) _isds_b64encode(data, length);
4617 if (!base64data) {
4618 isds_printf_message(context,
4619 ngettext("Not enough memory to encode %zd byte into Base64",
4620 "Not enough memory to encode %zd bytes into Base64",
4621 length),
4622 length);
4623 err = IE_NOMEM;
4624 goto leave;
4626 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4628 leave:
4629 free(base64data);
4630 return err;
4634 /* Convert isds_document structure into XML tree and append to dmFiles node.
4635 * @context is session context
4636 * @document is ISDS document
4637 * @dm_files is XML element the resulting tree will be appended to as a child.
4638 * @return error code, in case of error context' message is filled. */
4639 static isds_error insert_document(struct isds_ctx *context,
4640 struct isds_document *document, xmlNodePtr dm_files) {
4641 isds_error err = IE_SUCCESS;
4642 xmlNodePtr new_file = NULL, file = NULL, node;
4643 xmlAttrPtr attribute_node;
4645 if (!context) return IE_INVALID_CONTEXT;
4646 if (!document || !dm_files) return IE_INVAL;
4648 /* Allocate new dmFile */
4649 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4650 if (!new_file) {
4651 isds_printf_message(context, _("Could not allocate main dmFile"));
4652 err = IE_ERROR;
4653 goto leave;
4655 /* Append the new dmFile.
4656 * XXX: Main document must go first */
4657 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4658 file = xmlAddPrevSibling(dm_files->children, new_file);
4659 else
4660 file = xmlAddChild(dm_files, new_file);
4662 if (!file) {
4663 xmlFreeNode(new_file); new_file = NULL;
4664 isds_printf_message(context, _("Could not add dmFile child to "
4665 "%s element"), dm_files->name);
4666 err = IE_ERROR;
4667 goto leave;
4670 /* @dmMimeType is required */
4671 if (!document->dmMimeType) {
4672 isds_log_message(context,
4673 _("Document is missing mandatory MIME type definition"));
4674 err = IE_INVAL;
4675 goto leave;
4677 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4679 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4680 if (!string) {
4681 isds_printf_message(context,
4682 _("Document has unknown dmFileMetaType: %ld"),
4683 document->dmFileMetaType);
4684 err = IE_ENUM;
4685 goto leave;
4687 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4689 if (document->dmFileGuid) {
4690 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4692 if (document->dmUpFileGuid) {
4693 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4696 /* @dmFileDescr is required */
4697 if (!document->dmFileDescr) {
4698 isds_log_message(context,
4699 _("Document is missing mandatory description (title)"));
4700 err = IE_INVAL;
4701 goto leave;
4703 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4705 if (document->dmFormat) {
4706 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4710 /* Insert content (body) of the document. */
4711 if (document->is_xml) {
4712 /* XML document requested */
4714 /* Allocate new dmXMLContent */
4715 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4716 if (!xmlcontent) {
4717 isds_printf_message(context,
4718 _("Could not allocate dmXMLContent element"));
4719 err = IE_ERROR;
4720 goto leave;
4722 /* Append it */
4723 node = xmlAddChild(file, xmlcontent);
4724 if (!node) {
4725 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4726 isds_printf_message(context,
4727 _("Could not add dmXMLContent child to %s element"),
4728 file->name);
4729 err = IE_ERROR;
4730 goto leave;
4733 /* Copy non-empty node list */
4734 if (document->xml_node_list) {
4735 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4736 document->xml_node_list);
4737 if (!content) {
4738 isds_printf_message(context,
4739 _("Not enough memory to copy XML document"));
4740 err = IE_NOMEM;
4741 goto leave;
4744 if (!xmlAddChildList(node, content)) {
4745 xmlFreeNodeList(content);
4746 isds_printf_message(context,
4747 _("Error while adding XML document into dmXMLContent"));
4748 err = IE_XML;
4749 goto leave;
4751 /* XXX: We cannot free the content here because it's part of node's
4752 * document since now. It will be freed with it automatically. */
4754 } else {
4755 /* Binary document requested */
4756 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4757 document->data, document->data_length);
4758 if (err) goto leave;
4761 leave:
4762 return err;
4766 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4767 * The copy must be preallocated, the date are just appended into structure.
4768 * @context is ISDS context
4769 * @copy is message copy structure
4770 * @xpath_ctx is XPath context with current node as tMStatus */
4771 static isds_error append_TMStatus(struct isds_ctx *context,
4772 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4773 isds_error err = IE_SUCCESS;
4774 xmlXPathObjectPtr result = NULL;
4775 char *code = NULL, *message = NULL;
4777 if (!context) return IE_INVALID_CONTEXT;
4778 if (!copy || !xpath_ctx) return IE_INVAL;
4780 /* Free old values */
4781 zfree(copy->dmStatus);
4782 zfree(copy->dmID);
4784 /* Get error specific to this copy */
4785 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4786 if (!code) {
4787 isds_log_message(context,
4788 _("Missing isds:dmStatusCode under "
4789 "XSD:tMStatus type element"));
4790 err = IE_ISDS;
4791 goto leave;
4794 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4795 /* This copy failed */
4796 copy->error = IE_ISDS;
4797 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4798 if (message) {
4799 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4800 if (!copy->dmStatus) {
4801 copy->dmStatus = code;
4802 code = NULL;
4804 } else {
4805 copy->dmStatus = code;
4806 code = NULL;
4808 } else {
4809 /* This copy succeeded. In this case only, message ID is valid */
4810 copy->error = IE_SUCCESS;
4812 EXTRACT_STRING("isds:dmID", copy->dmID);
4813 if (!copy->dmID) {
4814 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4815 "but did not returned assigned message ID\n"));
4816 err = IE_ISDS;
4820 leave:
4821 free(code);
4822 free(message);
4823 xmlXPathFreeObject(result);
4824 return err;
4828 /* Insert struct isds_approval data (box approval) into XML tree
4829 * @context is session context
4830 * @approval is libisds structure with approval description. NULL is
4831 * acceptable.
4832 * @parent is XML element to append @approval to */
4833 static isds_error insert_GExtApproval(struct isds_ctx *context,
4834 const struct isds_approval *approval, xmlNodePtr parent) {
4836 isds_error err = IE_SUCCESS;
4837 xmlNodePtr node;
4839 if (!context) return IE_INVALID_CONTEXT;
4840 if (!parent) return IE_INVAL;
4842 if (!approval) return IE_SUCCESS;
4844 /* Build XSD:gExtApproval */
4845 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4846 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4848 leave:
4849 return err;
4853 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4854 * code
4855 * @context is session context
4856 * @service_name is name of SERVICE_DB_ACCESS
4857 * @response is reallocated server SOAP body response as XML document
4858 * @raw_response is reallocated bit stream with response body. Use
4859 * NULL if you don't care
4860 * @raw_response_length is size of @raw_response in bytes
4861 * @code is reallocated ISDS status code
4862 * @status_message is reallocated ISDS status message
4863 * @return error coded from lower layer, context message will be set up
4864 * appropriately. */
4865 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4866 const xmlChar *service_name,
4867 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4868 xmlChar **code, xmlChar **status_message) {
4870 isds_error err = IE_SUCCESS;
4871 char *service_name_locale = NULL;
4872 xmlNodePtr request = NULL, node;
4873 xmlNsPtr isds_ns = NULL;
4875 if (!context) return IE_INVALID_CONTEXT;
4876 if (!service_name) return IE_INVAL;
4877 if (!response || !code || !status_message) return IE_INVAL;
4878 if (!raw_response_length && raw_response) return IE_INVAL;
4880 /* Free output argument */
4881 xmlFreeDoc(*response); *response = NULL;
4882 if (raw_response) zfree(*raw_response);
4883 zfree(*code);
4884 zfree(*status_message);
4887 /* Check if connection is established
4888 * TODO: This check should be done downstairs. */
4889 if (!context->curl) return IE_CONNECTION_CLOSED;
4891 service_name_locale = _isds_utf82locale((char*)service_name);
4892 if (!service_name_locale) {
4893 err = IE_NOMEM;
4894 goto leave;
4897 /* Build request */
4898 request = xmlNewNode(NULL, service_name);
4899 if (!request) {
4900 isds_printf_message(context,
4901 _("Could not build %s request"), service_name_locale);
4902 err = IE_ERROR;
4903 goto leave;
4905 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4906 if(!isds_ns) {
4907 isds_log_message(context, _("Could not create ISDS name space"));
4908 err = IE_ERROR;
4909 goto leave;
4911 xmlSetNs(request, isds_ns);
4914 /* Add XSD:tDummyInput child */
4915 INSERT_STRING(request, "dbDummy", NULL);
4918 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4919 service_name_locale);
4921 /* Send request */
4922 err = _isds(context, SERVICE_DB_ACCESS, request, response,
4923 raw_response, raw_response_length);
4924 xmlFreeNode(request); request = NULL;
4926 if (err) {
4927 isds_log(ILF_ISDS, ILL_DEBUG,
4928 _("Processing ISDS response on %s request failed\n"),
4929 service_name_locale);
4930 goto leave;
4933 /* Check for response status */
4934 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4935 code, status_message, NULL);
4936 if (err) {
4937 isds_log(ILF_ISDS, ILL_DEBUG,
4938 _("ISDS response on %s request is missing status\n"),
4939 service_name_locale);
4940 goto leave;
4943 /* Request processed, but nothing found */
4944 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4945 char *code_locale = _isds_utf82locale((char*) *code);
4946 char *status_message_locale =
4947 _isds_utf82locale((char*) *status_message);
4948 isds_log(ILF_ISDS, ILL_DEBUG,
4949 _("Server refused %s request (code=%s, message=%s)\n"),
4950 service_name_locale, code_locale, status_message_locale);
4951 isds_log_message(context, status_message_locale);
4952 free(code_locale);
4953 free(status_message_locale);
4954 err = IE_ISDS;
4955 goto leave;
4958 leave:
4959 free(service_name_locale);
4960 xmlFreeNode(request);
4961 return err;
4963 #endif
4966 /* Get data about logged in user and his box. */
4967 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4968 struct isds_DbOwnerInfo **db_owner_info) {
4969 isds_error err = IE_SUCCESS;
4970 #if HAVE_LIBCURL
4971 xmlDocPtr response = NULL;
4972 xmlChar *code = NULL, *message = NULL;
4973 xmlXPathContextPtr xpath_ctx = NULL;
4974 xmlXPathObjectPtr result = NULL;
4975 char *string = NULL;
4976 #endif
4978 if (!context) return IE_INVALID_CONTEXT;
4979 zfree(context->long_message);
4980 if (!db_owner_info) return IE_INVAL;
4981 isds_DbOwnerInfo_free(db_owner_info);
4983 #if HAVE_LIBCURL
4984 /* Check if connection is established */
4985 if (!context->curl) return IE_CONNECTION_CLOSED;
4988 /* Do request and check for success */
4989 err = build_send_check_dbdummy_request(context,
4990 BAD_CAST "GetOwnerInfoFromLogin",
4991 &response, NULL, NULL, &code, &message);
4992 if (err) goto leave;
4995 /* Extract data */
4996 /* Prepare structure */
4997 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4998 if (!*db_owner_info) {
4999 err = IE_NOMEM;
5000 goto leave;
5002 xpath_ctx = xmlXPathNewContext(response);
5003 if (!xpath_ctx) {
5004 err = IE_ERROR;
5005 goto leave;
5007 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5008 err = IE_ERROR;
5009 goto leave;
5012 /* Set context node */
5013 result = xmlXPathEvalExpression(BAD_CAST
5014 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
5015 if (!result) {
5016 err = IE_ERROR;
5017 goto leave;
5019 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5020 isds_log_message(context, _("Missing dbOwnerInfo element"));
5021 err = IE_ISDS;
5022 goto leave;
5024 if (result->nodesetval->nodeNr > 1) {
5025 isds_log_message(context, _("Multiple dbOwnerInfo element"));
5026 err = IE_ISDS;
5027 goto leave;
5029 xpath_ctx->node = result->nodesetval->nodeTab[0];
5030 xmlXPathFreeObject(result); result = NULL;
5032 /* Extract it */
5033 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
5036 leave:
5037 if (err) {
5038 isds_DbOwnerInfo_free(db_owner_info);
5041 free(string);
5042 xmlXPathFreeObject(result);
5043 xmlXPathFreeContext(xpath_ctx);
5045 free(code);
5046 free(message);
5047 xmlFreeDoc(response);
5049 if (!err)
5050 isds_log(ILF_ISDS, ILL_DEBUG,
5051 _("GetOwnerInfoFromLogin request processed by server "
5052 "successfully.\n"));
5053 #else /* not HAVE_LIBCURL */
5054 err = IE_NOTSUP;
5055 #endif
5057 return err;
5061 /* Get data about logged in user. */
5062 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
5063 struct isds_DbUserInfo **db_user_info) {
5064 isds_error err = IE_SUCCESS;
5065 #if HAVE_LIBCURL
5066 xmlDocPtr response = NULL;
5067 xmlChar *code = NULL, *message = NULL;
5068 xmlXPathContextPtr xpath_ctx = NULL;
5069 xmlXPathObjectPtr result = NULL;
5070 #endif
5072 if (!context) return IE_INVALID_CONTEXT;
5073 zfree(context->long_message);
5074 if (!db_user_info) return IE_INVAL;
5075 isds_DbUserInfo_free(db_user_info);
5077 #if HAVE_LIBCURL
5078 /* Check if connection is established */
5079 if (!context->curl) return IE_CONNECTION_CLOSED;
5082 /* Do request and check for success */
5083 err = build_send_check_dbdummy_request(context,
5084 BAD_CAST "GetUserInfoFromLogin",
5085 &response, NULL, NULL, &code, &message);
5086 if (err) goto leave;
5089 /* Extract data */
5090 /* Prepare structure */
5091 *db_user_info = calloc(1, sizeof(**db_user_info));
5092 if (!*db_user_info) {
5093 err = IE_NOMEM;
5094 goto leave;
5096 xpath_ctx = xmlXPathNewContext(response);
5097 if (!xpath_ctx) {
5098 err = IE_ERROR;
5099 goto leave;
5101 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5102 err = IE_ERROR;
5103 goto leave;
5106 /* Set context node */
5107 result = xmlXPathEvalExpression(BAD_CAST
5108 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
5109 if (!result) {
5110 err = IE_ERROR;
5111 goto leave;
5113 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5114 isds_log_message(context, _("Missing dbUserInfo element"));
5115 err = IE_ISDS;
5116 goto leave;
5118 if (result->nodesetval->nodeNr > 1) {
5119 isds_log_message(context, _("Multiple dbUserInfo element"));
5120 err = IE_ISDS;
5121 goto leave;
5123 xpath_ctx->node = result->nodesetval->nodeTab[0];
5124 xmlXPathFreeObject(result); result = NULL;
5126 /* Extract it */
5127 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5129 leave:
5130 if (err) {
5131 isds_DbUserInfo_free(db_user_info);
5134 xmlXPathFreeObject(result);
5135 xmlXPathFreeContext(xpath_ctx);
5137 free(code);
5138 free(message);
5139 xmlFreeDoc(response);
5141 if (!err)
5142 isds_log(ILF_ISDS, ILL_DEBUG,
5143 _("GetUserInfoFromLogin request processed by server "
5144 "successfully.\n"));
5145 #else /* not HAVE_LIBCURL */
5146 err = IE_NOTSUP;
5147 #endif
5149 return err;
5153 /* Get expiration time of current password
5154 * @context is session context
5155 * @expiration is automatically reallocated time when password expires. If
5156 * password expiration is disabled, NULL will be returned. In case of error
5157 * it will be nulled too. */
5158 isds_error isds_get_password_expiration(struct isds_ctx *context,
5159 struct timeval **expiration) {
5160 isds_error err = IE_SUCCESS;
5161 #if HAVE_LIBCURL
5162 xmlDocPtr response = NULL;
5163 xmlChar *code = NULL, *message = NULL;
5164 xmlXPathContextPtr xpath_ctx = NULL;
5165 xmlXPathObjectPtr result = NULL;
5166 char *string = NULL;
5167 #endif
5169 if (!context) return IE_INVALID_CONTEXT;
5170 zfree(context->long_message);
5171 if (!expiration) return IE_INVAL;
5172 zfree(*expiration);
5174 #if HAVE_LIBCURL
5175 /* Check if connection is established */
5176 if (!context->curl) return IE_CONNECTION_CLOSED;
5179 /* Do request and check for success */
5180 err = build_send_check_dbdummy_request(context,
5181 BAD_CAST "GetPasswordInfo",
5182 &response, NULL, NULL, &code, &message);
5183 if (err) goto leave;
5186 /* Extract data */
5187 xpath_ctx = xmlXPathNewContext(response);
5188 if (!xpath_ctx) {
5189 err = IE_ERROR;
5190 goto leave;
5192 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5193 err = IE_ERROR;
5194 goto leave;
5197 /* Set context node */
5198 result = xmlXPathEvalExpression(BAD_CAST
5199 "/isds:GetPasswordInfoResponse", xpath_ctx);
5200 if (!result) {
5201 err = IE_ERROR;
5202 goto leave;
5204 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5205 isds_log_message(context,
5206 _("Missing GetPasswordInfoResponse element"));
5207 err = IE_ISDS;
5208 goto leave;
5210 if (result->nodesetval->nodeNr > 1) {
5211 isds_log_message(context,
5212 _("Multiple GetPasswordInfoResponse element"));
5213 err = IE_ISDS;
5214 goto leave;
5216 xpath_ctx->node = result->nodesetval->nodeTab[0];
5217 xmlXPathFreeObject(result); result = NULL;
5219 /* Extract expiration date */
5220 EXTRACT_STRING("isds:pswExpDate", string);
5221 if (string) {
5222 /* And convert it if any returned. Otherwise expiration is disabled. */
5223 err = timestring2timeval((xmlChar *) string, expiration);
5224 if (err) {
5225 char *string_locale = _isds_utf82locale(string);
5226 if (err == IE_DATE) err = IE_ISDS;
5227 isds_printf_message(context,
5228 _("Could not convert pswExpDate as ISO time: %s"),
5229 string_locale);
5230 free(string_locale);
5231 goto leave;
5235 leave:
5236 if (err) {
5237 if (*expiration) {
5238 zfree(*expiration);
5242 free(string);
5243 xmlXPathFreeObject(result);
5244 xmlXPathFreeContext(xpath_ctx);
5246 free(code);
5247 free(message);
5248 xmlFreeDoc(response);
5250 if (!err)
5251 isds_log(ILF_ISDS, ILL_DEBUG,
5252 _("GetPasswordInfo request processed by server "
5253 "successfully.\n"));
5254 #else /* not HAVE_LIBCURL */
5255 err = IE_NOTSUP;
5256 #endif
5258 return err;
5262 #if HAVE_LIBCURL
5263 /* Request delivering new TOTP code from ISDS through side channel before
5264 * changing password.
5265 * @context is session context
5266 * @password is current password.
5267 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5268 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5269 * function for more details.
5270 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5271 * NULL, if you don't care.
5272 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5273 * error code. */
5274 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5275 const char *password, struct isds_otp *otp, char **refnumber) {
5276 isds_error err = IE_SUCCESS;
5277 char *saved_url = NULL; /* No copy */
5278 #if HAVE_CURL_REAUTHORIZATION_BUG
5279 CURL *saved_curl = NULL; /* No copy */
5280 #endif
5281 xmlNsPtr isds_ns = NULL;
5282 xmlNodePtr request = NULL;
5283 xmlDocPtr response = NULL;
5284 xmlChar *code = NULL, *message = NULL;
5285 const xmlChar *codes[] = {
5286 BAD_CAST "2300",
5287 BAD_CAST "2301",
5288 BAD_CAST "2302"
5290 const char *meanings[] = {
5291 N_("Unexpected error"),
5292 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5293 N_("One-time code could not been sent. Try later again.")
5295 const isds_otp_resolution resolutions[] = {
5296 OTP_RESOLUTION_UNKNOWN,
5297 OTP_RESOLUTION_TO_FAST,
5298 OTP_RESOLUTION_TOTP_NOT_SENT
5301 if (NULL == context) return IE_INVALID_CONTEXT;
5302 zfree(context->long_message);
5303 if (NULL == password) {
5304 isds_log_message(context,
5305 _("Second argument (password) of isds_change_password() "
5306 "is NULL"));
5307 return IE_INVAL;
5310 /* Check if connection is established
5311 * TODO: This check should be done downstairs. */
5312 if (!context->curl) return IE_CONNECTION_CLOSED;
5314 if (!context->otp) {
5315 isds_log_message(context, _("This function requires OTP-authenticated "
5316 "context"));
5317 return IE_INVALID_CONTEXT;
5319 if (NULL == otp) {
5320 isds_log_message(context, _("If one-time password authentication "
5321 "method is in use, requesting new OTP code requires "
5322 "one-time credentials argument either"));
5323 return IE_INVAL;
5325 if (otp->method != OTP_TIME) {
5326 isds_log_message(context, _("Requesting new time-based OTP code from "
5327 "server requires one-time password authentication "
5328 "method"));
5329 return IE_INVAL;
5331 if (otp->otp_code != NULL) {
5332 isds_log_message(context, _("Requesting new time-based OTP code from "
5333 "server requires undefined OTP code member in "
5334 "one-time credentials argument"));
5335 return IE_INVAL;
5339 /* Build request */
5340 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5341 if (!request) {
5342 isds_log_message(context, _("Could not build SendSMSCode request"));
5343 return IE_ERROR;
5345 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5346 if(!isds_ns) {
5347 isds_log_message(context, _("Could not create ISDS name space"));
5348 xmlFreeNode(request);
5349 return IE_ERROR;
5351 xmlSetNs(request, isds_ns);
5353 /* Change URL temporarily for sending this request only */
5355 char *new_url = NULL;
5356 if ((err = _isds_build_url_from_context(context,
5357 "%1$.*2$sasws/changePassword", &new_url))) {
5358 goto leave;
5360 saved_url = context->url;
5361 context->url = new_url;
5364 /* Store credentials for sending this request only */
5365 context->otp_credentials = otp;
5366 _isds_discard_credentials(context, 0);
5367 if ((err = _isds_store_credentials(context, context->saved_username,
5368 password, NULL))) {
5369 _isds_discard_credentials(context, 0);
5370 goto leave;
5372 #if HAVE_CURL_REAUTHORIZATION_BUG
5373 saved_curl = context->curl;
5374 context->curl = curl_easy_init();
5375 if (NULL == context->curl) {
5376 err = IE_ERROR;
5377 goto leave;
5379 if (context->timeout) {
5380 err = isds_set_timeout(context, context->timeout);
5381 if (err) goto leave;
5383 #endif
5385 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5387 /* Sent request */
5388 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5390 /* Remove temporal credentials */
5391 _isds_discard_credentials(context, 0);
5392 /* Detach pointer to OTP credentials from context */
5393 context->otp_credentials = NULL;
5394 /* Keep context->otp true to keep signaling this is OTP session */
5396 /* Destroy request */
5397 xmlFreeNode(request); request = NULL;
5399 if (err) {
5400 isds_log(ILF_ISDS, ILL_DEBUG,
5401 _("Processing ISDS response on SendSMSCode request failed\n"));
5402 goto leave;
5405 /* Check for response status */
5406 err = isds_response_status(context, SERVICE_ASWS, response,
5407 &code, &message, (xmlChar **)refnumber);
5408 if (err) {
5409 isds_log(ILF_ISDS, ILL_DEBUG,
5410 _("ISDS response on SendSMSCode request is missing "
5411 "status\n"));
5412 goto leave;
5415 /* Check for error */
5416 if (xmlStrcmp(code, BAD_CAST "0000")) {
5417 char *code_locale = _isds_utf82locale((char*)code);
5418 char *message_locale = _isds_utf82locale((char*)message);
5419 size_t i;
5420 isds_log(ILF_ISDS, ILL_DEBUG,
5421 _("Server refused to send new code on SendSMSCode "
5422 "request (code=%s, message=%s)\n"),
5423 code_locale, message_locale);
5425 /* Check for known error codes */
5426 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5427 if (!xmlStrcmp(code, codes[i])) break;
5429 if (i < sizeof(codes)/sizeof(*codes)) {
5430 isds_log_message(context, _(meanings[i]));
5431 /* Mimic otp->resolution according to the code, specification does
5432 * prescribe OTP header to be available. */
5433 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5434 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5435 otp->resolution = resolutions[i];
5436 } else
5437 isds_log_message(context, message_locale);
5439 free(code_locale);
5440 free(message_locale);
5442 err = IE_ISDS;
5443 goto leave;
5446 /* Otherwise new code sent successfully */
5447 /* Mimic otp->resolution according to the code, specification does
5448 * prescribe OTP header to be available. */
5449 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5450 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5452 leave:
5453 if (NULL != saved_url) {
5454 /* Revert URL to original one */
5455 zfree(context->url);
5456 context->url = saved_url;
5458 #if HAVE_CURL_REAUTHORIZATION_BUG
5459 if (NULL != saved_curl) {
5460 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5461 context->curl = saved_curl;
5463 #endif
5465 free(code);
5466 free(message);
5467 xmlFreeDoc(response);
5468 xmlFreeNode(request);
5470 if (!err)
5471 isds_log(ILF_ISDS, ILL_DEBUG,
5472 _("New OTP code has been sent successfully on SendSMSCode "
5473 "request.\n"));
5474 return err;
5478 /* Convert response status code to isds_error code and set long message
5479 * @context is context to save long message to
5480 * @map is mapping from codes to errors and messages. Pass NULL for generic
5481 * handling.
5482 * @code is status code to translate
5483 * @message is non-localized status message to put into long message in case
5484 * of uknown error. It can be NULL if server did not provide any.
5485 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5486 * invalid invocation. */
5487 static isds_error statuscode2isds_error(struct isds_ctx *context,
5488 const struct code_map_isds_error *map,
5489 const xmlChar *code, const xmlChar *message) {
5490 if (NULL == code) {
5491 isds_log_message(context,
5492 _("NULL status code passed to statuscode2isds_error()"));
5493 return IE_INVAL;
5496 if (NULL != map) {
5497 /* Check for known error codes */
5498 for (int i=0; map->codes[i] != NULL; i++) {
5499 if (!xmlStrcmp(code, map->codes[i])) {
5500 isds_log_message(context, _(map->meanings[i]));
5501 return map->errors[i];
5506 /* Other error */
5507 if (xmlStrcmp(code, BAD_CAST "0000")) {
5508 char *message_locale = _isds_utf82locale((char*)message);
5509 if (NULL == message_locale)
5510 isds_log_message(context, _("ISDS server returned unknown error"));
5511 else
5512 isds_log_message(context, message_locale);
5513 free(message_locale);
5514 return IE_ISDS;
5517 return IE_SUCCESS;
5519 #endif
5522 /* Change user password in ISDS.
5523 * User must supply old password, new password will takes effect after some
5524 * time, current session can continue. Password must fulfill some constraints.
5525 * @context is session context
5526 * @old_password is current password.
5527 * @new_password is requested new password
5528 * @otp auxiliary data required if one-time password authentication is in use,
5529 * defines OTP code (if known) and returns fine grade resolution of OTP
5530 * procedure. Pass NULL, if one-time password authentication is not needed.
5531 * Please note the @otp argument must match OTP method used at log-in time. See
5532 * isds_login() function for more details.
5533 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5534 * NULL, if you don't care.
5535 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5536 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5537 * awaiting OTP code that has been delivered by side channel to the user. */
5538 isds_error isds_change_password(struct isds_ctx *context,
5539 const char *old_password, const char *new_password,
5540 struct isds_otp *otp, char **refnumber) {
5541 isds_error err = IE_SUCCESS;
5542 #if HAVE_LIBCURL
5543 char *saved_url = NULL; /* No copy */
5544 #if HAVE_CURL_REAUTHORIZATION_BUG
5545 CURL *saved_curl = NULL; /* No copy */
5546 #endif
5547 xmlNsPtr isds_ns = NULL;
5548 xmlNodePtr request = NULL, node;
5549 xmlDocPtr response = NULL;
5550 xmlChar *code = NULL, *message = NULL;
5551 const xmlChar *codes[] = {
5552 BAD_CAST "1066",
5553 BAD_CAST "1067",
5554 BAD_CAST "1079",
5555 BAD_CAST "1080",
5556 BAD_CAST "1081",
5557 BAD_CAST "1082",
5558 BAD_CAST "1083",
5559 BAD_CAST "1090",
5560 BAD_CAST "1091",
5561 BAD_CAST "2300",
5562 BAD_CAST "9204"
5564 const char *meanings[] = {
5565 N_("Password length must be between 8 and 32 characters"),
5566 N_("Password cannot be reused"), /* Server does not distinguish 1067
5567 and 1091 on ChangePasswordOTP */
5568 N_("Password contains forbidden character"),
5569 N_("Password must contain at least one upper-case letter, "
5570 "one lower-case, and one digit"),
5571 N_("Password cannot contain sequence of three identical characters"),
5572 N_("Password cannot contain user identifier"),
5573 N_("Password is too simmple"),
5574 N_("Old password is not valid"),
5575 N_("Password cannot be reused"),
5576 N_("Unexpected error"),
5577 N_("LDAP update error")
5579 #endif
5581 if (!context) return IE_INVALID_CONTEXT;
5582 zfree(context->long_message);
5583 if (NULL != refnumber)
5584 zfree(*refnumber);
5585 if (NULL == old_password) {
5586 isds_log_message(context,
5587 _("Second argument (old password) of isds_change_password() "
5588 "is NULL"));
5589 return IE_INVAL;
5591 if (NULL == otp && NULL == new_password) {
5592 isds_log_message(context,
5593 _("Third argument (new password) of isds_change_password() "
5594 "is NULL"));
5595 return IE_INVAL;
5598 #if HAVE_LIBCURL
5599 /* Check if connection is established
5600 * TODO: This check should be done downstairs. */
5601 if (!context->curl) return IE_CONNECTION_CLOSED;
5603 if (context->otp && NULL == otp) {
5604 isds_log_message(context, _("If one-time password authentication "
5605 "method is in use, changing password requires one-time "
5606 "credentials either"));
5607 return IE_INVAL;
5610 /* Build ChangeISDSPassword request */
5611 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5612 BAD_CAST "ChangePasswordOTP");
5613 if (!request) {
5614 isds_log_message(context, (NULL == otp) ?
5615 _("Could not build ChangeISDSPassword request") :
5616 _("Could not build ChangePasswordOTP request"));
5617 return IE_ERROR;
5619 isds_ns = xmlNewNs(request,
5620 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5621 NULL);
5622 if(!isds_ns) {
5623 isds_log_message(context, _("Could not create ISDS name space"));
5624 xmlFreeNode(request);
5625 return IE_ERROR;
5627 xmlSetNs(request, isds_ns);
5629 INSERT_STRING(request, "dbOldPassword", old_password);
5630 INSERT_STRING(request, "dbNewPassword", new_password);
5632 if (NULL != otp) {
5633 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5634 switch (otp->method) {
5635 case OTP_HMAC:
5636 isds_log(ILF_SEC, ILL_INFO,
5637 _("Selected authentication method: "
5638 "HMAC-based one-time password\n"));
5639 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5640 break;
5641 case OTP_TIME:
5642 isds_log(ILF_SEC, ILL_INFO,
5643 _("Selected authentication method: "
5644 "Time-based one-time password\n"));
5645 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5646 if (otp->otp_code == NULL) {
5647 isds_log(ILF_SEC, ILL_INFO,
5648 _("OTP code has not been provided by "
5649 "application, requesting server for "
5650 "new one.\n"));
5651 err = _isds_request_totp_code(context, old_password, otp,
5652 refnumber);
5653 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5654 goto leave;
5656 } else {
5657 isds_log(ILF_SEC, ILL_INFO,
5658 _("OTP code has been provided by "
5659 "application, not requesting server "
5660 "for new one.\n"));
5662 break;
5663 default:
5664 isds_log_message(context,
5665 _("Unknown one-time password authentication "
5666 "method requested by application"));
5667 err = IE_ENUM;
5668 goto leave;
5671 /* Change URL temporarily for sending this request only */
5673 char *new_url = NULL;
5674 if ((err = _isds_build_url_from_context(context,
5675 "%1$.*2$sasws/changePassword", &new_url))) {
5676 goto leave;
5678 saved_url = context->url;
5679 context->url = new_url;
5682 /* Store credentials for sending this request only */
5683 context->otp_credentials = otp;
5684 _isds_discard_credentials(context, 0);
5685 if ((err = _isds_store_credentials(context, context->saved_username,
5686 old_password, NULL))) {
5687 _isds_discard_credentials(context, 0);
5688 goto leave;
5690 #if HAVE_CURL_REAUTHORIZATION_BUG
5691 saved_curl = context->curl;
5692 context->curl = curl_easy_init();
5693 if (NULL == context->curl) {
5694 err = IE_ERROR;
5695 goto leave;
5697 if (context->timeout) {
5698 err = isds_set_timeout(context, context->timeout);
5699 if (err) goto leave;
5701 #endif
5704 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5705 _("Sending ChangeISDSPassword request to ISDS\n") :
5706 _("Sending ChangePasswordOTP request to ISDS\n"));
5708 /* Sent request */
5709 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5710 request, &response, NULL, NULL);
5712 if (otp) {
5713 /* Remove temporal credentials */
5714 _isds_discard_credentials(context, 0);
5715 /* Detach pointer to OTP credentials from context */
5716 context->otp_credentials = NULL;
5717 /* Keep context->otp true to keep signaling this is OTP session */
5720 /* Destroy request */
5721 xmlFreeNode(request); request = NULL;
5723 if (err) {
5724 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5725 _("Processing ISDS response on ChangeISDSPassword "
5726 "request failed\n") :
5727 _("Processing ISDS response on ChangePasswordOTP "
5728 "request failed\n"));
5729 goto leave;
5732 /* Check for response status */
5733 err = isds_response_status(context,
5734 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5735 &code, &message, (xmlChar **)refnumber);
5736 if (err) {
5737 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5738 _("ISDS response on ChangeISDSPassword request is missing "
5739 "status\n") :
5740 _("ISDS response on ChangePasswordOTP request is missing "
5741 "status\n"));
5742 goto leave;
5745 /* Check for known error codes */
5746 for (size_t i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5747 if (!xmlStrcmp(code, codes[i])) {
5748 char *code_locale = _isds_utf82locale((char*)code);
5749 char *message_locale = _isds_utf82locale((char*)message);
5750 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5751 _("Server refused to change password on ChangeISDSPassword "
5752 "request (code=%s, message=%s)\n") :
5753 _("Server refused to change password on ChangePasswordOTP "
5754 "request (code=%s, message=%s)\n"),
5755 code_locale, message_locale);
5756 free(code_locale);
5757 free(message_locale);
5758 isds_log_message(context, _(meanings[i]));
5759 err = IE_INVAL;
5760 goto leave;
5764 /* Other error */
5765 if (xmlStrcmp(code, BAD_CAST "0000")) {
5766 char *code_locale = _isds_utf82locale((char*)code);
5767 char *message_locale = _isds_utf82locale((char*)message);
5768 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5769 _("Server refused to change password on ChangeISDSPassword "
5770 "request (code=%s, message=%s)\n") :
5771 _("Server refused to change password on ChangePasswordOTP "
5772 "request (code=%s, message=%s)\n"),
5773 code_locale, message_locale);
5774 isds_log_message(context, message_locale);
5775 free(code_locale);
5776 free(message_locale);
5777 err = IE_ISDS;
5778 goto leave;
5781 /* Otherwise password changed successfully */
5783 leave:
5784 if (NULL != saved_url) {
5785 /* Revert URL to original one */
5786 zfree(context->url);
5787 context->url = saved_url;
5789 #if HAVE_CURL_REAUTHORIZATION_BUG
5790 if (NULL != saved_curl) {
5791 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5792 context->curl = saved_curl;
5794 #endif
5796 free(code);
5797 free(message);
5798 xmlFreeDoc(response);
5799 xmlFreeNode(request);
5801 if (!err)
5802 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5803 _("Password changed successfully on ChangeISDSPassword "
5804 "request.\n") :
5805 _("Password changed successfully on ChangePasswordOTP "
5806 "request.\n"));
5807 #else /* not HAVE_LIBCURL */
5808 err = IE_NOTSUP;
5809 #endif
5811 return err;
5815 #if HAVE_LIBCURL
5816 /* Generic middle part with request sending and response check.
5817 * It sends prepared request and checks for error code.
5818 * @context is ISDS session context.
5819 * @service is ISDS service handler
5820 * @service_name is name in scope of given @service
5821 * @request is XML tree with request. Will be freed to save memory.
5822 * @response is XML document outputting ISDS response.
5823 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5824 * @map is mapping from status code to library error. Pass NULL if no special
5825 * handling is requested.
5826 * NULL, if you don't care. */
5827 static isds_error send_destroy_request_check_response(
5828 struct isds_ctx *context,
5829 const isds_service service, const xmlChar *service_name,
5830 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5831 const struct code_map_isds_error *map) {
5832 isds_error err = IE_SUCCESS;
5833 char *service_name_locale = NULL;
5834 xmlChar *code = NULL, *message = NULL;
5837 if (!context) return IE_INVALID_CONTEXT;
5838 if (!service_name || *service_name == '\0' || !request || !*request ||
5839 !response)
5840 return IE_INVAL;
5842 /* Check if connection is established
5843 * TODO: This check should be done downstairs. */
5844 if (!context->curl) return IE_CONNECTION_CLOSED;
5846 service_name_locale = _isds_utf82locale((char*) service_name);
5847 if (!service_name_locale) {
5848 err = IE_NOMEM;
5849 goto leave;
5852 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5853 service_name_locale);
5855 /* Send request */
5856 err = _isds(context, service, *request, response, NULL, NULL);
5857 xmlFreeNode(*request); *request = NULL;
5859 if (err) {
5860 isds_log(ILF_ISDS, ILL_DEBUG,
5861 _("Processing ISDS response on %s request failed\n"),
5862 service_name_locale);
5863 goto leave;
5866 /* Check for response status */
5867 err = isds_response_status(context, service, *response,
5868 &code, &message, refnumber);
5869 if (err) {
5870 isds_log(ILF_ISDS, ILL_DEBUG,
5871 _("ISDS response on %s request is missing status\n"),
5872 service_name_locale);
5873 goto leave;
5876 err = statuscode2isds_error(context, map, code, message);
5878 /* Request processed, but server failed */
5879 if (xmlStrcmp(code, BAD_CAST "0000")) {
5880 char *code_locale = _isds_utf82locale((char*) code);
5881 char *message_locale = _isds_utf82locale((char*) message);
5882 isds_log(ILF_ISDS, ILL_DEBUG,
5883 _("Server refused %s request (code=%s, message=%s)\n"),
5884 service_name_locale, code_locale, message_locale);
5885 free(code_locale);
5886 free(message_locale);
5887 goto leave;
5891 leave:
5892 free(code);
5893 free(message);
5894 if (err && *response) {
5895 xmlFreeDoc(*response);
5896 *response = NULL;
5898 if (*request) {
5899 xmlFreeNode(*request);
5900 *request = NULL;
5902 free(service_name_locale);
5904 return err;
5908 /* Generic bottom half with request sending.
5909 * It sends prepared request, checks for error code, destroys response and
5910 * request and log success or failure.
5911 * @context is ISDS session context.
5912 * @service is ISDS service handler
5913 * @service_name is name in scope of given @service
5914 * @request is XML tree with request. Will be freed to save memory.
5915 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5916 * NULL, if you don't care. */
5917 static isds_error send_request_check_drop_response(
5918 struct isds_ctx *context,
5919 const isds_service service, const xmlChar *service_name,
5920 xmlNodePtr *request, xmlChar **refnumber) {
5921 isds_error err = IE_SUCCESS;
5922 xmlDocPtr response = NULL;
5925 if (!context) return IE_INVALID_CONTEXT;
5926 if (!service_name || *service_name == '\0' || !request || !*request)
5927 return IE_INVAL;
5929 /* Send request and check response*/
5930 err = send_destroy_request_check_response(context,
5931 service, service_name, request, &response, refnumber, NULL);
5933 xmlFreeDoc(response);
5935 if (*request) {
5936 xmlFreeNode(*request);
5937 *request = NULL;
5940 if (!err) {
5941 char *service_name_locale = _isds_utf82locale((char *) service_name);
5942 isds_log(ILF_ISDS, ILL_DEBUG,
5943 _("%s request processed by server successfully.\n"),
5944 service_name_locale);
5945 free(service_name_locale);
5948 return err;
5952 /* Insert isds_credentials_delivery structure into XML request if not NULL
5953 * @context is session context
5954 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5955 * credentials delivery. The email field is passed.
5956 * @parent is XML element where to insert */
5957 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5958 const struct isds_credentials_delivery *credentials_delivery,
5959 xmlNodePtr parent) {
5960 isds_error err = IE_SUCCESS;
5961 xmlNodePtr node;
5963 if (!context) return IE_INVALID_CONTEXT;
5964 if (!parent) return IE_INVAL;
5966 if (credentials_delivery) {
5967 /* Following elements are valid only for services:
5968 * NewAccessData, AddDataBoxUser, CreateDataBox */
5969 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5970 INSERT_STRING(parent, "email", credentials_delivery->email);
5973 leave:
5974 return err;
5978 /* Extract credentials delivery from ISDS response.
5979 * @context is session context
5980 * @credentials_delivery is pointer to valid structure to fill in returned
5981 * user's password (and new log-in name). If NULL, do not extract the data.
5982 * @response is pointer to XML document with ISDS response
5983 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5984 * @return IE_SUCCESS even if new user name has not been found because it's not
5985 * clear whether it's returned always. */
5986 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5987 struct isds_credentials_delivery *credentials_delivery,
5988 xmlDocPtr response, const char *request_name) {
5989 isds_error err = IE_SUCCESS;
5990 xmlXPathContextPtr xpath_ctx = NULL;
5991 xmlXPathObjectPtr result = NULL;
5992 char *xpath_query = NULL;
5994 if (!context) return IE_INVALID_CONTEXT;
5995 if (credentials_delivery) {
5996 zfree(credentials_delivery->token);
5997 zfree(credentials_delivery->new_user_name);
5999 if (!response || !request_name || !*request_name) return IE_INVAL;
6002 /* Extract optional token */
6003 if (credentials_delivery) {
6004 xpath_ctx = xmlXPathNewContext(response);
6005 if (!xpath_ctx) {
6006 err = IE_ERROR;
6007 goto leave;
6009 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6010 err = IE_ERROR;
6011 goto leave;
6014 /* Verify root element */
6015 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
6016 request_name)) {
6017 err = IE_NOMEM;
6018 goto leave;
6020 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
6021 if (!result) {
6022 err = IE_ERROR;
6023 goto leave;
6025 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6026 char *request_name_locale = _isds_utf82locale(request_name);
6027 isds_log(ILF_ISDS, ILL_WARNING,
6028 _("Wrong element in ISDS response for %s request "
6029 "while extracting credentials delivery details\n"),
6030 request_name_locale);
6031 free(request_name_locale);
6032 err = IE_ERROR;
6033 goto leave;
6035 xpath_ctx->node = result->nodesetval->nodeTab[0];
6038 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6039 * optional. */
6040 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
6042 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
6043 if (!credentials_delivery->token) {
6044 char *request_name_locale = _isds_utf82locale(request_name);
6045 isds_log(ILF_ISDS, ILL_ERR,
6046 _("ISDS did not return token on %s request "
6047 "even if requested\n"), request_name_locale);
6048 free(request_name_locale);
6049 err = IE_ERROR;
6053 leave:
6054 free(xpath_query);
6055 xmlXPathFreeObject(result);
6056 xmlXPathFreeContext(xpath_ctx);
6058 return err;
6062 /* Build XSD:tCreateDBInput request type for box creating.
6063 * @context is session context
6064 * @request outputs built XML tree
6065 * @service_name is request name of SERVICE_DB_MANIPULATION service
6066 * @box is box description to create including single primary user (in case of
6067 * FO box type)
6068 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6069 * box, or contact address of PFO box owner)
6070 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6071 * @upper_box_id is optional ID of supper box if currently created box is
6072 * subordinated.
6073 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6074 * don't care.
6075 * @credentials_delivery is valid pointer if ISDS should return token that box
6076 * owner can use to obtain his new credentials in on-line way. Then valid email
6077 * member value should be supplied.
6078 * @approval is optional external approval of box manipulation */
6079 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
6080 xmlNodePtr *request, const xmlChar *service_name,
6081 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6082 const xmlChar *former_names, const xmlChar *upper_box_id,
6083 const xmlChar *ceo_label,
6084 const struct isds_credentials_delivery *credentials_delivery,
6085 const struct isds_approval *approval) {
6086 isds_error err = IE_SUCCESS;
6087 xmlNsPtr isds_ns = NULL;
6088 xmlNodePtr node, dbPrimaryUsers;
6089 xmlChar *string = NULL;
6090 const struct isds_list *item;
6093 if (!context) return IE_INVALID_CONTEXT;
6094 if (!request || !service_name || service_name[0] == '\0' || !box)
6095 return IE_INVAL;
6098 /* Build CreateDataBox-similar request */
6099 *request = xmlNewNode(NULL, service_name);
6100 if (!*request) {
6101 char *service_name_locale = _isds_utf82locale((char*) service_name);
6102 isds_printf_message(context, _("Could build %s request"),
6103 service_name_locale);
6104 free(service_name_locale);
6105 return IE_ERROR;
6107 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
6108 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
6109 if (!isds_ns) {
6110 isds_log_message(context, _("Could not create ISDS1 name space"));
6111 xmlFreeNode(*request);
6112 return IE_ERROR;
6114 } else {
6115 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6116 if (!isds_ns) {
6117 isds_log_message(context, _("Could not create ISDS name space"));
6118 xmlFreeNode(*request);
6119 return IE_ERROR;
6122 xmlSetNs(*request, isds_ns);
6124 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6125 err = insert_DbOwnerInfo(context, box, node);
6126 if (err) goto leave;
6128 /* Insert users */
6129 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6130 * verbose documentation allows none dbUserInfo */
6131 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6132 for (item = users; item; item = item->next) {
6133 if (item->data) {
6134 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6135 err = insert_DbUserInfo(context,
6136 (struct isds_DbUserInfo *) item->data, 1, node);
6137 if (err) goto leave;
6141 INSERT_STRING(*request, "dbFormerNames", former_names);
6142 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6143 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6145 err = insert_credentials_delivery(context, credentials_delivery, *request);
6146 if (err) goto leave;
6148 err = insert_GExtApproval(context, approval, *request);
6149 if (err) goto leave;
6151 leave:
6152 if (err) {
6153 xmlFreeNode(*request);
6154 *request = NULL;
6156 free(string);
6157 return err;
6159 #endif /* HAVE_LIBCURL */
6162 /* Create new box.
6163 * @context is session context
6164 * @box is box description to create including single primary user (in case of
6165 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6166 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6167 * box, or contact address of PFO box owner)
6168 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6169 * @upper_box_id is optional ID of supper box if currently created box is
6170 * subordinated.
6171 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6172 * @credentials_delivery is NULL if new password should be delivered off-line
6173 * to box owner. It is valid pointer if owner should obtain new password on-line
6174 * on dedicated web server. Then input @credentials_delivery.email value is
6175 * his e-mail address he must provide to dedicated web server together
6176 * with output reallocated @credentials_delivery.token member. Output
6177 * member @credentials_delivery.new_user_name is unused up on this call.
6178 * @approval is optional external approval of box manipulation
6179 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6180 * NULL, if you don't care.*/
6181 isds_error isds_add_box(struct isds_ctx *context,
6182 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6183 const char *former_names, const char *upper_box_id,
6184 const char *ceo_label,
6185 struct isds_credentials_delivery *credentials_delivery,
6186 const struct isds_approval *approval, char **refnumber) {
6187 isds_error err = IE_SUCCESS;
6188 #if HAVE_LIBCURL
6189 xmlNodePtr request = NULL;
6190 xmlDocPtr response = NULL;
6191 xmlXPathContextPtr xpath_ctx = NULL;
6192 xmlXPathObjectPtr result = NULL;
6193 #endif
6196 if (!context) return IE_INVALID_CONTEXT;
6197 zfree(context->long_message);
6198 if (credentials_delivery) {
6199 zfree(credentials_delivery->token);
6200 zfree(credentials_delivery->new_user_name);
6202 if (!box) return IE_INVAL;
6204 #if HAVE_LIBCURL
6205 /* Scratch box ID */
6206 zfree(box->dbID);
6208 /* Build CreateDataBox request */
6209 err = build_CreateDBInput_request(context,
6210 &request, BAD_CAST "CreateDataBox",
6211 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6212 (xmlChar *) ceo_label, credentials_delivery, approval);
6213 if (err) goto leave;
6215 /* Send it to server and process response */
6216 err = send_destroy_request_check_response(context,
6217 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6218 &response, (xmlChar **) refnumber, NULL);
6220 /* Extract box ID */
6221 xpath_ctx = xmlXPathNewContext(response);
6222 if (!xpath_ctx) {
6223 err = IE_ERROR;
6224 goto leave;
6226 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6227 err = IE_ERROR;
6228 goto leave;
6230 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6232 /* Extract optional token */
6233 err = extract_credentials_delivery(context, credentials_delivery, response,
6234 "CreateDataBox");
6236 leave:
6237 xmlXPathFreeObject(result);
6238 xmlXPathFreeContext(xpath_ctx);
6239 xmlFreeDoc(response);
6240 xmlFreeNode(request);
6242 if (!err) {
6243 isds_log(ILF_ISDS, ILL_DEBUG,
6244 _("CreateDataBox request processed by server successfully.\n"));
6246 #else /* not HAVE_LIBCURL */
6247 err = IE_NOTSUP;
6248 #endif
6250 return err;
6254 /* Notify ISDS about new PFO entity.
6255 * This function has no real effect.
6256 * @context is session context
6257 * @box is PFO description including single primary user.
6258 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6259 * @former_names is optional undocumented string. Pass NULL if you don't care.
6260 * @upper_box_id is optional ID of supper box if currently created box is
6261 * subordinated.
6262 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6263 * @approval is optional external approval of box manipulation
6264 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6265 * NULL, if you don't care.*/
6266 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6267 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6268 const char *former_names, const char *upper_box_id,
6269 const char *ceo_label, const struct isds_approval *approval,
6270 char **refnumber) {
6271 isds_error err = IE_SUCCESS;
6272 #if HAVE_LIBCURL
6273 xmlNodePtr request = NULL;
6274 #endif
6276 if (!context) return IE_INVALID_CONTEXT;
6277 zfree(context->long_message);
6278 if (!box) return IE_INVAL;
6280 #if HAVE_LIBCURL
6281 /* Build CreateDataBoxPFOInfo request */
6282 err = build_CreateDBInput_request(context,
6283 &request, BAD_CAST "CreateDataBoxPFOInfo",
6284 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6285 (xmlChar *) ceo_label, NULL, approval);
6286 if (err) goto leave;
6288 /* Send it to server and process response */
6289 err = send_request_check_drop_response(context,
6290 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6291 (xmlChar **) refnumber);
6292 /* XXX: XML Schema names output dbID element but textual documentation
6293 * states no box identifier is returned. */
6294 leave:
6295 xmlFreeNode(request);
6296 #else /* not HAVE_LIBCURL */
6297 err = IE_NOTSUP;
6298 #endif
6299 return err;
6303 /* Common implementation for removing given box.
6304 * @context is session context
6305 * @service_name is UTF-8 encoded name fo ISDS service
6306 * @box is box description to delete
6307 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6308 * carry sane value. If NULL, do not inject this information into request.
6309 * @approval is optional external approval of box manipulation
6310 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6311 * NULL, if you don't care.*/
6312 isds_error _isds_delete_box_common(struct isds_ctx *context,
6313 const xmlChar *service_name,
6314 const struct isds_DbOwnerInfo *box, const struct tm *since,
6315 const struct isds_approval *approval, char **refnumber) {
6316 isds_error err = IE_SUCCESS;
6317 #if HAVE_LIBCURL
6318 xmlNsPtr isds_ns = NULL;
6319 xmlNodePtr request = NULL;
6320 xmlNodePtr node;
6321 xmlChar *string = NULL;
6322 #endif
6325 if (!context) return IE_INVALID_CONTEXT;
6326 zfree(context->long_message);
6327 if (!service_name || !*service_name || !box) return IE_INVAL;
6330 #if HAVE_LIBCURL
6331 /* Build DeleteDataBox(Promptly) request */
6332 request = xmlNewNode(NULL, service_name);
6333 if (!request) {
6334 char *service_name_locale = _isds_utf82locale((char*)service_name);
6335 isds_printf_message(context,
6336 _("Could build %s request"), service_name_locale);
6337 free(service_name_locale);
6338 return IE_ERROR;
6340 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6341 if(!isds_ns) {
6342 isds_log_message(context, _("Could not create ISDS name space"));
6343 xmlFreeNode(request);
6344 return IE_ERROR;
6346 xmlSetNs(request, isds_ns);
6348 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6349 err = insert_DbOwnerInfo(context, box, node);
6350 if (err) goto leave;
6352 if (since) {
6353 err = tm2datestring(since, &string);
6354 if (err) {
6355 isds_log_message(context,
6356 _("Could not convert `since' argument to ISO date string"));
6357 goto leave;
6359 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6360 zfree(string);
6363 err = insert_GExtApproval(context, approval, request);
6364 if (err) goto leave;
6367 /* Send it to server and process response */
6368 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6369 service_name, &request, (xmlChar **) refnumber);
6371 leave:
6372 xmlFreeNode(request);
6373 free(string);
6374 #else /* not HAVE_LIBCURL */
6375 err = IE_NOTSUP;
6376 #endif
6377 return err;
6381 /* Remove given box permanently.
6382 * @context is session context
6383 * @box is box description to delete
6384 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6385 * carry sane value.
6386 * @approval is optional external approval of box manipulation
6387 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6388 * NULL, if you don't care.*/
6389 isds_error isds_delete_box(struct isds_ctx *context,
6390 const struct isds_DbOwnerInfo *box, const struct tm *since,
6391 const struct isds_approval *approval, char **refnumber) {
6392 if (!context) return IE_INVALID_CONTEXT;
6393 zfree(context->long_message);
6394 if (!box || !since) return IE_INVAL;
6396 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6397 box, since, approval, refnumber);
6401 /* Undocumented function.
6402 * @context is session context
6403 * @box is box description to delete
6404 * @approval is optional external approval of box manipulation
6405 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6406 * NULL, if you don't care.*/
6407 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6408 const struct isds_DbOwnerInfo *box,
6409 const struct isds_approval *approval, char **refnumber) {
6410 if (!context) return IE_INVALID_CONTEXT;
6411 zfree(context->long_message);
6412 if (!box) return IE_INVAL;
6414 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6415 box, NULL, approval, refnumber);
6419 /* Update data about given box.
6420 * @context is session context
6421 * @old_box current box description
6422 * @new_box are updated data about @old_box
6423 * @approval is optional external approval of box manipulation
6424 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6425 * NULL, if you don't care.*/
6426 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6427 const struct isds_DbOwnerInfo *old_box,
6428 const struct isds_DbOwnerInfo *new_box,
6429 const struct isds_approval *approval, char **refnumber) {
6430 isds_error err = IE_SUCCESS;
6431 #if HAVE_LIBCURL
6432 xmlNsPtr isds_ns = NULL;
6433 xmlNodePtr request = NULL;
6434 xmlNodePtr node;
6435 #endif
6438 if (!context) return IE_INVALID_CONTEXT;
6439 zfree(context->long_message);
6440 if (!old_box || !new_box) return IE_INVAL;
6443 #if HAVE_LIBCURL
6444 /* Build UpdateDataBoxDescr request */
6445 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6446 if (!request) {
6447 isds_log_message(context,
6448 _("Could build UpdateDataBoxDescr request"));
6449 return IE_ERROR;
6451 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6452 if(!isds_ns) {
6453 isds_log_message(context, _("Could not create ISDS name space"));
6454 xmlFreeNode(request);
6455 return IE_ERROR;
6457 xmlSetNs(request, isds_ns);
6459 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6460 err = insert_DbOwnerInfo(context, old_box, node);
6461 if (err) goto leave;
6463 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6464 err = insert_DbOwnerInfo(context, new_box, node);
6465 if (err) goto leave;
6467 err = insert_GExtApproval(context, approval, request);
6468 if (err) goto leave;
6471 /* Send it to server and process response */
6472 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6473 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6475 leave:
6476 xmlFreeNode(request);
6477 #else /* not HAVE_LIBCURL */
6478 err = IE_NOTSUP;
6479 #endif
6481 return err;
6485 #if HAVE_LIBCURL
6486 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6487 * code
6488 * @context is session context
6489 * @service is SOAP service
6490 * @service_name is name of request in @service
6491 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6492 * @box_id is box ID of interest
6493 * @approval is optional external approval of box manipulation
6494 * @response is server SOAP body response as XML document
6495 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6496 * NULL, if you don't care.
6497 * @return error coded from lower layer, context message will be set up
6498 * appropriately. */
6499 static isds_error build_send_dbid_request_check_response(
6500 struct isds_ctx *context, const isds_service service,
6501 const xmlChar *service_name, const xmlChar *box_id_element,
6502 const xmlChar *box_id, const struct isds_approval *approval,
6503 xmlDocPtr *response, xmlChar **refnumber) {
6505 isds_error err = IE_SUCCESS;
6506 char *service_name_locale = NULL, *box_id_locale = NULL;
6507 xmlNodePtr request = NULL, node;
6508 xmlNsPtr isds_ns = NULL;
6510 if (!context) return IE_INVALID_CONTEXT;
6511 if (!service_name || !box_id) return IE_INVAL;
6512 if (!response) return IE_INVAL;
6514 /* Free output argument */
6515 xmlFreeDoc(*response); *response = NULL;
6517 /* Prepare strings */
6518 service_name_locale = _isds_utf82locale((char*)service_name);
6519 if (!service_name_locale) {
6520 err = IE_NOMEM;
6521 goto leave;
6523 box_id_locale = _isds_utf82locale((char*)box_id);
6524 if (!box_id_locale) {
6525 err = IE_NOMEM;
6526 goto leave;
6529 /* Build request */
6530 request = xmlNewNode(NULL, service_name);
6531 if (!request) {
6532 isds_printf_message(context,
6533 _("Could not build %s request for %s box"), service_name_locale,
6534 box_id_locale);
6535 err = IE_ERROR;
6536 goto leave;
6538 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6539 if(!isds_ns) {
6540 isds_log_message(context, _("Could not create ISDS name space"));
6541 err = IE_ERROR;
6542 goto leave;
6544 xmlSetNs(request, isds_ns);
6546 /* Add XSD:tIdDbInput children */
6547 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6548 INSERT_STRING(request, box_id_element, box_id);
6549 err = insert_GExtApproval(context, approval, request);
6550 if (err) goto leave;
6552 /* Send request and check response*/
6553 err = send_destroy_request_check_response(context,
6554 service, service_name, &request, response, refnumber, NULL);
6556 leave:
6557 free(service_name_locale);
6558 free(box_id_locale);
6559 xmlFreeNode(request);
6560 return err;
6562 #endif /* HAVE_LIBCURL */
6565 /* Get data about all users assigned to given box.
6566 * @context is session context
6567 * @box_id is box ID
6568 * @users is automatically reallocated list of struct isds_DbUserInfo */
6569 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6570 struct isds_list **users) {
6571 isds_error err = IE_SUCCESS;
6572 #if HAVE_LIBCURL
6573 xmlDocPtr response = NULL;
6574 xmlXPathContextPtr xpath_ctx = NULL;
6575 xmlXPathObjectPtr result = NULL;
6576 int i;
6577 struct isds_list *item, *prev_item = NULL;
6578 #endif
6580 if (!context) return IE_INVALID_CONTEXT;
6581 zfree(context->long_message);
6582 if (!users || !box_id) return IE_INVAL;
6583 isds_list_free(users);
6586 #if HAVE_LIBCURL
6587 /* Do request and check for success */
6588 err = build_send_dbid_request_check_response(context,
6589 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6590 BAD_CAST box_id, NULL, &response, NULL);
6591 if (err) goto leave;
6594 /* Extract data */
6595 /* Prepare structure */
6596 xpath_ctx = xmlXPathNewContext(response);
6597 if (!xpath_ctx) {
6598 err = IE_ERROR;
6599 goto leave;
6601 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6602 err = IE_ERROR;
6603 goto leave;
6606 /* Set context node */
6607 result = xmlXPathEvalExpression(BAD_CAST
6608 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6609 xpath_ctx);
6610 if (!result) {
6611 err = IE_ERROR;
6612 goto leave;
6614 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6615 /* Iterate over all users */
6616 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6618 /* Prepare structure */
6619 item = calloc(1, sizeof(*item));
6620 if (!item) {
6621 err = IE_NOMEM;
6622 goto leave;
6624 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6625 if (i == 0) *users = item;
6626 else prev_item->next = item;
6627 prev_item = item;
6629 /* Extract it */
6630 xpath_ctx->node = result->nodesetval->nodeTab[i];
6631 err = extract_DbUserInfo(context,
6632 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6633 if (err) goto leave;
6637 leave:
6638 if (err) {
6639 isds_list_free(users);
6642 xmlXPathFreeObject(result);
6643 xmlXPathFreeContext(xpath_ctx);
6644 xmlFreeDoc(response);
6646 if (!err)
6647 isds_log(ILF_ISDS, ILL_DEBUG,
6648 _("GetDataBoxUsers request processed by server "
6649 "successfully.\n"));
6650 #else /* not HAVE_LIBCURL */
6651 err = IE_NOTSUP;
6652 #endif
6654 return err;
6658 /* Update data about user assigned to given box.
6659 * @context is session context
6660 * @box is box identification
6661 * @old_user identifies user to update, aifo_ticket member is ignored
6662 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6663 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6664 * NULL, if you don't care.*/
6665 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6666 const struct isds_DbOwnerInfo *box,
6667 const struct isds_DbUserInfo *old_user,
6668 const struct isds_DbUserInfo *new_user,
6669 char **refnumber) {
6670 isds_error err = IE_SUCCESS;
6671 #if HAVE_LIBCURL
6672 xmlNsPtr isds_ns = NULL;
6673 xmlNodePtr request = NULL;
6674 xmlNodePtr node;
6675 #endif
6678 if (!context) return IE_INVALID_CONTEXT;
6679 zfree(context->long_message);
6680 if (!box || !old_user || !new_user) return IE_INVAL;
6683 #if HAVE_LIBCURL
6684 /* Build UpdateDataBoxUser request */
6685 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6686 if (!request) {
6687 isds_log_message(context,
6688 _("Could build UpdateDataBoxUser request"));
6689 return IE_ERROR;
6691 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6692 if(!isds_ns) {
6693 isds_log_message(context, _("Could not create ISDS name space"));
6694 xmlFreeNode(request);
6695 return IE_ERROR;
6697 xmlSetNs(request, isds_ns);
6699 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6700 err = insert_DbOwnerInfo(context, box, node);
6701 if (err) goto leave;
6703 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6704 err = insert_DbUserInfo(context, old_user, 0, node);
6705 if (err) goto leave;
6707 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6708 err = insert_DbUserInfo(context, new_user, 0, node);
6709 if (err) goto leave;
6711 /* Send it to server and process response */
6712 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6713 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6715 leave:
6716 xmlFreeNode(request);
6717 #else /* not HAVE_LIBCURL */
6718 err = IE_NOTSUP;
6719 #endif
6721 return err;
6725 /* Undocumented function.
6726 * @context is session context
6727 * @box_id is UTF-8 encoded box identifier
6728 * @token is UTF-8 encoded temporary password
6729 * @user_id outputs UTF-8 encoded reallocated user identifier
6730 * @password outpus UTF-8 encoded reallocated user password
6731 * Output arguments will be nulled in case of error */
6732 isds_error isds_activate(struct isds_ctx *context,
6733 const char *box_id, const char *token,
6734 char **user_id, char **password) {
6735 isds_error err = IE_SUCCESS;
6736 #if HAVE_LIBCURL
6737 xmlNsPtr isds_ns = NULL;
6738 xmlNodePtr request = NULL, node;
6739 xmlDocPtr response = NULL;
6740 xmlXPathContextPtr xpath_ctx = NULL;
6741 xmlXPathObjectPtr result = NULL;
6742 #endif
6745 if (!context) return IE_INVALID_CONTEXT;
6746 zfree(context->long_message);
6748 if (user_id) zfree(*user_id);
6749 if (password) zfree(*password);
6751 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6754 #if HAVE_LIBCURL
6755 /* Build Activate request */
6756 request = xmlNewNode(NULL, BAD_CAST "Activate");
6757 if (!request) {
6758 isds_log_message(context, _("Could build Activate request"));
6759 return IE_ERROR;
6761 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6762 if(!isds_ns) {
6763 isds_log_message(context, _("Could not create ISDS name space"));
6764 xmlFreeNode(request);
6765 return IE_ERROR;
6767 xmlSetNs(request, isds_ns);
6769 INSERT_STRING(request, "dbAccessDataId", token);
6770 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6771 INSERT_STRING(request, "dbID", box_id);
6774 /* Send request and check response*/
6775 err = send_destroy_request_check_response(context,
6776 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6777 &response, NULL, NULL);
6778 if (err) goto leave;
6781 /* Extract data */
6782 xpath_ctx = xmlXPathNewContext(response);
6783 if (!xpath_ctx) {
6784 err = IE_ERROR;
6785 goto leave;
6787 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6788 err = IE_ERROR;
6789 goto leave;
6791 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6792 xpath_ctx);
6793 if (!result) {
6794 err = IE_ERROR;
6795 goto leave;
6797 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6798 isds_log_message(context, _("Missing ActivateResponse element"));
6799 err = IE_ISDS;
6800 goto leave;
6802 if (result->nodesetval->nodeNr > 1) {
6803 isds_log_message(context, _("Multiple ActivateResponse element"));
6804 err = IE_ISDS;
6805 goto leave;
6807 xpath_ctx->node = result->nodesetval->nodeTab[0];
6808 xmlXPathFreeObject(result); result = NULL;
6810 EXTRACT_STRING("isds:userId", *user_id);
6811 if (!*user_id)
6812 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6813 "but did not return `userId' element.\n"));
6815 EXTRACT_STRING("isds:password", *password);
6816 if (!*password)
6817 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6818 "but did not return `password' element.\n"));
6820 leave:
6821 xmlXPathFreeObject(result);
6822 xmlXPathFreeContext(xpath_ctx);
6823 xmlFreeDoc(response);
6824 xmlFreeNode(request);
6826 if (!err)
6827 isds_log(ILF_ISDS, ILL_DEBUG,
6828 _("Activate request processed by server successfully.\n"));
6829 #else /* not HAVE_LIBCURL */
6830 err = IE_NOTSUP;
6831 #endif
6833 return err;
6837 /* Reset credentials of user assigned to given box.
6838 * @context is session context
6839 * @box is box identification
6840 * @user identifies user to reset password, aifo_ticket member is ignored
6841 * @fee_paid is true if fee has been paid, false otherwise
6842 * @approval is optional external approval of box manipulation
6843 * @credentials_delivery is NULL if new password should be delivered off-line
6844 * to the user. It is valid pointer if user should obtain new password on-line
6845 * on dedicated web server. Then input @credentials_delivery.email value is
6846 * user's e-mail address user must provide to dedicated web server together
6847 * with @credentials_delivery.token. The output reallocated token user needs
6848 * to use to authorize on the web server to view his new password. Output
6849 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6850 * ISDS changed up on this call. (No reason why server could change the name
6851 * is known now.)
6852 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6853 * NULL, if you don't care.*/
6854 isds_error isds_reset_password(struct isds_ctx *context,
6855 const struct isds_DbOwnerInfo *box,
6856 const struct isds_DbUserInfo *user,
6857 const _Bool fee_paid, const struct isds_approval *approval,
6858 struct isds_credentials_delivery *credentials_delivery,
6859 char **refnumber) {
6860 isds_error err = IE_SUCCESS;
6861 #if HAVE_LIBCURL
6862 xmlNsPtr isds_ns = NULL;
6863 xmlNodePtr request = NULL, node;
6864 xmlDocPtr response = NULL;
6865 #endif
6868 if (!context) return IE_INVALID_CONTEXT;
6869 zfree(context->long_message);
6871 if (credentials_delivery) {
6872 zfree(credentials_delivery->token);
6873 zfree(credentials_delivery->new_user_name);
6875 if (!box || !user) return IE_INVAL;
6878 #if HAVE_LIBCURL
6879 /* Build NewAccessData request */
6880 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6881 if (!request) {
6882 isds_log_message(context,
6883 _("Could build NewAccessData request"));
6884 return IE_ERROR;
6886 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6887 if(!isds_ns) {
6888 isds_log_message(context, _("Could not create ISDS name space"));
6889 xmlFreeNode(request);
6890 return IE_ERROR;
6892 xmlSetNs(request, isds_ns);
6894 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6895 err = insert_DbOwnerInfo(context, box, node);
6896 if (err) goto leave;
6898 INSERT_ELEMENT(node, request, "dbUserInfo");
6899 err = insert_DbUserInfo(context, user, 0, node);
6900 if (err) goto leave;
6902 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6904 err = insert_credentials_delivery(context, credentials_delivery, request);
6905 if (err) goto leave;
6907 err = insert_GExtApproval(context, approval, request);
6908 if (err) goto leave;
6910 /* Send request and check response*/
6911 err = send_destroy_request_check_response(context,
6912 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6913 &response, (xmlChar **) refnumber, NULL);
6914 if (err) goto leave;
6917 /* Extract optional token */
6918 err = extract_credentials_delivery(context, credentials_delivery,
6919 response, "NewAccessData");
6921 leave:
6922 xmlFreeDoc(response);
6923 xmlFreeNode(request);
6925 if (!err)
6926 isds_log(ILF_ISDS, ILL_DEBUG,
6927 _("NewAccessData request processed by server "
6928 "successfully.\n"));
6929 #else /* not HAVE_LIBCURL */
6930 err = IE_NOTSUP;
6931 #endif
6933 return err;
6937 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6938 * code, destroy response and log success.
6939 * @context is ISDS session context.
6940 * @service_name is name of SERVICE_DB_MANIPULATION service
6941 * @box is box identification
6942 * @user identifies user to remove
6943 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
6944 * @credentials_delivery is NULL if new user's password should be delivered
6945 * off-line to the user. It is valid pointer if user should obtain new
6946 * password on-line on dedicated web server. Then input
6947 * @credentials_delivery.email value is user's e-mail address user must
6948 * provide to dedicated web server together with @credentials_delivery.token.
6949 * The output reallocated token user needs to use to authorize on the web
6950 * server to view his new password. Output reallocated
6951 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6952 * assingned or changed up on this call.
6953 * @approval is optional external approval of box manipulation
6954 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6955 * NULL, if you don't care. */
6956 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6957 struct isds_ctx *context, const xmlChar *service_name,
6958 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6959 _Bool honor_aifo_ticket,
6960 struct isds_credentials_delivery *credentials_delivery,
6961 const struct isds_approval *approval, xmlChar **refnumber) {
6962 isds_error err = IE_SUCCESS;
6963 #if HAVE_LIBCURL
6964 xmlNsPtr isds_ns = NULL;
6965 xmlNodePtr request = NULL, node;
6966 xmlDocPtr response = NULL;
6967 #endif
6970 if (!context) return IE_INVALID_CONTEXT;
6971 zfree(context->long_message);
6972 if (credentials_delivery) {
6973 zfree(credentials_delivery->token);
6974 zfree(credentials_delivery->new_user_name);
6976 if (!service_name || service_name[0] == '\0' || !box || !user)
6977 return IE_INVAL;
6980 #if HAVE_LIBCURL
6981 /* Build NewAccessData or similar request */
6982 request = xmlNewNode(NULL, service_name);
6983 if (!request) {
6984 char *service_name_locale = _isds_utf82locale((char *) service_name);
6985 isds_printf_message(context, _("Could not build %s request"),
6986 service_name_locale);
6987 free(service_name_locale);
6988 return IE_ERROR;
6990 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6991 if(!isds_ns) {
6992 isds_log_message(context, _("Could not create ISDS name space"));
6993 xmlFreeNode(request);
6994 return IE_ERROR;
6996 xmlSetNs(request, isds_ns);
6998 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6999 err = insert_DbOwnerInfo(context, box, node);
7000 if (err) goto leave;
7002 INSERT_ELEMENT(node, request, "dbUserInfo");
7003 err = insert_DbUserInfo(context, user, honor_aifo_ticket, node);
7004 if (err) goto leave;
7006 err = insert_credentials_delivery(context, credentials_delivery, request);
7007 if (err) goto leave;
7009 err = insert_GExtApproval(context, approval, request);
7010 if (err) goto leave;
7013 /* Send request and check response*/
7014 err = send_destroy_request_check_response(context,
7015 SERVICE_DB_MANIPULATION, service_name, &request, &response,
7016 refnumber, NULL);
7018 xmlFreeNode(request);
7019 request = NULL;
7021 /* Pick up credentials_delivery if requested */
7022 err = extract_credentials_delivery(context, credentials_delivery, response,
7023 (char *)service_name);
7025 leave:
7026 xmlFreeDoc(response);
7027 if (request) xmlFreeNode(request);
7029 if (!err) {
7030 char *service_name_locale = _isds_utf82locale((char *) service_name);
7031 isds_log(ILF_ISDS, ILL_DEBUG,
7032 _("%s request processed by server successfully.\n"),
7033 service_name_locale);
7034 free(service_name_locale);
7036 #else /* not HAVE_LIBCURL */
7037 err = IE_NOTSUP;
7038 #endif
7040 return err;
7044 /* Assign new user to given box.
7045 * @context is session context
7046 * @box is box identification
7047 * @user defines new user to add
7048 * @credentials_delivery is NULL if new user's password should be delivered
7049 * off-line to the user. It is valid pointer if user should obtain new
7050 * password on-line on dedicated web server. Then input
7051 * @credentials_delivery.email value is user's e-mail address user must
7052 * provide to dedicated web server together with @credentials_delivery.token.
7053 * The output reallocated token user needs to use to authorize on the web
7054 * server to view his new password. Output reallocated
7055 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7056 * assingned up on this call.
7057 * @approval is optional external approval of box manipulation
7058 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7059 * NULL, if you don't care.*/
7060 isds_error isds_add_user(struct isds_ctx *context,
7061 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7062 struct isds_credentials_delivery *credentials_delivery,
7063 const struct isds_approval *approval, char **refnumber) {
7064 return build_send_manipulationboxuser_request_check_drop_response(context,
7065 BAD_CAST "AddDataBoxUser", box, user, 1, credentials_delivery,
7066 approval, (xmlChar **) refnumber);
7070 /* Remove user assigned to given box.
7071 * @context is session context
7072 * @box is box identification
7073 * @user identifies user to remove, aifo_ticket member is ignored
7074 * @approval is optional external approval of box manipulation
7075 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7076 * NULL, if you don't care.*/
7077 isds_error isds_delete_user(struct isds_ctx *context,
7078 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7079 const struct isds_approval *approval, char **refnumber) {
7080 return build_send_manipulationboxuser_request_check_drop_response(context,
7081 BAD_CAST "DeleteDataBoxUser", box, user, 0, NULL, approval,
7082 (xmlChar **) refnumber);
7086 /* Get list of boxes in ZIP archive.
7087 * @context is session context
7088 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7089 * System recognizes following values currently: ALL (all boxes), UPG
7090 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7091 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7092 * commercial messages). This argument is a string because specification
7093 * states new values can appear in the future. Not all list types are
7094 * available to all users.
7095 * @buffer is automatically reallocated memory to store the list of boxes. The
7096 * list is zipped CSV file.
7097 * @buffer_length is size of @buffer data in bytes.
7098 * In case of error @buffer will be freed and @buffer_length will be
7099 * undefined.*/
7100 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7101 const char *list_identifier, void **buffer, size_t *buffer_length) {
7102 isds_error err = IE_SUCCESS;
7103 #if HAVE_LIBCURL
7104 xmlNsPtr isds_ns = NULL;
7105 xmlNodePtr request = NULL, node;
7106 xmlDocPtr response = NULL;
7107 xmlXPathContextPtr xpath_ctx = NULL;
7108 xmlXPathObjectPtr result = NULL;
7109 char *string = NULL;
7110 #endif
7113 if (!context) return IE_INVALID_CONTEXT;
7114 zfree(context->long_message);
7115 if (buffer) zfree(*buffer);
7116 if (!buffer || !buffer_length) return IE_INVAL;
7119 #if HAVE_LIBCURL
7120 /* Check if connection is established
7121 * TODO: This check should be done downstairs. */
7122 if (!context->curl) return IE_CONNECTION_CLOSED;
7125 /* Build AuthenticateMessage request */
7126 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7127 if (!request) {
7128 isds_log_message(context,
7129 _("Could not build GetDataBoxList request"));
7130 return IE_ERROR;
7132 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7133 if(!isds_ns) {
7134 isds_log_message(context, _("Could not create ISDS name space"));
7135 xmlFreeNode(request);
7136 return IE_ERROR;
7138 xmlSetNs(request, isds_ns);
7139 INSERT_STRING(request, "dblType", list_identifier);
7141 /* Send request to server and process response */
7142 err = send_destroy_request_check_response(context,
7143 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7144 &response, NULL, NULL);
7145 if (err) goto leave;
7148 /* Extract Base-64 encoded ZIP file */
7149 xpath_ctx = xmlXPathNewContext(response);
7150 if (!xpath_ctx) {
7151 err = IE_ERROR;
7152 goto leave;
7154 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7155 err = IE_ERROR;
7156 goto leave;
7158 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7160 /* Decode non-empty archive */
7161 if (string && string[0] != '\0') {
7162 *buffer_length = _isds_b64decode(string, buffer);
7163 if (*buffer_length == (size_t) -1) {
7164 isds_printf_message(context,
7165 _("Error while Base64-decoding box list archive"));
7166 err = IE_ERROR;
7167 goto leave;
7172 leave:
7173 free(string);
7174 xmlXPathFreeObject(result);
7175 xmlXPathFreeContext(xpath_ctx);
7176 xmlFreeDoc(response);
7177 xmlFreeNode(request);
7179 if (!err) {
7180 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7181 "processed by server successfully.\n"));
7183 #else /* not HAVE_LIBCURL */
7184 err = IE_NOTSUP;
7185 #endif
7187 return err;
7191 /* Find boxes suiting given criteria.
7192 * @criteria is filter. You should fill in at least some members.
7193 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7194 * possibly empty. Input NULL or valid old structure.
7195 * @return:
7196 * IE_SUCCESS if search succeeded, @boxes contains useful data
7197 * IE_NOEXIST if no such box exists, @boxes will be NULL
7198 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7199 * contains still valid data
7200 * other code if something bad happens. @boxes will be NULL. */
7201 isds_error isds_FindDataBox(struct isds_ctx *context,
7202 const struct isds_DbOwnerInfo *criteria,
7203 struct isds_list **boxes) {
7204 isds_error err = IE_SUCCESS;
7205 #if HAVE_LIBCURL
7206 _Bool truncated = 0;
7207 xmlNsPtr isds_ns = NULL;
7208 xmlNodePtr request = NULL;
7209 xmlDocPtr response = NULL;
7210 xmlChar *code = NULL, *message = NULL;
7211 xmlNodePtr db_owner_info;
7212 xmlXPathContextPtr xpath_ctx = NULL;
7213 xmlXPathObjectPtr result = NULL;
7214 xmlChar *string = NULL;
7215 #endif
7218 if (!context) return IE_INVALID_CONTEXT;
7219 zfree(context->long_message);
7220 if (!boxes) return IE_INVAL;
7221 isds_list_free(boxes);
7223 if (!criteria) {
7224 return IE_INVAL;
7227 #if HAVE_LIBCURL
7228 /* Check if connection is established
7229 * TODO: This check should be done downstairs. */
7230 if (!context->curl) return IE_CONNECTION_CLOSED;
7233 /* Build FindDataBox request */
7234 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
7235 if (!request) {
7236 isds_log_message(context,
7237 _("Could build FindDataBox request"));
7238 return IE_ERROR;
7240 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7241 if(!isds_ns) {
7242 isds_log_message(context, _("Could not create ISDS name space"));
7243 xmlFreeNode(request);
7244 return IE_ERROR;
7246 xmlSetNs(request, isds_ns);
7247 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7248 if (!db_owner_info) {
7249 isds_log_message(context, _("Could not add dbOwnerInfo child to "
7250 "FindDataBox element"));
7251 xmlFreeNode(request);
7252 return IE_ERROR;
7255 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
7256 if (err) goto leave;
7259 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
7261 /* Sent request */
7262 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7264 /* Destroy request */
7265 xmlFreeNode(request); request = NULL;
7267 if (err) {
7268 isds_log(ILF_ISDS, ILL_DEBUG,
7269 _("Processing ISDS response on FindDataBox "
7270 "request failed\n"));
7271 goto leave;
7274 /* Check for response status */
7275 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7276 &code, &message, NULL);
7277 if (err) {
7278 isds_log(ILF_ISDS, ILL_DEBUG,
7279 _("ISDS response on FindDataBox request is missing status\n"));
7280 goto leave;
7283 /* Request processed, but nothing found */
7284 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7285 !xmlStrcmp(code, BAD_CAST "5001")) {
7286 char *code_locale = _isds_utf82locale((char*)code);
7287 char *message_locale = _isds_utf82locale((char*)message);
7288 isds_log(ILF_ISDS, ILL_DEBUG,
7289 _("Server did not found any box on FindDataBox request "
7290 "(code=%s, message=%s)\n"), code_locale, message_locale);
7291 isds_log_message(context, message_locale);
7292 free(code_locale);
7293 free(message_locale);
7294 err = IE_NOEXIST;
7295 goto leave;
7298 /* Warning, not a error */
7299 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7300 char *code_locale = _isds_utf82locale((char*)code);
7301 char *message_locale = _isds_utf82locale((char*)message);
7302 isds_log(ILF_ISDS, ILL_DEBUG,
7303 _("Server truncated response on FindDataBox request "
7304 "(code=%s, message=%s)\n"), code_locale, message_locale);
7305 isds_log_message(context, message_locale);
7306 free(code_locale);
7307 free(message_locale);
7308 truncated = 1;
7311 /* Other error */
7312 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7313 char *code_locale = _isds_utf82locale((char*)code);
7314 char *message_locale = _isds_utf82locale((char*)message);
7315 isds_log(ILF_ISDS, ILL_DEBUG,
7316 _("Server refused FindDataBox request "
7317 "(code=%s, message=%s)\n"), code_locale, message_locale);
7318 isds_log_message(context, message_locale);
7319 free(code_locale);
7320 free(message_locale);
7321 err = IE_ISDS;
7322 goto leave;
7325 xpath_ctx = xmlXPathNewContext(response);
7326 if (!xpath_ctx) {
7327 err = IE_ERROR;
7328 goto leave;
7330 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7331 err = IE_ERROR;
7332 goto leave;
7335 /* Extract boxes if they present */
7336 result = xmlXPathEvalExpression(BAD_CAST
7337 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7338 xpath_ctx);
7339 if (!result) {
7340 err = IE_ERROR;
7341 goto leave;
7343 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7344 struct isds_list *item, *prev_item = NULL;
7345 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7346 item = calloc(1, sizeof(*item));
7347 if (!item) {
7348 err = IE_NOMEM;
7349 goto leave;
7352 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7353 if (i == 0) *boxes = item;
7354 else prev_item->next = item;
7355 prev_item = item;
7357 xpath_ctx->node = result->nodesetval->nodeTab[i];
7358 err = extract_DbOwnerInfo(context,
7359 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7360 if (err) goto leave;
7364 leave:
7365 if (err) {
7366 isds_list_free(boxes);
7367 } else {
7368 if (truncated) err = IE_2BIG;
7371 free(string);
7372 xmlFreeNode(request);
7373 xmlXPathFreeObject(result);
7374 xmlXPathFreeContext(xpath_ctx);
7376 free(code);
7377 free(message);
7378 xmlFreeDoc(response);
7380 if (!err)
7381 isds_log(ILF_ISDS, ILL_DEBUG,
7382 _("FindDataBox request processed by server successfully.\n"));
7383 #else /* not HAVE_LIBCURL */
7384 err = IE_NOTSUP;
7385 #endif
7387 return err;
7391 #if HAVE_LIBCURL
7392 /* Convert a string with match markers into a plain string with list of
7393 * pointers to the matches
7394 * @string is an UTF-8 encoded non-constant string with match markers
7395 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7396 * The markers will be removed from the string.
7397 * @starts is a reallocated list of static pointers into the @string pointing
7398 * to places where match start markers occured.
7399 * @ends is a reallocated list of static pointers into the @string pointing
7400 * to places where match end markers occured.
7401 * @return IE_SUCCESS in case of no failure. */
7402 static isds_error interpret_matches(xmlChar *string,
7403 struct isds_list **starts, struct isds_list **ends) {
7404 isds_error err = IE_SUCCESS;
7405 xmlChar *pointer, *destination, *source;
7406 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7408 isds_list_free(starts);
7409 isds_list_free(ends);
7410 if (NULL == starts || NULL == ends) return IE_INVAL;
7411 if (NULL == string) return IE_SUCCESS;
7413 for (pointer = string; *pointer != '\0';) {
7414 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7415 /* Remove the start marker */
7416 for (source = pointer + 14, destination = pointer;
7417 *source != '\0'; source++, destination++) {
7418 *destination = *source;
7420 *destination = '\0';
7421 /* Append the pointer into the list */
7422 item = calloc(1, sizeof(*item));
7423 if (!item) {
7424 err = IE_NOMEM;
7425 goto leave;
7427 item->destructor = (void (*)(void **))NULL;
7428 item->data = pointer;
7429 if (NULL == prev_start) *starts = item;
7430 else prev_start->next = item;
7431 prev_start = item;
7432 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7433 /* Remove the end marker */
7434 for (source = pointer + 12, destination = pointer;
7435 *source != '\0'; source++, destination++) {
7436 *destination = *source;
7438 *destination = '\0';
7439 /* Append the pointer into the list */
7440 item = calloc(1, sizeof(*item));
7441 if (!item) {
7442 err = IE_NOMEM;
7443 goto leave;
7445 item->destructor = (void (*)(void **))NULL;
7446 item->data = pointer;
7447 if (NULL == prev_end) *ends = item;
7448 else prev_end->next = item;
7449 prev_end = item;
7450 } else {
7451 pointer++;
7455 leave:
7456 if (err) {
7457 isds_list_free(starts);
7458 isds_list_free(ends);
7460 return err;
7464 /* Convert isds:dbResult XML tree into structure
7465 * @context is ISDS context.
7466 * @fulltext_result is automatically reallocated found box structure.
7467 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7468 * @collect_matches is true to interpret match markers.
7469 * In case of error @result will be freed. */
7470 static isds_error extract_dbResult(struct isds_ctx *context,
7471 struct isds_fulltext_result **fulltext_result,
7472 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7473 isds_error err = IE_SUCCESS;
7474 xmlXPathObjectPtr result = NULL;
7475 char *string = NULL;
7477 if (NULL == context) return IE_INVALID_CONTEXT;
7478 if (NULL == fulltext_result) return IE_INVAL;
7479 isds_fulltext_result_free(fulltext_result);
7480 if (!xpath_ctx) return IE_INVAL;
7483 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7484 if (NULL == *fulltext_result) {
7485 err = IE_NOMEM;
7486 goto leave;
7489 /* Extract data */
7490 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7492 EXTRACT_STRING("isds:dbType", string);
7493 if (NULL == string) {
7494 err = IE_ISDS;
7495 isds_log_message(context, _("Empty isds:dbType element"));
7496 goto leave;
7498 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7499 if (err) {
7500 if (err == IE_ENUM) {
7501 err = IE_ISDS;
7502 char *string_locale = _isds_utf82locale(string);
7503 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7504 string_locale);
7505 free(string_locale);
7507 goto leave;
7509 zfree(string);
7511 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7512 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7514 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7515 if (err) goto leave;
7517 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7518 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7519 (*fulltext_result)->dbEffectiveOVM);
7521 EXTRACT_STRING("isds:dbSendOptions", string);
7522 if (NULL == string) {
7523 err = IE_ISDS;
7524 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7525 goto leave;
7527 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7528 (*fulltext_result)->active = 1;
7529 (*fulltext_result)->public_sending = 1;
7530 (*fulltext_result)->commercial_sending = 0;
7531 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7532 (*fulltext_result)->active = 1;
7533 (*fulltext_result)->public_sending = 1;
7534 (*fulltext_result)->commercial_sending = 1;
7535 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7536 (*fulltext_result)->active = 1;
7537 (*fulltext_result)->public_sending = 0;
7538 (*fulltext_result)->commercial_sending = 1;
7539 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7540 (*fulltext_result)->active = 1;
7541 (*fulltext_result)->public_sending = 0;
7542 (*fulltext_result)->commercial_sending = 0;
7543 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7544 (*fulltext_result)->active = 0;
7545 (*fulltext_result)->public_sending = 0;
7546 (*fulltext_result)->commercial_sending = 0;
7547 } else {
7548 err = IE_ISDS;
7549 char *string_locale = _isds_utf82locale(string);
7550 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7551 string_locale);
7552 free(string_locale);
7553 goto leave;
7555 zfree(string);
7557 /* Interpret match marks */
7558 if (collect_matches) {
7559 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7560 &((*fulltext_result)->name_match_start),
7561 &((*fulltext_result)->name_match_end));
7562 if (err) goto leave;
7563 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7564 &((*fulltext_result)->address_match_start),
7565 &((*fulltext_result)->address_match_end));
7566 if (err) goto leave;
7569 leave:
7570 if (err) isds_fulltext_result_free(fulltext_result);
7571 free(string);
7572 xmlXPathFreeObject(result);
7573 return err;
7575 #endif /* HAVE_LIBCURL */
7578 /* Find boxes matching a given full-text criteria.
7579 * @context is a session context
7580 * @query is a non-empty string which consists of words to search
7581 * @target selects box attributes to search for @query words. Pass NULL if you
7582 * don't care.
7583 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7584 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7585 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7586 * which is DBTYPE_SYSTEM.
7587 * @page_size defines count of boxes to constitute a response page. It counts
7588 * from zero. Pass NULL to let server to use a default value (50 now).
7589 * @page_number defines ordinar number of the response page to return. It
7590 * counts from zero. Pass NULL to let server to use a default value (0 now).
7591 * @track_matches points to true for marking @query words found in the box
7592 * attributes. It points to false for not marking. Pass NULL to let the server
7593 * to use default value (false now).
7594 * @total_matching_boxes outputs reallocated number of all boxes matching the
7595 * query. Will be pointer to NULL if server did not provide the value.
7596 * Pass NULL if you don't care.
7597 * @current_page_beginning outputs reallocated ordinar number of the first box
7598 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7599 * server did not provide the value. Pass NULL if you don't care.
7600 * @current_page_size outputs reallocated count of boxes in the this @boxes
7601 * page. It will be pointer to NULL if the server did not provide the value.
7602 * Pass NULL if you don't care.
7603 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7604 * is the last one, false if more boxes match, NULL if the server did not
7605 * provude the value. Pass NULL if you don't care.
7606 * @boxes outputs reallocated list of isds_fulltext_result structures,
7607 * possibly empty.
7608 * @return:
7609 * IE_SUCCESS if search succeeded
7610 * IE_2BIG if @page_size is too large
7611 * other code if something bad happens; output arguments will be NULL. */
7612 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7613 const char *query,
7614 const isds_fulltext_target *target,
7615 const isds_DbType *box_type,
7616 const unsigned long int *page_size,
7617 const unsigned long int *page_number,
7618 const _Bool *track_matches,
7619 unsigned long int **total_matching_boxes,
7620 unsigned long int **current_page_beginning,
7621 unsigned long int **current_page_size,
7622 _Bool **last_page,
7623 struct isds_list **boxes) {
7624 isds_error err = IE_SUCCESS;
7625 #if HAVE_LIBCURL
7626 xmlNsPtr isds_ns = NULL;
7627 xmlNodePtr request = NULL;
7628 xmlDocPtr response = NULL;
7629 xmlNodePtr node;
7630 xmlXPathContextPtr xpath_ctx = NULL;
7631 xmlXPathObjectPtr result = NULL;
7632 const xmlChar *static_string = NULL;
7633 xmlChar *string = NULL;
7635 const xmlChar *codes[] = {
7636 BAD_CAST "1004",
7637 BAD_CAST "1152",
7638 BAD_CAST "1153",
7639 BAD_CAST "1154",
7640 BAD_CAST "1155",
7641 BAD_CAST "1156",
7642 BAD_CAST "9002",
7643 NULL
7645 const char *meanings[] = {
7646 N_("You are not allowed to perform the search"),
7647 N_("The query string is empty"),
7648 N_("Searched box ID is malformed"),
7649 N_("Searched organization ID is malformed"),
7650 N_("Invalid input"),
7651 N_("Requested page size is too large"),
7652 N_("Search engine internal error")
7654 const isds_error errors[] = {
7655 IE_ISDS,
7656 IE_INVAL,
7657 IE_INVAL,
7658 IE_INVAL,
7659 IE_INVAL,
7660 IE_2BIG,
7661 IE_ISDS
7663 struct code_map_isds_error map = {
7664 .codes = codes,
7665 .meanings = meanings,
7666 .errors = errors
7668 #endif
7671 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7672 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7673 if (NULL != current_page_size) zfree(*current_page_size);
7674 if (NULL != last_page) zfree(*last_page);
7675 isds_list_free(boxes);
7677 if (NULL == context) return IE_INVALID_CONTEXT;
7678 zfree(context->long_message);
7680 if (NULL == boxes) return IE_INVAL;
7682 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
7683 isds_log_message(context, _("Query string must be non-empty"));
7684 return IE_INVAL;
7687 #if HAVE_LIBCURL
7688 /* Check if connection is established
7689 * TODO: This check should be done downstairs. */
7690 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7692 /* Build FindDataBox request */
7693 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
7694 if (NULL == request) {
7695 isds_log_message(context,
7696 _("Could not build ISDSSearch2 request"));
7697 return IE_ERROR;
7699 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7700 if(NULL == isds_ns) {
7701 isds_log_message(context, _("Could not create ISDS name space"));
7702 xmlFreeNode(request);
7703 return IE_ERROR;
7705 xmlSetNs(request, isds_ns);
7707 INSERT_STRING(request, "searchText", query);
7709 if (NULL != target) {
7710 static_string = isds_fulltext_target2string(*(target));
7711 if (NULL == static_string) {
7712 isds_printf_message(context, _("Invalid target value: %d"),
7713 *(target));
7714 err = IE_ENUM;
7715 goto leave;
7718 INSERT_STRING(request, "searchType", static_string);
7719 static_string = NULL;
7721 if (NULL != box_type) {
7722 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7723 if (DBTYPE_SYSTEM == *box_type) {
7724 static_string = BAD_CAST "ALL";
7725 } else if (DBTYPE_OVM_MAIN == *box_type) {
7726 static_string = BAD_CAST "OVM_MAIN";
7727 } else {
7728 static_string = isds_DbType2string(*(box_type));
7729 if (NULL == static_string) {
7730 isds_printf_message(context, _("Invalid box type value: %d"),
7731 *(box_type));
7732 err = IE_ENUM;
7733 goto leave;
7737 INSERT_STRING(request, "searchScope", static_string);
7738 static_string = NULL;
7740 INSERT_ULONGINT(request, "page", page_number, string);
7741 INSERT_ULONGINT(request, "pageSize", page_size, string);
7742 INSERT_BOOLEAN(request, "highlighting", track_matches);
7744 /* Send request and check response */
7745 err = send_destroy_request_check_response(context,
7746 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
7747 &request, &response, NULL, &map);
7748 if (err) goto leave;
7750 /* Parse response */
7751 xpath_ctx = xmlXPathNewContext(response);
7752 if (NULL == xpath_ctx) {
7753 err = IE_ERROR;
7754 goto leave;
7756 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7757 err = IE_ERROR;
7758 goto leave;
7760 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
7761 xpath_ctx);
7762 if (!result) {
7763 err = IE_ERROR;
7764 goto leave;
7766 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7767 isds_log_message(context, _("Missing ISDSSearch2 element"));
7768 err = IE_ISDS;
7769 goto leave;
7771 if (result->nodesetval->nodeNr > 1) {
7772 isds_log_message(context, _("Multiple ISDSSearch2 element"));
7773 err = IE_ISDS;
7774 goto leave;
7776 xpath_ctx->node = result->nodesetval->nodeTab[0];
7777 xmlXPathFreeObject(result); result = NULL;
7780 /* Extract counters */
7781 if (NULL != total_matching_boxes) {
7782 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
7784 if (NULL != current_page_size) {
7785 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
7787 if (NULL != current_page_beginning) {
7788 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
7790 if (NULL != last_page) {
7791 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
7793 xmlXPathFreeObject(result); result = NULL;
7795 /* Extract boxes if they present */
7796 result = xmlXPathEvalExpression(BAD_CAST
7797 "isds:dbResults/isds:dbResult", xpath_ctx);
7798 if (NULL == result) {
7799 err = IE_ERROR;
7800 goto leave;
7802 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7803 struct isds_list *item, *prev_item = NULL;
7804 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7805 item = calloc(1, sizeof(*item));
7806 if (!item) {
7807 err = IE_NOMEM;
7808 goto leave;
7811 item->destructor = (void (*)(void **))isds_fulltext_result_free;
7812 if (i == 0) *boxes = item;
7813 else prev_item->next = item;
7814 prev_item = item;
7816 xpath_ctx->node = result->nodesetval->nodeTab[i];
7817 err = extract_dbResult(context,
7818 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
7819 (NULL == track_matches) ? 0 : *track_matches);
7820 if (err) goto leave;
7824 leave:
7825 if (err) {
7826 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7827 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7828 if (NULL != current_page_size) zfree(*current_page_size);
7829 if (NULL != last_page) zfree(*last_page);
7830 isds_list_free(boxes);
7833 free(string);
7834 xmlFreeNode(request);
7835 xmlXPathFreeObject(result);
7836 xmlXPathFreeContext(xpath_ctx);
7837 xmlFreeDoc(response);
7839 if (!err)
7840 isds_log(ILF_ISDS, ILL_DEBUG,
7841 _("ISDSSearch2 request processed by server successfully.\n"));
7842 #else /* not HAVE_LIBCURL */
7843 err = IE_NOTSUP;
7844 #endif
7846 return err;
7850 /* Get status of a box.
7851 * @context is ISDS session context.
7852 * @box_id is UTF-8 encoded box identifier as zero terminated string
7853 * @box_status is return value of box status.
7854 * @return:
7855 * IE_SUCCESS if box has been found and its status retrieved
7856 * IE_NOEXIST if box is not known to ISDS server
7857 * or other appropriate error.
7858 * You can use isds_DbState to enumerate box status. However out of enum
7859 * range value can be returned too. This is feature because ISDS
7860 * specification leaves the set of values open.
7861 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7862 * the box has been deleted, but ISDS still lists its former existence. */
7863 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7864 long int *box_status) {
7865 isds_error err = IE_SUCCESS;
7866 #if HAVE_LIBCURL
7867 xmlNsPtr isds_ns = NULL;
7868 xmlNodePtr request = NULL, db_id;
7869 xmlDocPtr response = NULL;
7870 xmlXPathContextPtr xpath_ctx = NULL;
7871 xmlXPathObjectPtr result = NULL;
7872 xmlChar *string = NULL;
7874 const xmlChar *codes[] = {
7875 BAD_CAST "5001",
7876 BAD_CAST "1007",
7877 BAD_CAST "2011",
7878 NULL
7880 const char *meanings[] = {
7881 "The box does not exist",
7882 "Box ID is malformed",
7883 "Box ID malformed",
7885 const isds_error errors[] = {
7886 IE_NOEXIST,
7887 IE_INVAL,
7888 IE_INVAL,
7890 struct code_map_isds_error map = {
7891 .codes = codes,
7892 .meanings = meanings,
7893 .errors = errors
7895 #endif
7897 if (!context) return IE_INVALID_CONTEXT;
7898 zfree(context->long_message);
7899 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7901 #if HAVE_LIBCURL
7902 /* Check if connection is established
7903 * TODO: This check should be done downstairs. */
7904 if (!context->curl) return IE_CONNECTION_CLOSED;
7907 /* Build CheckDataBox request */
7908 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7909 if (!request) {
7910 isds_log_message(context,
7911 _("Could build CheckDataBox request"));
7912 return IE_ERROR;
7914 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7915 if(!isds_ns) {
7916 isds_log_message(context, _("Could not create ISDS name space"));
7917 xmlFreeNode(request);
7918 return IE_ERROR;
7920 xmlSetNs(request, isds_ns);
7921 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7922 if (!db_id) {
7923 isds_log_message(context, _("Could not add dbID child to "
7924 "CheckDataBox element"));
7925 xmlFreeNode(request);
7926 return IE_ERROR;
7930 /* Send request and check response*/
7931 err = send_destroy_request_check_response(context,
7932 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7933 &request, &response, NULL, &map);
7934 if (err) goto leave;
7937 /* Extract data */
7938 xpath_ctx = xmlXPathNewContext(response);
7939 if (!xpath_ctx) {
7940 err = IE_ERROR;
7941 goto leave;
7943 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7944 err = IE_ERROR;
7945 goto leave;
7947 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7948 xpath_ctx);
7949 if (!result) {
7950 err = IE_ERROR;
7951 goto leave;
7953 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7954 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7955 err = IE_ISDS;
7956 goto leave;
7958 if (result->nodesetval->nodeNr > 1) {
7959 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7960 err = IE_ISDS;
7961 goto leave;
7963 xpath_ctx->node = result->nodesetval->nodeTab[0];
7964 xmlXPathFreeObject(result); result = NULL;
7966 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7969 leave:
7970 free(string);
7971 xmlXPathFreeObject(result);
7972 xmlXPathFreeContext(xpath_ctx);
7974 xmlFreeDoc(response);
7976 if (!err)
7977 isds_log(ILF_ISDS, ILL_DEBUG,
7978 _("CheckDataBox request processed by server successfully.\n"));
7979 #else /* not HAVE_LIBCURL */
7980 err = IE_NOTSUP;
7981 #endif
7983 return err;
7987 /* Get list of permissions to send commercial messages.
7988 * @context is ISDS session context.
7989 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7990 * @permissions is a reallocated list of permissions (struct
7991 * isds_commercial_permission*) to send commercial messages from @box_id. The
7992 * order of permissions is significant as the server applies the permissions
7993 * and associated pre-paid credits in the order. Empty list means no
7994 * permission.
7995 * @return:
7996 * IE_SUCCESS if the list has been obtained correctly,
7997 * or other appropriate error. */
7998 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7999 const char *box_id, struct isds_list **permissions) {
8000 isds_error err = IE_SUCCESS;
8001 #if HAVE_LIBCURL
8002 xmlDocPtr response = NULL;
8003 xmlXPathContextPtr xpath_ctx = NULL;
8004 xmlXPathObjectPtr result = NULL;
8005 #endif
8007 if (!context) return IE_INVALID_CONTEXT;
8008 zfree(context->long_message);
8009 if (NULL == permissions) return IE_INVAL;
8010 isds_list_free(permissions);
8011 if (NULL == box_id) return IE_INVAL;
8013 #if HAVE_LIBCURL
8014 /* Check if connection is established */
8015 if (!context->curl) return IE_CONNECTION_CLOSED;
8017 /* Do request and check for success */
8018 err = build_send_dbid_request_check_response(context,
8019 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
8020 BAD_CAST box_id, NULL, &response, NULL);
8021 if (!err) {
8022 isds_log(ILF_ISDS, ILL_DEBUG,
8023 _("PDZInfo request processed by server successfully.\n"));
8026 /* Extract data */
8027 /* Prepare structure */
8028 xpath_ctx = xmlXPathNewContext(response);
8029 if (!xpath_ctx) {
8030 err = IE_ERROR;
8031 goto leave;
8033 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8034 err = IE_ERROR;
8035 goto leave;
8038 /* Set context node */
8039 result = xmlXPathEvalExpression(BAD_CAST
8040 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8041 xpath_ctx);
8042 if (!result) {
8043 err = IE_ERROR;
8044 goto leave;
8046 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8047 struct isds_list *prev_item = NULL;
8049 /* Iterate over all permission records */
8050 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8051 struct isds_list *item;
8053 /* Prepare structure */
8054 item = calloc(1, sizeof(*item));
8055 if (!item) {
8056 err = IE_NOMEM;
8057 goto leave;
8059 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8060 if (i == 0) *permissions = item;
8061 else prev_item->next = item;
8062 prev_item = item;
8064 /* Extract it */
8065 xpath_ctx->node = result->nodesetval->nodeTab[i];
8066 err = extract_DbPDZRecord(context,
8067 (struct isds_commercial_permission **) (&item->data),
8068 xpath_ctx);
8069 if (err) goto leave;
8073 leave:
8074 if (err) {
8075 isds_list_free(permissions);
8078 xmlXPathFreeObject(result);
8079 xmlXPathFreeContext(xpath_ctx);
8080 xmlFreeDoc(response);
8082 #else /* not HAVE_LIBCURL */
8083 err = IE_NOTSUP;
8084 #endif
8086 return err;
8090 /* Get details about credit for sending pre-paid commercial messages.
8091 * @context is ISDS session context.
8092 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8093 * @from_date is first day of credit history to return in @history. Only
8094 * tm_year, tm_mon and tm_mday carry sane value.
8095 * @to_date is last day of credit history to return in @history. Only
8096 * tm_year, tm_mon and tm_mday carry sane value.
8097 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8098 * if you don't care. This and all other credit values are integers in
8099 * hundredths of Czech Crowns.
8100 * @email outputs notification e-mail address where notifications about credit
8101 * are sent. This is automatically reallocated string. Pass NULL if you don't
8102 * care. It can return NULL if no address is defined.
8103 * @history outputs auto-reallocated list of pointers to struct
8104 * isds_credit_event. Events in closed interval @from_time to @to_time are
8105 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8106 * are sorted by time.
8107 * @return:
8108 * IE_SUCCESS if the credit details have been obtained correctly,
8109 * or other appropriate error. Please note that server allows to retrieve
8110 * only limited history of events. */
8111 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8112 const char *box_id,
8113 const struct tm *from_date, const struct tm *to_date,
8114 long int *credit, char **email, struct isds_list **history) {
8115 isds_error err = IE_SUCCESS;
8116 #if HAVE_LIBCURL
8117 char *box_id_locale = NULL;
8118 xmlNodePtr request = NULL, node;
8119 xmlNsPtr isds_ns = NULL;
8120 xmlChar *string = NULL;
8122 xmlDocPtr response = NULL;
8123 xmlXPathContextPtr xpath_ctx = NULL;
8124 xmlXPathObjectPtr result = NULL;
8126 const xmlChar *codes[] = {
8127 BAD_CAST "1004",
8128 BAD_CAST "2011",
8129 BAD_CAST "1093",
8130 BAD_CAST "1137",
8131 BAD_CAST "1058",
8132 NULL
8134 const char *meanings[] = {
8135 "Insufficient priviledges for the box",
8136 "The box does not exist",
8137 "Date is too long (history is not available after 15 months)",
8138 "Interval is too long (limit is 3 months)",
8139 "Invalid date"
8141 const isds_error errors[] = {
8142 IE_ISDS,
8143 IE_NOEXIST,
8144 IE_DATE,
8145 IE_DATE,
8146 IE_DATE,
8148 struct code_map_isds_error map = {
8149 .codes = codes,
8150 .meanings = meanings,
8151 .errors = errors
8153 #endif
8155 if (!context) return IE_INVALID_CONTEXT;
8156 zfree(context->long_message);
8158 /* Free output argument */
8159 if (NULL != credit) *credit = 0;
8160 if (NULL != email) zfree(*email);
8161 isds_list_free(history);
8163 if (NULL == box_id) return IE_INVAL;
8165 #if HAVE_LIBCURL
8166 /* Check if connection is established */
8167 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8169 box_id_locale = _isds_utf82locale((char*)box_id);
8170 if (NULL == box_id_locale) {
8171 err = IE_NOMEM;
8172 goto leave;
8175 /* Build request */
8176 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8177 if (NULL == request) {
8178 isds_printf_message(context,
8179 _("Could not build DataBoxCreditInfo request for %s box"),
8180 box_id_locale);
8181 err = IE_ERROR;
8182 goto leave;
8184 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8185 if(!isds_ns) {
8186 isds_log_message(context, _("Could not create ISDS name space"));
8187 err = IE_ERROR;
8188 goto leave;
8190 xmlSetNs(request, isds_ns);
8192 /* Add mandatory XSD:tIdDbInput child */
8193 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8194 /* Add mandatory dates elements with optional values */
8195 if (from_date) {
8196 err = tm2datestring(from_date, &string);
8197 if (err) {
8198 isds_log_message(context,
8199 _("Could not convert `from_date' argument to ISO date "
8200 "string"));
8201 goto leave;
8203 INSERT_STRING(request, "ciFromDate", string);
8204 zfree(string);
8205 } else {
8206 INSERT_STRING(request, "ciFromDate", NULL);
8208 if (to_date) {
8209 err = tm2datestring(to_date, &string);
8210 if (err) {
8211 isds_log_message(context,
8212 _("Could not convert `to_date' argument to ISO date "
8213 "string"));
8214 goto leave;
8216 INSERT_STRING(request, "ciTodate", string);
8217 zfree(string);
8218 } else {
8219 INSERT_STRING(request, "ciTodate", NULL);
8222 /* Send request and check response*/
8223 err = send_destroy_request_check_response(context,
8224 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8225 &request, &response, NULL, &map);
8226 if (err) goto leave;
8229 /* Extract data */
8230 /* Set context to the root */
8231 xpath_ctx = xmlXPathNewContext(response);
8232 if (!xpath_ctx) {
8233 err = IE_ERROR;
8234 goto leave;
8236 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8237 err = IE_ERROR;
8238 goto leave;
8240 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8241 xpath_ctx);
8242 if (!result) {
8243 err = IE_ERROR;
8244 goto leave;
8246 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8247 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8248 err = IE_ISDS;
8249 goto leave;
8251 if (result->nodesetval->nodeNr > 1) {
8252 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8253 err = IE_ISDS;
8254 goto leave;
8256 xpath_ctx->node = result->nodesetval->nodeTab[0];
8257 xmlXPathFreeObject(result); result = NULL;
8259 /* Extract common data */
8260 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8261 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8263 /* Extract records */
8264 if (NULL == history) goto leave;
8265 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8266 xpath_ctx);
8267 if (!result) {
8268 err = IE_ERROR;
8269 goto leave;
8271 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8272 struct isds_list *prev_item = NULL;
8274 /* Iterate over all records */
8275 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8276 struct isds_list *item;
8278 /* Prepare structure */
8279 item = calloc(1, sizeof(*item));
8280 if (!item) {
8281 err = IE_NOMEM;
8282 goto leave;
8284 item->destructor = (void(*)(void**))isds_credit_event_free;
8285 if (i == 0) *history = item;
8286 else prev_item->next = item;
8287 prev_item = item;
8289 /* Extract it */
8290 xpath_ctx->node = result->nodesetval->nodeTab[i];
8291 err = extract_CiRecord(context,
8292 (struct isds_credit_event **) (&item->data),
8293 xpath_ctx);
8294 if (err) goto leave;
8298 leave:
8299 if (!err) {
8300 isds_log(ILF_ISDS, ILL_DEBUG,
8301 _("DataBoxCreditInfo request processed by server successfully.\n"));
8303 if (err) {
8304 isds_list_free(history);
8305 if (NULL != email) zfree(*email)
8308 free(box_id_locale);
8309 xmlXPathFreeObject(result);
8310 xmlXPathFreeContext(xpath_ctx);
8311 xmlFreeDoc(response);
8313 #else /* not HAVE_LIBCURL */
8314 err = IE_NOTSUP;
8315 #endif
8317 return err;
8321 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8322 * code, destroy response and log success.
8323 * @context is ISDS session context.
8324 * @service_name is name of SERVICE_DB_MANIPULATION service
8325 * @box_id is UTF-8 encoded box identifier as zero terminated string
8326 * @approval is optional external approval of box manipulation
8327 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8328 * NULL, if you don't care. */
8329 static isds_error build_send_manipulationdbid_request_check_drop_response(
8330 struct isds_ctx *context, const xmlChar *service_name,
8331 const xmlChar *box_id, const struct isds_approval *approval,
8332 xmlChar **refnumber) {
8333 isds_error err = IE_SUCCESS;
8334 #if HAVE_LIBCURL
8335 xmlDocPtr response = NULL;
8336 #endif
8338 if (!context) return IE_INVALID_CONTEXT;
8339 zfree(context->long_message);
8340 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8342 #if HAVE_LIBCURL
8343 /* Check if connection is established */
8344 if (!context->curl) return IE_CONNECTION_CLOSED;
8346 /* Do request and check for success */
8347 err = build_send_dbid_request_check_response(context,
8348 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8349 &response, refnumber);
8350 xmlFreeDoc(response);
8352 if (!err) {
8353 char *service_name_locale = _isds_utf82locale((char *) service_name);
8354 isds_log(ILF_ISDS, ILL_DEBUG,
8355 _("%s request processed by server successfully.\n"),
8356 service_name_locale);
8357 free(service_name_locale);
8359 #else /* not HAVE_LIBCURL */
8360 err = IE_NOTSUP;
8361 #endif
8363 return err;
8367 /* Switch box into state where box can receive commercial messages (off by
8368 * default)
8369 * @context is ISDS session context.
8370 * @box_id is UTF-8 encoded box identifier as zero terminated string
8371 * @allow is true for enable, false for disable commercial messages income
8372 * @approval is optional external approval of box manipulation
8373 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8374 * NULL, if you don't care. */
8375 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8376 const char *box_id, const _Bool allow,
8377 const struct isds_approval *approval, char **refnumber) {
8378 return build_send_manipulationdbid_request_check_drop_response(context,
8379 (allow) ? BAD_CAST "SetOpenAddressing" :
8380 BAD_CAST "ClearOpenAddressing",
8381 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8385 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8386 * message acceptance). This is just a box permission. Sender must apply
8387 * such role by sending each message.
8388 * @context is ISDS session context.
8389 * @box_id is UTF-8 encoded box identifier as zero terminated string
8390 * @allow is true for enable, false for disable OVM role permission
8391 * @approval is optional external approval of box manipulation
8392 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8393 * NULL, if you don't care. */
8394 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
8395 const char *box_id, const _Bool allow,
8396 const struct isds_approval *approval, char **refnumber) {
8397 return build_send_manipulationdbid_request_check_drop_response(context,
8398 (allow) ? BAD_CAST "SetEffectiveOVM" :
8399 BAD_CAST "ClearEffectiveOVM",
8400 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8404 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8405 * code, destroy response and log success.
8406 * @context is ISDS session context.
8407 * @service_name is name of SERVICE_DB_MANIPULATION service
8408 * @owner is structure describing box
8409 * @approval is optional external approval of box manipulation
8410 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8411 * NULL, if you don't care. */
8412 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8413 struct isds_ctx *context, const xmlChar *service_name,
8414 const struct isds_DbOwnerInfo *owner,
8415 const struct isds_approval *approval, xmlChar **refnumber) {
8416 isds_error err = IE_SUCCESS;
8417 #if HAVE_LIBCURL
8418 char *service_name_locale = NULL;
8419 xmlNodePtr request = NULL, db_owner_info;
8420 xmlNsPtr isds_ns = NULL;
8421 #endif
8424 if (!context) return IE_INVALID_CONTEXT;
8425 zfree(context->long_message);
8426 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8428 #if HAVE_LIBCURL
8429 service_name_locale = _isds_utf82locale((char*)service_name);
8430 if (!service_name_locale) {
8431 err = IE_NOMEM;
8432 goto leave;
8435 /* Build request */
8436 request = xmlNewNode(NULL, service_name);
8437 if (!request) {
8438 isds_printf_message(context,
8439 _("Could not build %s request"), service_name_locale);
8440 err = IE_ERROR;
8441 goto leave;
8443 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8444 if(!isds_ns) {
8445 isds_log_message(context, _("Could not create ISDS name space"));
8446 err = IE_ERROR;
8447 goto leave;
8449 xmlSetNs(request, isds_ns);
8452 /* Add XSD:tOwnerInfoInput child*/
8453 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8454 err = insert_DbOwnerInfo(context, owner, db_owner_info);
8455 if (err) goto leave;
8457 /* Add XSD:gExtApproval*/
8458 err = insert_GExtApproval(context, approval, request);
8459 if (err) goto leave;
8461 /* Send it to server and process response */
8462 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8463 service_name, &request, refnumber);
8465 leave:
8466 xmlFreeNode(request);
8467 free(service_name_locale);
8468 #else /* not HAVE_LIBCURL */
8469 err = IE_NOTSUP;
8470 #endif
8472 return err;
8476 /* Switch box accessibility state on request of box owner.
8477 * Despite the name, owner must do the request off-line. This function is
8478 * designed for such off-line meeting points (e.g. Czech POINT).
8479 * @context is ISDS session context.
8480 * @box identifies box to switch accessibility state.
8481 * @allow is true for making accessible, false to disallow access.
8482 * @approval is optional external approval of box manipulation
8483 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8484 * NULL, if you don't care. */
8485 isds_error isds_switch_box_accessibility_on_owner_request(
8486 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8487 const _Bool allow, const struct isds_approval *approval,
8488 char **refnumber) {
8489 return build_send_manipulationdbowner_request_check_drop_response(context,
8490 (allow) ? BAD_CAST "EnableOwnDataBox" :
8491 BAD_CAST "DisableOwnDataBox",
8492 box, approval, (xmlChar **) refnumber);
8496 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8497 * date.
8498 * @context is ISDS session context.
8499 * @box identifies box to switch accessibility state.
8500 * @since is date since accessibility has been denied. This can be past too.
8501 * Only tm_year, tm_mon and tm_mday carry sane value.
8502 * @approval is optional external approval of box manipulation
8503 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8504 * NULL, if you don't care. */
8505 isds_error isds_disable_box_accessibility_externaly(
8506 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8507 const struct tm *since, const struct isds_approval *approval,
8508 char **refnumber) {
8509 isds_error err = IE_SUCCESS;
8510 #if HAVE_LIBCURL
8511 char *service_name_locale = NULL;
8512 xmlNodePtr request = NULL, node;
8513 xmlNsPtr isds_ns = NULL;
8514 xmlChar *string = NULL;
8515 #endif
8518 if (!context) return IE_INVALID_CONTEXT;
8519 zfree(context->long_message);
8520 if (!box || !since) return IE_INVAL;
8522 #if HAVE_LIBCURL
8523 /* Build request */
8524 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
8525 if (!request) {
8526 isds_printf_message(context,
8527 _("Could not build %s request"), "DisableDataBoxExternally");
8528 err = IE_ERROR;
8529 goto leave;
8531 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8532 if(!isds_ns) {
8533 isds_log_message(context, _("Could not create ISDS name space"));
8534 err = IE_ERROR;
8535 goto leave;
8537 xmlSetNs(request, isds_ns);
8540 /* Add @box identification */
8541 INSERT_ELEMENT(node, request, "dbOwnerInfo");
8542 err = insert_DbOwnerInfo(context, box, node);
8543 if (err) goto leave;
8545 /* Add @since date */
8546 err = tm2datestring(since, &string);
8547 if(err) {
8548 isds_log_message(context,
8549 _("Could not convert `since' argument to ISO date string"));
8550 goto leave;
8552 INSERT_STRING(request, "dbOwnerDisableDate", string);
8553 zfree(string);
8555 /* Add @approval */
8556 err = insert_GExtApproval(context, approval, request);
8557 if (err) goto leave;
8559 /* Send it to server and process response */
8560 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8561 BAD_CAST "DisableDataBoxExternally", &request,
8562 (xmlChar **) refnumber);
8564 leave:
8565 free(string);
8566 xmlFreeNode(request);
8567 free(service_name_locale);
8568 #else /* not HAVE_LIBCURL */
8569 err = IE_NOTSUP;
8570 #endif
8572 return err;
8576 #if HAVE_LIBCURL
8577 /* Insert struct isds_message data (envelope (recipient data optional) and
8578 * documents into XML tree
8579 * @context is session context
8580 * @outgoing_message is libisds structure with message data
8581 * @create_message is XML CreateMessage or CreateMultipleMessage element
8582 * @process_recipient true for recipient data serialization, false for no
8583 * serialization */
8584 static isds_error insert_envelope_files(struct isds_ctx *context,
8585 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8586 const _Bool process_recipient) {
8588 isds_error err = IE_SUCCESS;
8589 xmlNodePtr envelope, dm_files, node;
8590 xmlChar *string = NULL;
8592 if (!context) return IE_INVALID_CONTEXT;
8593 if (!outgoing_message || !create_message) return IE_INVAL;
8596 /* Build envelope */
8597 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8598 if (!envelope) {
8599 isds_printf_message(context, _("Could not add dmEnvelope child to "
8600 "%s element"), create_message->name);
8601 return IE_ERROR;
8604 if (!outgoing_message->envelope) {
8605 isds_log_message(context, _("Outgoing message is missing envelope"));
8606 err = IE_INVAL;
8607 goto leave;
8610 /* Insert optional message type */
8611 err = insert_message_type(context, outgoing_message->envelope->dmType,
8612 envelope);
8613 if (err) goto leave;
8615 INSERT_STRING(envelope, "dmSenderOrgUnit",
8616 outgoing_message->envelope->dmSenderOrgUnit);
8617 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8618 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8620 if (process_recipient) {
8621 if (!outgoing_message->envelope->dbIDRecipient) {
8622 isds_log_message(context,
8623 _("Outgoing message is missing recipient box identifier"));
8624 err = IE_INVAL;
8625 goto leave;
8627 INSERT_STRING(envelope, "dbIDRecipient",
8628 outgoing_message->envelope->dbIDRecipient);
8630 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8631 outgoing_message->envelope->dmRecipientOrgUnit);
8632 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8633 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8634 INSERT_STRING(envelope, "dmToHands",
8635 outgoing_message->envelope->dmToHands);
8638 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8639 "dmAnnotation");
8640 INSERT_STRING(envelope, "dmAnnotation",
8641 outgoing_message->envelope->dmAnnotation);
8643 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8644 0, 50, "dmRecipientRefNumber");
8645 INSERT_STRING(envelope, "dmRecipientRefNumber",
8646 outgoing_message->envelope->dmRecipientRefNumber);
8648 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8649 0, 50, "dmSenderRefNumber");
8650 INSERT_STRING(envelope, "dmSenderRefNumber",
8651 outgoing_message->envelope->dmSenderRefNumber);
8653 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8654 0, 50, "dmRecipientIdent");
8655 INSERT_STRING(envelope, "dmRecipientIdent",
8656 outgoing_message->envelope->dmRecipientIdent);
8658 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8659 0, 50, "dmSenderIdent");
8660 INSERT_STRING(envelope, "dmSenderIdent",
8661 outgoing_message->envelope->dmSenderIdent);
8663 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8664 outgoing_message->envelope->dmLegalTitleLaw, string);
8665 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8666 outgoing_message->envelope->dmLegalTitleYear, string);
8667 INSERT_STRING(envelope, "dmLegalTitleSect",
8668 outgoing_message->envelope->dmLegalTitleSect);
8669 INSERT_STRING(envelope, "dmLegalTitlePar",
8670 outgoing_message->envelope->dmLegalTitlePar);
8671 INSERT_STRING(envelope, "dmLegalTitlePoint",
8672 outgoing_message->envelope->dmLegalTitlePoint);
8674 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8675 outgoing_message->envelope->dmPersonalDelivery);
8676 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8677 outgoing_message->envelope->dmAllowSubstDelivery);
8679 /* ???: Should we require value for dbEffectiveOVM sender?
8680 * ISDS has default as true */
8681 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8682 INSERT_BOOLEAN(envelope, "dmOVM",
8683 outgoing_message->envelope->dmPublishOwnID);
8686 /* Append dmFiles */
8687 if (!outgoing_message->documents) {
8688 isds_log_message(context,
8689 _("Outgoing message is missing list of documents"));
8690 err = IE_INVAL;
8691 goto leave;
8693 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8694 if (!dm_files) {
8695 isds_printf_message(context, _("Could not add dmFiles child to "
8696 "%s element"), create_message->name);
8697 err = IE_ERROR;
8698 goto leave;
8701 /* Check for document hierarchy */
8702 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8703 if (err) goto leave;
8705 /* Process each document */
8706 for (struct isds_list *item =
8707 (struct isds_list *) outgoing_message->documents;
8708 item; item = item->next) {
8709 if (!item->data) {
8710 isds_log_message(context,
8711 _("List of documents contains empty item"));
8712 err = IE_INVAL;
8713 goto leave;
8715 /* FIXME: Check for dmFileMetaType and for document references.
8716 * Only first document can be of MAIN type */
8717 err = insert_document(context, (struct isds_document*) item->data,
8718 dm_files);
8720 if (err) goto leave;
8723 leave:
8724 free(string);
8725 return err;
8727 #endif /* HAVE_LIBCURL */
8730 /* Send a message via ISDS to a recipient
8731 * @context is session context
8732 * @outgoing_message is message to send; Some members are mandatory (like
8733 * dbIDRecipient), some are optional and some are irrelevant (especially data
8734 * about sender). Included pointer to isds_list documents must contain at
8735 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8736 * members will be filled with valid data from ISDS. Exact list of write
8737 * members is subject to change. Currently dmID is changed.
8738 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8739 isds_error isds_send_message(struct isds_ctx *context,
8740 struct isds_message *outgoing_message) {
8742 isds_error err = IE_SUCCESS;
8743 #if HAVE_LIBCURL
8744 xmlNsPtr isds_ns = NULL;
8745 xmlNodePtr request = NULL;
8746 xmlDocPtr response = NULL;
8747 xmlChar *code = NULL, *message = NULL;
8748 xmlXPathContextPtr xpath_ctx = NULL;
8749 xmlXPathObjectPtr result = NULL;
8750 /*_Bool message_is_complete = 0;*/
8751 #endif
8753 if (!context) return IE_INVALID_CONTEXT;
8754 zfree(context->long_message);
8755 if (!outgoing_message) return IE_INVAL;
8757 #if HAVE_LIBCURL
8758 /* Check if connection is established
8759 * TODO: This check should be done downstairs. */
8760 if (!context->curl) return IE_CONNECTION_CLOSED;
8763 /* Build CreateMessage request */
8764 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8765 if (!request) {
8766 isds_log_message(context,
8767 _("Could not build CreateMessage request"));
8768 return IE_ERROR;
8770 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8771 if(!isds_ns) {
8772 isds_log_message(context, _("Could not create ISDS name space"));
8773 xmlFreeNode(request);
8774 return IE_ERROR;
8776 xmlSetNs(request, isds_ns);
8778 /* Append envelope and files */
8779 err = insert_envelope_files(context, outgoing_message, request, 1);
8780 if (err) goto leave;
8783 /* Signal we can serialize message since now */
8784 /*message_is_complete = 1;*/
8787 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8789 /* Sent request */
8790 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8792 /* Don't' destroy request, we want to provide it to application later */
8794 if (err) {
8795 isds_log(ILF_ISDS, ILL_DEBUG,
8796 _("Processing ISDS response on CreateMessage "
8797 "request failed\n"));
8798 goto leave;
8801 /* Check for response status */
8802 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8803 &code, &message, NULL);
8804 if (err) {
8805 isds_log(ILF_ISDS, ILL_DEBUG,
8806 _("ISDS response on CreateMessage request "
8807 "is missing status\n"));
8808 goto leave;
8811 /* Request processed, but refused by server or server failed */
8812 if (xmlStrcmp(code, BAD_CAST "0000")) {
8813 char *box_id_locale =
8814 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8815 char *code_locale = _isds_utf82locale((char*)code);
8816 char *message_locale = _isds_utf82locale((char*)message);
8817 isds_log(ILF_ISDS, ILL_DEBUG,
8818 _("Server did not accept message for %s on CreateMessage "
8819 "request (code=%s, message=%s)\n"),
8820 box_id_locale, code_locale, message_locale);
8821 isds_log_message(context, message_locale);
8822 free(box_id_locale);
8823 free(code_locale);
8824 free(message_locale);
8825 err = IE_ISDS;
8826 goto leave;
8830 /* Extract data */
8831 xpath_ctx = xmlXPathNewContext(response);
8832 if (!xpath_ctx) {
8833 err = IE_ERROR;
8834 goto leave;
8836 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8837 err = IE_ERROR;
8838 goto leave;
8840 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8841 xpath_ctx);
8842 if (!result) {
8843 err = IE_ERROR;
8844 goto leave;
8846 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8847 isds_log_message(context, _("Missing CreateMessageResponse element"));
8848 err = IE_ISDS;
8849 goto leave;
8851 if (result->nodesetval->nodeNr > 1) {
8852 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8853 err = IE_ISDS;
8854 goto leave;
8856 xpath_ctx->node = result->nodesetval->nodeTab[0];
8857 xmlXPathFreeObject(result); result = NULL;
8859 if (outgoing_message->envelope->dmID) {
8860 free(outgoing_message->envelope->dmID);
8861 outgoing_message->envelope->dmID = NULL;
8863 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8864 if (!outgoing_message->envelope->dmID) {
8865 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8866 "but did not return assigned message ID\n"));
8869 leave:
8870 /* TODO: Serialize message into structure member raw */
8871 /* XXX: Each web service transport message in different format.
8872 * Therefore it's not possible to save them directly.
8873 * To save them, one must figure out common format.
8874 * We can leave it on application, or we can implement the ESS format. */
8875 /*if (message_is_complete) {
8876 if (outgoing_message->envelope->dmID) {
8878 /* Add assigned message ID as first child*/
8879 /*xmlNodePtr dmid_text = xmlNewText(
8880 (xmlChar *) outgoing_message->envelope->dmID);
8881 if (!dmid_text) goto serialization_failed;
8883 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8884 BAD_CAST "dmID");
8885 if (!dmid_element) {
8886 xmlFreeNode(dmid_text);
8887 goto serialization_failed;
8890 xmlNodePtr dmid_element_with_text =
8891 xmlAddChild(dmid_element, dmid_text);
8892 if (!dmid_element_with_text) {
8893 xmlFreeNode(dmid_element);
8894 xmlFreeNode(dmid_text);
8895 goto serialization_failed;
8898 node = xmlAddPrevSibling(envelope->childern,
8899 dmid_element_with_text);
8900 if (!node) {
8901 xmlFreeNodeList(dmid_element_with_text);
8902 goto serialization_failed;
8906 /* Serialize message with ID into raw */
8907 /*buffer = serialize_element(envelope)*/
8908 /* }
8910 serialization_failed:
8914 /* Clean up */
8915 xmlXPathFreeObject(result);
8916 xmlXPathFreeContext(xpath_ctx);
8918 free(code);
8919 free(message);
8920 xmlFreeDoc(response);
8921 xmlFreeNode(request);
8923 if (!err)
8924 isds_log(ILF_ISDS, ILL_DEBUG,
8925 _("CreateMessage request processed by server "
8926 "successfully.\n"));
8927 #else /* not HAVE_LIBCURL */
8928 err = IE_NOTSUP;
8929 #endif
8931 return err;
8935 /* Send a message via ISDS to a multiple recipients
8936 * @context is session context
8937 * @outgoing_message is message to send; Some members are mandatory,
8938 * some are optional and some are irrelevant (especially data
8939 * about sender). Data about recipient will be substituted by ISDS from
8940 * @copies. Included pointer to isds_list documents must
8941 * contain at least one document of FILEMETATYPE_MAIN.
8942 * @copies is list of isds_message_copy structures addressing all desired
8943 * recipients. This is read-write structure, some members will be filled with
8944 * valid data from ISDS (message IDs, error codes, error descriptions).
8945 * @return
8946 * ISDS_SUCCESS if all messages have been sent
8947 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8948 * succeeded messages can be identified by copies->data->error),
8949 * or other error code if something other goes wrong. */
8950 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8951 const struct isds_message *outgoing_message,
8952 struct isds_list *copies) {
8954 isds_error err = IE_SUCCESS;
8955 #if HAVE_LIBCURL
8956 isds_error append_err;
8957 xmlNsPtr isds_ns = NULL;
8958 xmlNodePtr request = NULL, recipients, recipient, node;
8959 struct isds_list *item;
8960 struct isds_message_copy *copy;
8961 xmlDocPtr response = NULL;
8962 xmlChar *code = NULL, *message = NULL;
8963 xmlXPathContextPtr xpath_ctx = NULL;
8964 xmlXPathObjectPtr result = NULL;
8965 xmlChar *string = NULL;
8966 int i;
8967 #endif
8969 if (!context) return IE_INVALID_CONTEXT;
8970 zfree(context->long_message);
8971 if (!outgoing_message || !copies) return IE_INVAL;
8973 #if HAVE_LIBCURL
8974 /* Check if connection is established
8975 * TODO: This check should be done downstairs. */
8976 if (!context->curl) return IE_CONNECTION_CLOSED;
8979 /* Build CreateMultipleMessage request */
8980 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
8981 if (!request) {
8982 isds_log_message(context,
8983 _("Could not build CreateMultipleMessage request"));
8984 return IE_ERROR;
8986 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8987 if(!isds_ns) {
8988 isds_log_message(context, _("Could not create ISDS name space"));
8989 xmlFreeNode(request);
8990 return IE_ERROR;
8992 xmlSetNs(request, isds_ns);
8995 /* Build recipients */
8996 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8997 if (!recipients) {
8998 isds_log_message(context, _("Could not add dmRecipients child to "
8999 "CreateMultipleMessage element"));
9000 xmlFreeNode(request);
9001 return IE_ERROR;
9004 /* Insert each recipient */
9005 for (item = copies; item; item = item->next) {
9006 copy = (struct isds_message_copy *) item->data;
9007 if (!copy) {
9008 isds_log_message(context,
9009 _("`copies' list item contains empty data"));
9010 err = IE_INVAL;
9011 goto leave;
9014 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
9015 if (!recipient) {
9016 isds_log_message(context, _("Could not add dmRecipient child to "
9017 "dmRecipients element"));
9018 err = IE_ERROR;
9019 goto leave;
9022 if (!copy->dbIDRecipient) {
9023 isds_log_message(context,
9024 _("Message copy is missing recipient box identifier"));
9025 err = IE_INVAL;
9026 goto leave;
9028 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
9029 INSERT_STRING(recipient, "dmRecipientOrgUnit",
9030 copy->dmRecipientOrgUnit);
9031 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
9032 copy->dmRecipientOrgUnitNum, string);
9033 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
9036 /* Append envelope and files */
9037 err = insert_envelope_files(context, outgoing_message, request, 0);
9038 if (err) goto leave;
9041 isds_log(ILF_ISDS, ILL_DEBUG,
9042 _("Sending CreateMultipleMessage request to ISDS\n"));
9044 /* Sent request */
9045 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9046 if (err) {
9047 isds_log(ILF_ISDS, ILL_DEBUG,
9048 _("Processing ISDS response on CreateMultipleMessage "
9049 "request failed\n"));
9050 goto leave;
9053 /* Check for response status */
9054 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9055 &code, &message, NULL);
9056 if (err) {
9057 isds_log(ILF_ISDS, ILL_DEBUG,
9058 _("ISDS response on CreateMultipleMessage request "
9059 "is missing status\n"));
9060 goto leave;
9063 /* Request processed, but some copies failed */
9064 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9065 char *box_id_locale =
9066 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9067 char *code_locale = _isds_utf82locale((char*)code);
9068 char *message_locale = _isds_utf82locale((char*)message);
9069 isds_log(ILF_ISDS, ILL_DEBUG,
9070 _("Server did accept message for multiple recipients "
9071 "on CreateMultipleMessage request but delivery to "
9072 "some of them failed (code=%s, message=%s)\n"),
9073 box_id_locale, code_locale, message_locale);
9074 isds_log_message(context, message_locale);
9075 free(box_id_locale);
9076 free(code_locale);
9077 free(message_locale);
9078 err = IE_PARTIAL_SUCCESS;
9081 /* Request refused by server as whole */
9082 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9083 char *box_id_locale =
9084 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9085 char *code_locale = _isds_utf82locale((char*)code);
9086 char *message_locale = _isds_utf82locale((char*)message);
9087 isds_log(ILF_ISDS, ILL_DEBUG,
9088 _("Server did not accept message for multiple recipients "
9089 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9090 box_id_locale, code_locale, message_locale);
9091 isds_log_message(context, message_locale);
9092 free(box_id_locale);
9093 free(code_locale);
9094 free(message_locale);
9095 err = IE_ISDS;
9096 goto leave;
9100 /* Extract data */
9101 xpath_ctx = xmlXPathNewContext(response);
9102 if (!xpath_ctx) {
9103 err = IE_ERROR;
9104 goto leave;
9106 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9107 err = IE_ERROR;
9108 goto leave;
9110 result = xmlXPathEvalExpression(
9111 BAD_CAST "/isds:CreateMultipleMessageResponse"
9112 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9113 xpath_ctx);
9114 if (!result) {
9115 err = IE_ERROR;
9116 goto leave;
9118 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9119 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9120 err = IE_ISDS;
9121 goto leave;
9124 /* Extract message ID and delivery status for each copy */
9125 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9126 item = item->next, i++) {
9127 copy = (struct isds_message_copy *) item->data;
9128 xpath_ctx->node = result->nodesetval->nodeTab[i];
9130 append_err = append_TMStatus(context, copy, xpath_ctx);
9131 if (append_err) {
9132 err = append_err;
9133 goto leave;
9136 if (item || i < result->nodesetval->nodeNr) {
9137 isds_printf_message(context, _("ISDS returned unexpected number of "
9138 "message copy delivery states: %d"),
9139 result->nodesetval->nodeNr);
9140 err = IE_ISDS;
9141 goto leave;
9145 leave:
9146 /* Clean up */
9147 free(string);
9148 xmlXPathFreeObject(result);
9149 xmlXPathFreeContext(xpath_ctx);
9151 free(code);
9152 free(message);
9153 xmlFreeDoc(response);
9154 xmlFreeNode(request);
9156 if (!err)
9157 isds_log(ILF_ISDS, ILL_DEBUG,
9158 _("CreateMultipleMessageResponse request processed by server "
9159 "successfully.\n"));
9160 #else /* not HAVE_LIBCURL */
9161 err = IE_NOTSUP;
9162 #endif
9164 return err;
9168 /* Get list of messages. This is common core for getting sent or received
9169 * messages.
9170 * Any criterion argument can be NULL, if you don't care about it.
9171 * @context is session context. Must not be NULL.
9172 * @outgoing_direction is true if you want list of outgoing messages,
9173 * it's false if you want incoming messages.
9174 * @from_time is minimal time and date of message sending inclusive.
9175 * @to_time is maximal time and date of message sending inclusive
9176 * @organization_unit_number is number of sender/recipient respectively.
9177 * @status_filter is bit field of isds_message_status values. Use special
9178 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9179 * all values, you can use bit-wise arithmetic if you want.)
9180 * @offset is index of first message we are interested in. First message is 1.
9181 * Set to 0 (or 1) if you don't care.
9182 * @number is maximal length of list you want to get as input value, outputs
9183 * number of messages matching these criteria. Can be NULL if you don't care
9184 * (applies to output value either).
9185 * @messages is automatically reallocated list of isds_message's. Be ware that
9186 * it returns only brief overview (envelope and some other fields) about each
9187 * message, not the complete message. FIXME: Specify exact fields.
9188 * The list is sorted by delivery time in ascending order.
9189 * Use NULL if you don't care about don't need the data (useful if you want to
9190 * know only the @number). If you provide &NULL, list will be allocated on
9191 * heap, if you provide pointer to non-NULL, list will be freed automatically
9192 * at first. Also in case of error the list will be NULLed.
9193 * @return IE_SUCCESS or appropriate error code. */
9194 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9195 _Bool outgoing_direction,
9196 const struct timeval *from_time, const struct timeval *to_time,
9197 const long int *organization_unit_number,
9198 const unsigned int status_filter,
9199 const unsigned long int offset, unsigned long int *number,
9200 struct isds_list **messages) {
9202 isds_error err = IE_SUCCESS;
9203 #if HAVE_LIBCURL
9204 xmlNsPtr isds_ns = NULL;
9205 xmlNodePtr request = NULL, node;
9206 xmlDocPtr response = NULL;
9207 xmlChar *code = NULL, *message = NULL;
9208 xmlXPathContextPtr xpath_ctx = NULL;
9209 xmlXPathObjectPtr result = NULL;
9210 xmlChar *string = NULL;
9211 int count = 0;
9212 #endif
9214 if (!context) return IE_INVALID_CONTEXT;
9215 zfree(context->long_message);
9217 /* Free former message list if any */
9218 if (messages) isds_list_free(messages);
9220 #if HAVE_LIBCURL
9221 /* Check if connection is established
9222 * TODO: This check should be done downstairs. */
9223 if (!context->curl) return IE_CONNECTION_CLOSED;
9225 /* Build GetListOf*Messages request */
9226 request = xmlNewNode(NULL,
9227 (outgoing_direction) ?
9228 BAD_CAST "GetListOfSentMessages" :
9229 BAD_CAST "GetListOfReceivedMessages"
9231 if (!request) {
9232 isds_log_message(context,
9233 (outgoing_direction) ?
9234 _("Could not build GetListOfSentMessages request") :
9235 _("Could not build GetListOfReceivedMessages request")
9237 return IE_ERROR;
9239 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9240 if(!isds_ns) {
9241 isds_log_message(context, _("Could not create ISDS name space"));
9242 xmlFreeNode(request);
9243 return IE_ERROR;
9245 xmlSetNs(request, isds_ns);
9248 if (from_time) {
9249 err = timeval2timestring(from_time, &string);
9250 if (err) goto leave;
9252 INSERT_STRING(request, "dmFromTime", string);
9253 free(string); string = NULL;
9255 if (to_time) {
9256 err = timeval2timestring(to_time, &string);
9257 if (err) goto leave;
9259 INSERT_STRING(request, "dmToTime", string);
9260 free(string); string = NULL;
9262 if (outgoing_direction) {
9263 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9264 organization_unit_number, string);
9265 } else {
9266 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9267 organization_unit_number, string);
9270 if (status_filter > MESSAGESTATE_ANY) {
9271 isds_printf_message(context,
9272 _("Invalid message state filter value: %ld"), status_filter);
9273 err = IE_INVAL;
9274 goto leave;
9276 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9278 if (offset > 0 ) {
9279 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9280 } else {
9281 INSERT_STRING(request, "dmOffset", "1");
9284 /* number 0 means no limit */
9285 if (number && *number == 0) {
9286 INSERT_STRING(request, "dmLimit", NULL);
9287 } else {
9288 INSERT_ULONGINT(request, "dmLimit", number, string);
9292 isds_log(ILF_ISDS, ILL_DEBUG,
9293 (outgoing_direction) ?
9294 _("Sending GetListOfSentMessages request to ISDS\n") :
9295 _("Sending GetListOfReceivedMessages request to ISDS\n")
9298 /* Sent request */
9299 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9300 xmlFreeNode(request); request = NULL;
9302 if (err) {
9303 isds_log(ILF_ISDS, ILL_DEBUG,
9304 (outgoing_direction) ?
9305 _("Processing ISDS response on GetListOfSentMessages "
9306 "request failed\n") :
9307 _("Processing ISDS response on GetListOfReceivedMessages "
9308 "request failed\n")
9310 goto leave;
9313 /* Check for response status */
9314 err = isds_response_status(context, SERVICE_DM_INFO, response,
9315 &code, &message, NULL);
9316 if (err) {
9317 isds_log(ILF_ISDS, ILL_DEBUG,
9318 (outgoing_direction) ?
9319 _("ISDS response on GetListOfSentMessages request "
9320 "is missing status\n") :
9321 _("ISDS response on GetListOfReceivedMessages request "
9322 "is missing status\n")
9324 goto leave;
9327 /* Request processed, but nothing found */
9328 if (xmlStrcmp(code, BAD_CAST "0000")) {
9329 char *code_locale = _isds_utf82locale((char*)code);
9330 char *message_locale = _isds_utf82locale((char*)message);
9331 isds_log(ILF_ISDS, ILL_DEBUG,
9332 (outgoing_direction) ?
9333 _("Server refused GetListOfSentMessages request "
9334 "(code=%s, message=%s)\n") :
9335 _("Server refused GetListOfReceivedMessages request "
9336 "(code=%s, message=%s)\n"),
9337 code_locale, message_locale);
9338 isds_log_message(context, message_locale);
9339 free(code_locale);
9340 free(message_locale);
9341 err = IE_ISDS;
9342 goto leave;
9346 /* Extract data */
9347 xpath_ctx = xmlXPathNewContext(response);
9348 if (!xpath_ctx) {
9349 err = IE_ERROR;
9350 goto leave;
9352 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9353 err = IE_ERROR;
9354 goto leave;
9356 result = xmlXPathEvalExpression(
9357 (outgoing_direction) ?
9358 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9359 "isds:dmRecords/isds:dmRecord" :
9360 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9361 "isds:dmRecords/isds:dmRecord",
9362 xpath_ctx);
9363 if (!result) {
9364 err = IE_ERROR;
9365 goto leave;
9368 /* Fill output arguments in */
9369 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9370 struct isds_envelope *envelope;
9371 struct isds_list *item = NULL, *last_item = NULL;
9373 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9374 /* Create new message */
9375 item = calloc(1, sizeof(*item));
9376 if (!item) {
9377 err = IE_NOMEM;
9378 goto leave;
9380 item->destructor = (void(*)(void**)) &isds_message_free;
9381 item->data = calloc(1, sizeof(struct isds_message));
9382 if (!item->data) {
9383 isds_list_free(&item);
9384 err = IE_NOMEM;
9385 goto leave;
9388 /* Extract envelope data */
9389 xpath_ctx->node = result->nodesetval->nodeTab[count];
9390 envelope = NULL;
9391 err = extract_DmRecord(context, &envelope, xpath_ctx);
9392 if (err) {
9393 isds_list_free(&item);
9394 goto leave;
9397 /* Attach extracted envelope */
9398 ((struct isds_message *) item->data)->envelope = envelope;
9400 /* Append new message into the list */
9401 if (!*messages) {
9402 *messages = last_item = item;
9403 } else {
9404 last_item->next = item;
9405 last_item = item;
9409 if (number) *number = count;
9411 leave:
9412 if (err) {
9413 isds_list_free(messages);
9416 free(string);
9417 xmlXPathFreeObject(result);
9418 xmlXPathFreeContext(xpath_ctx);
9420 free(code);
9421 free(message);
9422 xmlFreeDoc(response);
9423 xmlFreeNode(request);
9425 if (!err)
9426 isds_log(ILF_ISDS, ILL_DEBUG,
9427 (outgoing_direction) ?
9428 _("GetListOfSentMessages request processed by server "
9429 "successfully.\n") :
9430 _("GetListOfReceivedMessages request processed by server "
9431 "successfully.\n")
9433 #else /* not HAVE_LIBCURL */
9434 err = IE_NOTSUP;
9435 #endif
9436 return err;
9440 /* Get list of outgoing (already sent) messages.
9441 * Any criterion argument can be NULL, if you don't care about it.
9442 * @context is session context. Must not be NULL.
9443 * @from_time is minimal time and date of message sending inclusive.
9444 * @to_time is maximal time and date of message sending inclusive
9445 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9446 * @status_filter is bit field of isds_message_status values. Use special
9447 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9448 * all values, you can use bit-wise arithmetic if you want.)
9449 * @offset is index of first message we are interested in. First message is 1.
9450 * Set to 0 (or 1) if you don't care.
9451 * @number is maximal length of list you want to get as input value, outputs
9452 * number of messages matching these criteria. Can be NULL if you don't care
9453 * (applies to output value either).
9454 * @messages is automatically reallocated list of isds_message's. Be ware that
9455 * it returns only brief overview (envelope and some other fields) about each
9456 * message, not the complete message. FIXME: Specify exact fields.
9457 * The list is sorted by delivery time in ascending order.
9458 * Use NULL if you don't care about the meta data (useful if you want to know
9459 * only the @number). If you provide &NULL, list will be allocated on heap,
9460 * if you provide pointer to non-NULL, list will be freed automatically at
9461 * first. Also in case of error the list will be NULLed.
9462 * @return IE_SUCCESS or appropriate error code. */
9463 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9464 const struct timeval *from_time, const struct timeval *to_time,
9465 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9466 const unsigned long int offset, unsigned long int *number,
9467 struct isds_list **messages) {
9469 return isds_get_list_of_messages(
9470 context, 1,
9471 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9472 offset, number,
9473 messages);
9477 /* Get list of incoming (addressed to you) messages.
9478 * Any criterion argument can be NULL, if you don't care about it.
9479 * @context is session context. Must not be NULL.
9480 * @from_time is minimal time and date of message sending inclusive.
9481 * @to_time is maximal time and date of message sending inclusive
9482 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9483 * @status_filter is bit field of isds_message_status values. Use special
9484 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9485 * all values, you can use bit-wise arithmetic if you want.)
9486 * @offset is index of first message we are interested in. First message is 1.
9487 * Set to 0 (or 1) if you don't care.
9488 * @number is maximal length of list you want to get as input value, outputs
9489 * number of messages matching these criteria. Can be NULL if you don't care
9490 * (applies to output value either).
9491 * @messages is automatically reallocated list of isds_message's. Be ware that
9492 * it returns only brief overview (envelope and some other fields) about each
9493 * message, not the complete message. FIXME: Specify exact fields.
9494 * Use NULL if you don't care about the meta data (useful if you want to know
9495 * only the @number). If you provide &NULL, list will be allocated on heap,
9496 * if you provide pointer to non-NULL, list will be freed automatically at
9497 * first. Also in case of error the list will be NULLed.
9498 * @return IE_SUCCESS or appropriate error code. */
9499 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9500 const struct timeval *from_time, const struct timeval *to_time,
9501 const long int *dmRecipientOrgUnitNum,
9502 const unsigned int status_filter,
9503 const unsigned long int offset, unsigned long int *number,
9504 struct isds_list **messages) {
9506 return isds_get_list_of_messages(
9507 context, 0,
9508 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9509 offset, number,
9510 messages);
9514 /* Get list of sent message state changes.
9515 * Any criterion argument can be NULL, if you don't care about it.
9516 * @context is session context. Must not be NULL.
9517 * @from_time is minimal time and date of status changes inclusive
9518 * @to_time is maximal time and date of status changes inclusive
9519 * @changed_states is automatically reallocated list of
9520 * isds_message_status_change's. If you provide &NULL, list will be allocated
9521 * on heap, if you provide pointer to non-NULL, list will be freed
9522 * automatically at first. Also in case of error the list will be NULLed.
9523 * XXX: The list item ordering is not specified.
9524 * XXX: Server provides only `recent' changes.
9525 * @return IE_SUCCESS or appropriate error code. */
9526 isds_error isds_get_list_of_sent_message_state_changes(
9527 struct isds_ctx *context,
9528 const struct timeval *from_time, const struct timeval *to_time,
9529 struct isds_list **changed_states) {
9531 isds_error err = IE_SUCCESS;
9532 #if HAVE_LIBCURL
9533 xmlNsPtr isds_ns = NULL;
9534 xmlNodePtr request = NULL, node;
9535 xmlDocPtr response = NULL;
9536 xmlXPathContextPtr xpath_ctx = NULL;
9537 xmlXPathObjectPtr result = NULL;
9538 xmlChar *string = NULL;
9539 int count = 0;
9540 #endif
9542 if (!context) return IE_INVALID_CONTEXT;
9543 zfree(context->long_message);
9545 /* Free former message list if any */
9546 isds_list_free(changed_states);
9548 #if HAVE_LIBCURL
9549 /* Check if connection is established
9550 * TODO: This check should be done downstairs. */
9551 if (!context->curl) return IE_CONNECTION_CLOSED;
9553 /* Build GetMessageStateChanges request */
9554 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
9555 if (!request) {
9556 isds_log_message(context,
9557 _("Could not build GetMessageStateChanges request"));
9558 return IE_ERROR;
9560 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9561 if(!isds_ns) {
9562 isds_log_message(context, _("Could not create ISDS name space"));
9563 xmlFreeNode(request);
9564 return IE_ERROR;
9566 xmlSetNs(request, isds_ns);
9569 if (from_time) {
9570 err = timeval2timestring(from_time, &string);
9571 if (err) goto leave;
9573 INSERT_STRING(request, "dmFromTime", string);
9574 zfree(string);
9576 if (to_time) {
9577 err = timeval2timestring(to_time, &string);
9578 if (err) goto leave;
9580 INSERT_STRING(request, "dmToTime", string);
9581 zfree(string);
9584 /* Sent request */
9585 err = send_destroy_request_check_response(context,
9586 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9587 &response, NULL, NULL);
9588 if (err) goto leave;
9591 /* Extract data */
9592 xpath_ctx = xmlXPathNewContext(response);
9593 if (!xpath_ctx) {
9594 err = IE_ERROR;
9595 goto leave;
9597 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9598 err = IE_ERROR;
9599 goto leave;
9601 result = xmlXPathEvalExpression(
9602 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9603 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9604 if (!result) {
9605 err = IE_ERROR;
9606 goto leave;
9609 /* Fill output arguments in */
9610 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9611 struct isds_list *item = NULL, *last_item = NULL;
9613 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9614 /* Create new status change */
9615 item = calloc(1, sizeof(*item));
9616 if (!item) {
9617 err = IE_NOMEM;
9618 goto leave;
9620 item->destructor =
9621 (void(*)(void**)) &isds_message_status_change_free;
9623 /* Extract message status change */
9624 xpath_ctx->node = result->nodesetval->nodeTab[count];
9625 err = extract_StateChangesRecord(context,
9626 (struct isds_message_status_change **) &item->data,
9627 xpath_ctx);
9628 if (err) {
9629 isds_list_free(&item);
9630 goto leave;
9633 /* Append new message status change into the list */
9634 if (!*changed_states) {
9635 *changed_states = last_item = item;
9636 } else {
9637 last_item->next = item;
9638 last_item = item;
9643 leave:
9644 if (err) {
9645 isds_list_free(changed_states);
9648 free(string);
9649 xmlXPathFreeObject(result);
9650 xmlXPathFreeContext(xpath_ctx);
9651 xmlFreeDoc(response);
9652 xmlFreeNode(request);
9654 if (!err)
9655 isds_log(ILF_ISDS, ILL_DEBUG,
9656 _("GetMessageStateChanges request processed by server "
9657 "successfully.\n"));
9658 #else /* not HAVE_LIBCURL */
9659 err = IE_NOTSUP;
9660 #endif
9661 return err;
9665 #if HAVE_LIBCURL
9666 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9667 * code
9668 * @context is session context
9669 * @service is ISDS WS service handler
9670 * @service_name is name of SERVICE_DM_OPERATIONS
9671 * @message_id is message ID to send as service argument to ISDS
9672 * @response is reallocated server SOAP body response as XML document
9673 * @raw_response is reallocated bit stream with response body. Use
9674 * NULL if you don't care
9675 * @raw_response_length is size of @raw_response in bytes
9676 * @code is reallocated ISDS status code
9677 * @status_message is reallocated ISDS status message
9678 * @return error coded from lower layer, context message will be set up
9679 * appropriately. */
9680 static isds_error build_send_check_message_request(struct isds_ctx *context,
9681 const isds_service service, const xmlChar *service_name,
9682 const char *message_id,
9683 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9684 xmlChar **code, xmlChar **status_message) {
9686 isds_error err = IE_SUCCESS;
9687 char *service_name_locale = NULL, *message_id_locale = NULL;
9688 xmlNodePtr request = NULL, node;
9689 xmlNsPtr isds_ns = NULL;
9691 if (!context) return IE_INVALID_CONTEXT;
9692 if (!service_name || !message_id) return IE_INVAL;
9693 if (!response || !code || !status_message) return IE_INVAL;
9694 if (!raw_response_length && raw_response) return IE_INVAL;
9696 /* Free output argument */
9697 xmlFreeDoc(*response); *response = NULL;
9698 if (raw_response) zfree(*raw_response);
9699 zfree(*code);
9700 zfree(*status_message);
9703 /* Check if connection is established
9704 * TODO: This check should be done downstairs. */
9705 if (!context->curl) return IE_CONNECTION_CLOSED;
9707 service_name_locale = _isds_utf82locale((char*)service_name);
9708 message_id_locale = _isds_utf82locale(message_id);
9709 if (!service_name_locale || !message_id_locale) {
9710 err = IE_NOMEM;
9711 goto leave;
9714 /* Build request */
9715 request = xmlNewNode(NULL, service_name);
9716 if (!request) {
9717 isds_printf_message(context,
9718 _("Could not build %s request for %s message ID"),
9719 service_name_locale, message_id_locale);
9720 err = IE_ERROR;
9721 goto leave;
9723 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9724 if(!isds_ns) {
9725 isds_log_message(context, _("Could not create ISDS name space"));
9726 err = IE_ERROR;
9727 goto leave;
9729 xmlSetNs(request, isds_ns);
9732 /* Add requested ID */
9733 err = validate_message_id_length(context, (xmlChar *) message_id);
9734 if (err) goto leave;
9735 INSERT_STRING(request, "dmID", message_id);
9738 isds_log(ILF_ISDS, ILL_DEBUG,
9739 _("Sending %s request for %s message ID to ISDS\n"),
9740 service_name_locale, message_id_locale);
9742 /* Send request */
9743 err = _isds(context, service, request, response,
9744 raw_response, raw_response_length);
9745 xmlFreeNode(request); request = NULL;
9747 if (err) {
9748 isds_log(ILF_ISDS, ILL_DEBUG,
9749 _("Processing ISDS response on %s request failed\n"),
9750 service_name_locale);
9751 goto leave;
9754 /* Check for response status */
9755 err = isds_response_status(context, service, *response,
9756 code, status_message, NULL);
9757 if (err) {
9758 isds_log(ILF_ISDS, ILL_DEBUG,
9759 _("ISDS response on %s request is missing status\n"),
9760 service_name_locale);
9761 goto leave;
9764 /* Request processed, but nothing found */
9765 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9766 char *code_locale = _isds_utf82locale((char*) *code);
9767 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9768 isds_log(ILF_ISDS, ILL_DEBUG,
9769 _("Server refused %s request for %s message ID "
9770 "(code=%s, message=%s)\n"),
9771 service_name_locale, message_id_locale,
9772 code_locale, status_message_locale);
9773 isds_log_message(context, status_message_locale);
9774 free(code_locale);
9775 free(status_message_locale);
9776 err = IE_ISDS;
9777 goto leave;
9780 leave:
9781 free(message_id_locale);
9782 free(service_name_locale);
9783 xmlFreeNode(request);
9784 return err;
9788 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9789 * signed data and free ISDS response.
9790 * @context is session context
9791 * @message_id is UTF-8 encoded message ID for logging purpose
9792 * @response is parsed XML document. It will be freed and NULLed in the middle
9793 * of function run to save memory. This is not guaranteed in case of error.
9794 * @request_name is name of ISDS request used to construct response root
9795 * element name and for logging purpose.
9796 * @raw is reallocated output buffer with DER encoded CMS data
9797 * @raw_length is size of @raw buffer in bytes
9798 * @returns standard error codes, in case of error, @raw will be freed and
9799 * NULLed, @response sometimes. */
9800 static isds_error find_extract_signed_data_free_response(
9801 struct isds_ctx *context, const xmlChar *message_id,
9802 xmlDocPtr *response, const xmlChar *request_name,
9803 void **raw, size_t *raw_length) {
9805 isds_error err = IE_SUCCESS;
9806 char *xpath_expression = NULL;
9807 xmlXPathContextPtr xpath_ctx = NULL;
9808 xmlXPathObjectPtr result = NULL;
9809 char *encoded_structure = NULL;
9811 if (!context) return IE_INVALID_CONTEXT;
9812 if (!raw) return IE_INVAL;
9813 zfree(*raw);
9814 if (!message_id || !response || !*response || !request_name || !raw_length)
9815 return IE_INVAL;
9817 /* Build XPath expression */
9818 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9819 "Response/isds:dmSignature");
9820 if (!xpath_expression) return IE_NOMEM;
9822 /* Extract data */
9823 xpath_ctx = xmlXPathNewContext(*response);
9824 if (!xpath_ctx) {
9825 err = IE_ERROR;
9826 goto leave;
9828 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9829 err = IE_ERROR;
9830 goto leave;
9832 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9833 if (!result) {
9834 err = IE_ERROR;
9835 goto leave;
9837 /* Empty response */
9838 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9839 char *message_id_locale = _isds_utf82locale((char*) message_id);
9840 isds_printf_message(context,
9841 _("Server did not return any signed data for message ID `%s' "
9842 "on %s request"),
9843 message_id_locale, request_name);
9844 free(message_id_locale);
9845 err = IE_ISDS;
9846 goto leave;
9848 /* More responses */
9849 if (result->nodesetval->nodeNr > 1) {
9850 char *message_id_locale = _isds_utf82locale((char*) message_id);
9851 isds_printf_message(context,
9852 _("Server did return more signed data for message ID `%s' "
9853 "on %s request"),
9854 message_id_locale, request_name);
9855 free(message_id_locale);
9856 err = IE_ISDS;
9857 goto leave;
9859 /* One response */
9860 xpath_ctx->node = result->nodesetval->nodeTab[0];
9862 /* Extract PKCS#7 structure */
9863 EXTRACT_STRING(".", encoded_structure);
9864 if (!encoded_structure) {
9865 isds_log_message(context, _("dmSignature element is empty"));
9868 /* Here we have delivery info as standalone CMS in encoded_structure.
9869 * We don't need any other data, free them: */
9870 xmlXPathFreeObject(result); result = NULL;
9871 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9872 xmlFreeDoc(*response); *response = NULL;
9875 /* Decode PKCS#7 to DER format */
9876 *raw_length = _isds_b64decode(encoded_structure, raw);
9877 if (*raw_length == (size_t) -1) {
9878 isds_log_message(context,
9879 _("Error while Base64-decoding PKCS#7 structure"));
9880 err = IE_ERROR;
9881 goto leave;
9884 leave:
9885 if (err) {
9886 zfree(*raw);
9887 raw_length = 0;
9890 free(encoded_structure);
9891 xmlXPathFreeObject(result);
9892 xmlXPathFreeContext(xpath_ctx);
9893 free(xpath_expression);
9895 return err;
9897 #endif /* HAVE_LIBCURL */
9900 /* Download incoming message envelope identified by ID.
9901 * @context is session context
9902 * @message_id is message identifier (you can get them from
9903 * isds_get_list_of_received_messages())
9904 * @message is automatically reallocated message retrieved from ISDS.
9905 * It will miss documents per se. Use isds_get_received_message(), if you are
9906 * interested in documents (content) too.
9907 * Returned hash and timestamp require documents to be verifiable. */
9908 isds_error isds_get_received_envelope(struct isds_ctx *context,
9909 const char *message_id, struct isds_message **message) {
9911 isds_error err = IE_SUCCESS;
9912 #if HAVE_LIBCURL
9913 xmlDocPtr response = NULL;
9914 xmlChar *code = NULL, *status_message = NULL;
9915 xmlXPathContextPtr xpath_ctx = NULL;
9916 xmlXPathObjectPtr result = NULL;
9917 #endif
9919 if (!context) return IE_INVALID_CONTEXT;
9920 zfree(context->long_message);
9922 /* Free former message if any */
9923 if (!message) return IE_INVAL;
9924 isds_message_free(message);
9926 #if HAVE_LIBCURL
9927 /* Do request and check for success */
9928 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9929 BAD_CAST "MessageEnvelopeDownload", message_id,
9930 &response, NULL, NULL, &code, &status_message);
9931 if (err) goto leave;
9933 /* Extract data */
9934 xpath_ctx = xmlXPathNewContext(response);
9935 if (!xpath_ctx) {
9936 err = IE_ERROR;
9937 goto leave;
9939 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9940 err = IE_ERROR;
9941 goto leave;
9943 result = xmlXPathEvalExpression(
9944 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9945 "isds:dmReturnedMessageEnvelope",
9946 xpath_ctx);
9947 if (!result) {
9948 err = IE_ERROR;
9949 goto leave;
9951 /* Empty response */
9952 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9953 char *message_id_locale = _isds_utf82locale((char*) message_id);
9954 isds_printf_message(context,
9955 _("Server did not return any envelope for ID `%s' "
9956 "on MessageEnvelopeDownload request"), message_id_locale);
9957 free(message_id_locale);
9958 err = IE_ISDS;
9959 goto leave;
9961 /* More envelops */
9962 if (result->nodesetval->nodeNr > 1) {
9963 char *message_id_locale = _isds_utf82locale((char*) message_id);
9964 isds_printf_message(context,
9965 _("Server did return more envelopes for ID `%s' "
9966 "on MessageEnvelopeDownload request"), message_id_locale);
9967 free(message_id_locale);
9968 err = IE_ISDS;
9969 goto leave;
9971 /* One message */
9972 xpath_ctx->node = result->nodesetval->nodeTab[0];
9974 /* Extract the envelope (= message without documents, hence 0) */
9975 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9976 if (err) goto leave;
9978 /* Save XML blob */
9979 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9980 &(*message)->raw_length);
9982 leave:
9983 if (err) {
9984 isds_message_free(message);
9987 xmlXPathFreeObject(result);
9988 xmlXPathFreeContext(xpath_ctx);
9990 free(code);
9991 free(status_message);
9992 if (!*message || !(*message)->xml) {
9993 xmlFreeDoc(response);
9996 if (!err)
9997 isds_log(ILF_ISDS, ILL_DEBUG,
9998 _("MessageEnvelopeDownload request processed by server "
9999 "successfully.\n")
10001 #else /* not HAVE_LIBCURL */
10002 err = IE_NOTSUP;
10003 #endif
10004 return err;
10008 /* Load delivery info of any format from buffer.
10009 * @context is session context
10010 * @raw_type advertises format of @buffer content. Only delivery info types
10011 * are accepted.
10012 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10013 * retrieve such data from message->raw after calling
10014 * isds_get_signed_delivery_info().
10015 * @length is length of buffer in bytes.
10016 * @message is automatically reallocated message parsed from @buffer.
10017 * @strategy selects how buffer will be attached into raw isds_message member.
10018 * */
10019 isds_error isds_load_delivery_info(struct isds_ctx *context,
10020 const isds_raw_type raw_type,
10021 const void *buffer, const size_t length,
10022 struct isds_message **message, const isds_buffer_strategy strategy) {
10024 isds_error err = IE_SUCCESS;
10025 message_ns_type message_ns;
10026 xmlDocPtr message_doc = NULL;
10027 xmlXPathContextPtr xpath_ctx = NULL;
10028 xmlXPathObjectPtr result = NULL;
10029 void *xml_stream = NULL;
10030 size_t xml_stream_length = 0;
10032 if (!context) return IE_INVALID_CONTEXT;
10033 zfree(context->long_message);
10034 if (!message) return IE_INVAL;
10035 isds_message_free(message);
10036 if (!buffer) return IE_INVAL;
10039 /* Select buffer format and extract XML from CMS*/
10040 switch (raw_type) {
10041 case RAWTYPE_DELIVERYINFO:
10042 message_ns = MESSAGE_NS_UNSIGNED;
10043 xml_stream = (void *) buffer;
10044 xml_stream_length = length;
10045 break;
10047 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
10048 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10049 xml_stream = (void *) buffer;
10050 xml_stream_length = length;
10051 break;
10053 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10054 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10055 err = _isds_extract_cms_data(context, buffer, length,
10056 &xml_stream, &xml_stream_length);
10057 if (err) goto leave;
10058 break;
10060 default:
10061 isds_log_message(context, _("Bad raw delivery representation type"));
10062 return IE_INVAL;
10063 break;
10066 isds_log(ILF_ISDS, ILL_DEBUG,
10067 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10068 xml_stream_length, xml_stream);
10070 /* Convert delivery info XML stream into XPath context */
10071 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10072 if (!message_doc) {
10073 err = IE_XML;
10074 goto leave;
10076 xpath_ctx = xmlXPathNewContext(message_doc);
10077 if (!xpath_ctx) {
10078 err = IE_ERROR;
10079 goto leave;
10081 /* XXX: Name spaces mangled for signed delivery info:
10082 * http://isds.czechpoint.cz/v20/delivery:
10084 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10085 * <q:dmDelivery>
10086 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10087 * <p:dmID>170272</p:dmID>
10088 * ...
10089 * </p:dmDm>
10090 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10091 * ...
10092 * </q:dmEvents>...</q:dmEvents>
10093 * </q:dmDelivery>
10094 * </q:GetDeliveryInfoResponse>
10095 * */
10096 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10097 err = IE_ERROR;
10098 goto leave;
10100 result = xmlXPathEvalExpression(
10101 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10102 xpath_ctx);
10103 if (!result) {
10104 err = IE_ERROR;
10105 goto leave;
10107 /* Empty delivery info */
10108 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10109 isds_printf_message(context,
10110 _("XML document is not sisds:dmDelivery document"));
10111 err = IE_ISDS;
10112 goto leave;
10114 /* More delivery info's */
10115 if (result->nodesetval->nodeNr > 1) {
10116 isds_printf_message(context,
10117 _("XML document has more sisds:dmDelivery elements"));
10118 err = IE_ISDS;
10119 goto leave;
10121 /* One delivery info */
10122 xpath_ctx->node = result->nodesetval->nodeTab[0];
10124 /* Extract the envelope (= message without documents, hence 0).
10125 * XXX: extract_TReturnedMessage() can obtain attachments size,
10126 * but delivery info carries none. It's coded as option elements,
10127 * so it should work. */
10128 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10129 if (err) goto leave;
10131 /* Extract events */
10132 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10133 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10134 if (err) { err = IE_ERROR; goto leave; }
10135 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10136 if (err) goto leave;
10138 /* Append raw CMS structure into message */
10139 (*message)->raw_type = raw_type;
10140 switch (strategy) {
10141 case BUFFER_DONT_STORE:
10142 break;
10143 case BUFFER_COPY:
10144 (*message)->raw = malloc(length);
10145 if (!(*message)->raw) {
10146 err = IE_NOMEM;
10147 goto leave;
10149 memcpy((*message)->raw, buffer, length);
10150 (*message)->raw_length = length;
10151 break;
10152 case BUFFER_MOVE:
10153 (*message)->raw = (void *) buffer;
10154 (*message)->raw_length = length;
10155 break;
10156 default:
10157 err = IE_ENUM;
10158 goto leave;
10161 leave:
10162 if (err) {
10163 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10164 isds_message_free(message);
10167 xmlXPathFreeObject(result);
10168 xmlXPathFreeContext(xpath_ctx);
10169 if (!*message || !(*message)->xml) {
10170 xmlFreeDoc(message_doc);
10172 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10174 if (!err)
10175 isds_log(ILF_ISDS, ILL_DEBUG,
10176 _("Delivery info loaded successfully.\n"));
10177 return err;
10181 /* Download signed delivery info-sheet of given message identified by ID.
10182 * @context is session context
10183 * @message_id is message identifier (you can get them from
10184 * isds_get_list_of_{sent,received}_messages())
10185 * @message is automatically reallocated message retrieved from ISDS.
10186 * It will miss documents per se. Use isds_get_signed_received_message(),
10187 * if you are interested in documents (content). OTOH, only this function
10188 * can get list events message has gone through. */
10189 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10190 const char *message_id, struct isds_message **message) {
10192 isds_error err = IE_SUCCESS;
10193 #if HAVE_LIBCURL
10194 xmlDocPtr response = NULL;
10195 xmlChar *code = NULL, *status_message = NULL;
10196 void *raw = NULL;
10197 size_t raw_length = 0;
10198 #endif
10200 if (!context) return IE_INVALID_CONTEXT;
10201 zfree(context->long_message);
10203 /* Free former message if any */
10204 if (!message) return IE_INVAL;
10205 isds_message_free(message);
10207 #if HAVE_LIBCURL
10208 /* Do request and check for success */
10209 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10210 BAD_CAST "GetSignedDeliveryInfo", message_id,
10211 &response, NULL, NULL, &code, &status_message);
10212 if (err) goto leave;
10214 /* Find signed delivery info, extract it into raw and maybe free
10215 * response */
10216 err = find_extract_signed_data_free_response(context,
10217 (xmlChar *)message_id, &response,
10218 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10219 if (err) goto leave;
10221 /* Parse delivery info */
10222 err = isds_load_delivery_info(context,
10223 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10224 message, BUFFER_MOVE);
10225 if (err) goto leave;
10227 raw = NULL;
10229 leave:
10230 if (err) {
10231 isds_message_free(message);
10234 free(raw);
10235 free(code);
10236 free(status_message);
10237 xmlFreeDoc(response);
10239 if (!err)
10240 isds_log(ILF_ISDS, ILL_DEBUG,
10241 _("GetSignedDeliveryInfo request processed by server "
10242 "successfully.\n")
10244 #else /* not HAVE_LIBCURL */
10245 err = IE_NOTSUP;
10246 #endif
10247 return err;
10251 /* Download delivery info-sheet of given message identified by ID.
10252 * @context is session context
10253 * @message_id is message identifier (you can get them from
10254 * isds_get_list_of_{sent,received}_messages())
10255 * @message is automatically reallocated message retrieved from ISDS.
10256 * It will miss documents per se. Use isds_get_received_message(), if you are
10257 * interested in documents (content). OTOH, only this function can get list
10258 * of events message has gone through. */
10259 isds_error isds_get_delivery_info(struct isds_ctx *context,
10260 const char *message_id, struct isds_message **message) {
10262 isds_error err = IE_SUCCESS;
10263 #if HAVE_LIBCURL
10264 xmlDocPtr response = NULL;
10265 xmlChar *code = NULL, *status_message = NULL;
10266 xmlNodePtr delivery_node = NULL;
10267 void *raw = NULL;
10268 size_t raw_length = 0;
10269 #endif
10271 if (!context) return IE_INVALID_CONTEXT;
10272 zfree(context->long_message);
10274 /* Free former message if any */
10275 if (!message) return IE_INVAL;
10276 isds_message_free(message);
10278 #if HAVE_LIBCURL
10279 /* Do request and check for success */
10280 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10281 BAD_CAST "GetDeliveryInfo", message_id,
10282 &response, NULL, NULL, &code, &status_message);
10283 if (err) goto leave;
10286 /* Serialize delivery info */
10287 delivery_node = xmlDocGetRootElement(response);
10288 if (!delivery_node) {
10289 char *message_id_locale = _isds_utf82locale((char*) message_id);
10290 isds_printf_message(context,
10291 _("Server did not return any delivery info for ID `%s' "
10292 "on GetDeliveryInfo request"), message_id_locale);
10293 free(message_id_locale);
10294 err = IE_ISDS;
10295 goto leave;
10297 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10298 if (err) goto leave;
10300 /* Parse delivery info */
10301 /* TODO: Here we parse the response second time. We could single delivery
10302 * parser from isds_load_delivery_info() to make things faster. */
10303 err = isds_load_delivery_info(context,
10304 RAWTYPE_DELIVERYINFO, raw, raw_length,
10305 message, BUFFER_MOVE);
10306 if (err) goto leave;
10308 raw = NULL;
10311 leave:
10312 if (err) {
10313 isds_message_free(message);
10316 free(raw);
10317 free(code);
10318 free(status_message);
10319 xmlFreeDoc(response);
10321 if (!err)
10322 isds_log(ILF_ISDS, ILL_DEBUG,
10323 _("GetDeliveryInfo request processed by server "
10324 "successfully.\n")
10326 #else /* not HAVE_LIBCURL */
10327 err = IE_NOTSUP;
10328 #endif
10329 return err;
10333 /* Download incoming message identified by ID.
10334 * @context is session context
10335 * @message_id is message identifier (you can get them from
10336 * isds_get_list_of_received_messages())
10337 * @message is automatically reallocated message retrieved from ISDS */
10338 isds_error isds_get_received_message(struct isds_ctx *context,
10339 const char *message_id, struct isds_message **message) {
10341 isds_error err = IE_SUCCESS;
10342 #if HAVE_LIBCURL
10343 xmlDocPtr response = NULL;
10344 void *xml_stream = NULL;
10345 size_t xml_stream_length;
10346 xmlChar *code = NULL, *status_message = NULL;
10347 xmlXPathContextPtr xpath_ctx = NULL;
10348 xmlXPathObjectPtr result = NULL;
10349 char *phys_path = NULL;
10350 size_t phys_start, phys_end;
10351 #endif
10353 if (!context) return IE_INVALID_CONTEXT;
10354 zfree(context->long_message);
10356 /* Free former message if any */
10357 if (NULL == message) return IE_INVAL;
10358 if (message) isds_message_free(message);
10360 #if HAVE_LIBCURL
10361 /* Do request and check for success */
10362 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10363 BAD_CAST "MessageDownload", message_id,
10364 &response, &xml_stream, &xml_stream_length,
10365 &code, &status_message);
10366 if (err) goto leave;
10368 /* Extract data */
10369 xpath_ctx = xmlXPathNewContext(response);
10370 if (!xpath_ctx) {
10371 err = IE_ERROR;
10372 goto leave;
10374 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10375 err = IE_ERROR;
10376 goto leave;
10378 result = xmlXPathEvalExpression(
10379 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10380 xpath_ctx);
10381 if (!result) {
10382 err = IE_ERROR;
10383 goto leave;
10385 /* Empty response */
10386 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10387 char *message_id_locale = _isds_utf82locale((char*) message_id);
10388 isds_printf_message(context,
10389 _("Server did not return any message for ID `%s' "
10390 "on MessageDownload request"), message_id_locale);
10391 free(message_id_locale);
10392 err = IE_ISDS;
10393 goto leave;
10395 /* More messages */
10396 if (result->nodesetval->nodeNr > 1) {
10397 char *message_id_locale = _isds_utf82locale((char*) message_id);
10398 isds_printf_message(context,
10399 _("Server did return more messages for ID `%s' "
10400 "on MessageDownload request"), message_id_locale);
10401 free(message_id_locale);
10402 err = IE_ISDS;
10403 goto leave;
10405 /* One message */
10406 xpath_ctx->node = result->nodesetval->nodeTab[0];
10408 /* Extract the message */
10409 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10410 if (err) goto leave;
10412 /* Locate raw XML blob */
10413 phys_path = strdup(
10414 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10415 PHYSXML_ELEMENT_SEPARATOR
10416 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10417 PHYSXML_ELEMENT_SEPARATOR
10418 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10420 if (!phys_path) {
10421 err = IE_NOMEM;
10422 goto leave;
10424 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10425 phys_path, &phys_start, &phys_end);
10426 zfree(phys_path);
10427 if (err) {
10428 isds_log_message(context,
10429 _("Substring with isds:MessageDownloadResponse element "
10430 "could not be located in raw SOAP message"));
10431 goto leave;
10433 /* Save XML blob */
10434 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10435 &(*message)->raw_length);*/
10436 /* TODO: Store name space declarations from ancestors */
10437 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10438 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10439 (*message)->raw_length = phys_end - phys_start + 1;
10440 (*message)->raw = malloc((*message)->raw_length);
10441 if (!(*message)->raw) {
10442 err = IE_NOMEM;
10443 goto leave;
10445 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10448 leave:
10449 if (err) {
10450 isds_message_free(message);
10453 free(phys_path);
10455 xmlXPathFreeObject(result);
10456 xmlXPathFreeContext(xpath_ctx);
10458 free(code);
10459 free(status_message);
10460 free(xml_stream);
10461 if (!*message || !(*message)->xml) {
10462 xmlFreeDoc(response);
10465 if (!err)
10466 isds_log(ILF_ISDS, ILL_DEBUG,
10467 _("MessageDownload request processed by server "
10468 "successfully.\n")
10470 #else /* not HAVE_LIBCURL */
10471 err = IE_NOTSUP;
10472 #endif
10473 return err;
10477 /* Load message of any type from buffer.
10478 * @context is session context
10479 * @raw_type defines content type of @buffer. Only message types are allowed.
10480 * @buffer is message raw representation. Format (CMS, plain signed,
10481 * message direction) is defined in @raw_type. You can retrieve such data
10482 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10483 * @length is length of buffer in bytes.
10484 * @message is automatically reallocated message parsed from @buffer.
10485 * @strategy selects how buffer will be attached into raw isds_message member.
10486 * */
10487 isds_error isds_load_message(struct isds_ctx *context,
10488 const isds_raw_type raw_type, const void *buffer, const size_t length,
10489 struct isds_message **message, const isds_buffer_strategy strategy) {
10491 isds_error err = IE_SUCCESS;
10492 void *xml_stream = NULL;
10493 size_t xml_stream_length = 0;
10494 message_ns_type message_ns;
10495 xmlDocPtr message_doc = NULL;
10496 xmlXPathContextPtr xpath_ctx = NULL;
10497 xmlXPathObjectPtr result = NULL;
10499 if (!context) return IE_INVALID_CONTEXT;
10500 zfree(context->long_message);
10501 if (!message) return IE_INVAL;
10502 isds_message_free(message);
10503 if (!buffer) return IE_INVAL;
10506 /* Select buffer format and extract XML from CMS*/
10507 switch (raw_type) {
10508 case RAWTYPE_INCOMING_MESSAGE:
10509 message_ns = MESSAGE_NS_UNSIGNED;
10510 xml_stream = (void *) buffer;
10511 xml_stream_length = length;
10512 break;
10514 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10515 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10516 xml_stream = (void *) buffer;
10517 xml_stream_length = length;
10518 break;
10520 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10521 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10522 err = _isds_extract_cms_data(context, buffer, length,
10523 &xml_stream, &xml_stream_length);
10524 if (err) goto leave;
10525 break;
10527 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10528 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10529 xml_stream = (void *) buffer;
10530 xml_stream_length = length;
10531 break;
10533 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10534 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10535 err = _isds_extract_cms_data(context, buffer, length,
10536 &xml_stream, &xml_stream_length);
10537 if (err) goto leave;
10538 break;
10540 default:
10541 isds_log_message(context, _("Bad raw message representation type"));
10542 return IE_INVAL;
10543 break;
10546 isds_log(ILF_ISDS, ILL_DEBUG,
10547 _("Loading message:\n%.*s\nEnd of message\n"),
10548 xml_stream_length, xml_stream);
10550 /* Convert messages XML stream into XPath context */
10551 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10552 if (!message_doc) {
10553 err = IE_XML;
10554 goto leave;
10556 xpath_ctx = xmlXPathNewContext(message_doc);
10557 if (!xpath_ctx) {
10558 err = IE_ERROR;
10559 goto leave;
10561 /* XXX: Standard name space for unsigned incoming direction:
10562 * http://isds.czechpoint.cz/v20/
10564 * XXX: Name spaces mangled for signed outgoing direction:
10565 * http://isds.czechpoint.cz/v20/SentMessage:
10567 * <q:MessageDownloadResponse
10568 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10569 * <q:dmReturnedMessage>
10570 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10571 * <p:dmID>151916</p:dmID>
10572 * ...
10573 * </p:dmDm>
10574 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10575 * ...
10576 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10577 * </q:dmReturnedMessage>
10578 * </q:MessageDownloadResponse>
10580 * XXX: Name spaces mangled for signed incoming direction:
10581 * http://isds.czechpoint.cz/v20/message:
10583 * <q:MessageDownloadResponse
10584 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10585 * <q:dmReturnedMessage>
10586 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10587 * <p:dmID>151916</p:dmID>
10588 * ...
10589 * </p:dmDm>
10590 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10591 * ...
10592 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10593 * </q:dmReturnedMessage>
10594 * </q:MessageDownloadResponse>
10596 * Stupidity of ISDS developers is unlimited */
10597 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10598 err = IE_ERROR;
10599 goto leave;
10601 result = xmlXPathEvalExpression(
10602 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10603 xpath_ctx);
10604 if (!result) {
10605 err = IE_ERROR;
10606 goto leave;
10608 /* Empty message */
10609 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10610 isds_printf_message(context,
10611 _("XML document does not contain "
10612 "sisds:dmReturnedMessage element"));
10613 err = IE_ISDS;
10614 goto leave;
10616 /* More messages */
10617 if (result->nodesetval->nodeNr > 1) {
10618 isds_printf_message(context,
10619 _("XML document has more sisds:dmReturnedMessage elements"));
10620 err = IE_ISDS;
10621 goto leave;
10623 /* One message */
10624 xpath_ctx->node = result->nodesetval->nodeTab[0];
10626 /* Extract the message */
10627 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10628 if (err) goto leave;
10630 /* Append raw buffer into message */
10631 (*message)->raw_type = raw_type;
10632 switch (strategy) {
10633 case BUFFER_DONT_STORE:
10634 break;
10635 case BUFFER_COPY:
10636 (*message)->raw = malloc(length);
10637 if (!(*message)->raw) {
10638 err = IE_NOMEM;
10639 goto leave;
10641 memcpy((*message)->raw, buffer, length);
10642 (*message)->raw_length = length;
10643 break;
10644 case BUFFER_MOVE:
10645 (*message)->raw = (void *) buffer;
10646 (*message)->raw_length = length;
10647 break;
10648 default:
10649 err = IE_ENUM;
10650 goto leave;
10654 leave:
10655 if (err) {
10656 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10657 isds_message_free(message);
10660 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10661 xmlXPathFreeObject(result);
10662 xmlXPathFreeContext(xpath_ctx);
10663 if (!*message || !(*message)->xml) {
10664 xmlFreeDoc(message_doc);
10667 if (!err)
10668 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10669 return err;
10673 /* Determine type of raw message or delivery info according some heuristics.
10674 * It does not validate the raw blob.
10675 * @context is session context
10676 * @raw_type returns content type of @buffer. Valid only if exit code of this
10677 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10678 * reallocated memory.
10679 * @buffer is message raw representation.
10680 * @length is length of buffer in bytes. */
10681 isds_error isds_guess_raw_type(struct isds_ctx *context,
10682 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10683 isds_error err;
10684 void *xml_stream = NULL;
10685 size_t xml_stream_length = 0;
10686 xmlDocPtr document = NULL;
10687 xmlNodePtr root = NULL;
10689 if (!context) return IE_INVALID_CONTEXT;
10690 zfree(context->long_message);
10691 if (length == 0 || !buffer) return IE_INVAL;
10692 if (!raw_type) return IE_INVAL;
10694 /* Try CMS */
10695 err = _isds_extract_cms_data(context, buffer, length,
10696 &xml_stream, &xml_stream_length);
10697 if (err) {
10698 xml_stream = (void *) buffer;
10699 xml_stream_length = (size_t) length;
10700 err = IE_SUCCESS;
10703 /* Try XML */
10704 document = xmlParseMemory(xml_stream, xml_stream_length);
10705 if (!document) {
10706 isds_printf_message(context,
10707 _("Could not parse data as XML document"));
10708 err = IE_NOTSUP;
10709 goto leave;
10712 /* Get root element */
10713 root = xmlDocGetRootElement(document);
10714 if (!root) {
10715 isds_printf_message(context,
10716 _("XML document is missing root element"));
10717 err = IE_XML;
10718 goto leave;
10721 if (!root->ns || !root->ns->href) {
10722 isds_printf_message(context,
10723 _("Root element does not belong to any name space"));
10724 err = IE_NOTSUP;
10725 goto leave;
10728 /* Test name space */
10729 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10730 if (xml_stream == buffer)
10731 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10732 else
10733 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10734 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10735 if (xml_stream == buffer)
10736 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10737 else
10738 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10739 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10740 if (xml_stream == buffer)
10741 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10742 else
10743 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10744 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10745 if (xml_stream != buffer) {
10746 isds_printf_message(context,
10747 _("Document in ISDS name space is encapsulated into CMS" ));
10748 err = IE_NOTSUP;
10749 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10750 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10751 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10752 *raw_type = RAWTYPE_DELIVERYINFO;
10753 else {
10754 isds_printf_message(context,
10755 _("Unknown root element in ISDS name space"));
10756 err = IE_NOTSUP;
10758 } else {
10759 isds_printf_message(context,
10760 _("Unknown name space"));
10761 err = IE_NOTSUP;
10764 leave:
10765 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10766 xmlFreeDoc(document);
10767 return err;
10771 /* Download signed incoming/outgoing message identified by ID.
10772 * @context is session context
10773 * @output is true for outgoing message, false for incoming message
10774 * @message_id is message identifier (you can get them from
10775 * isds_get_list_of_{sent,received}_messages())
10776 * @message is automatically reallocated message retrieved from ISDS. The raw
10777 * member will be filled with PKCS#7 structure in DER format. */
10778 static isds_error isds_get_signed_message(struct isds_ctx *context,
10779 const _Bool outgoing, const char *message_id,
10780 struct isds_message **message) {
10782 isds_error err = IE_SUCCESS;
10783 #if HAVE_LIBCURL
10784 xmlDocPtr response = NULL;
10785 xmlChar *code = NULL, *status_message = NULL;
10786 xmlXPathContextPtr xpath_ctx = NULL;
10787 xmlXPathObjectPtr result = NULL;
10788 char *encoded_structure = NULL;
10789 void *raw = NULL;
10790 size_t raw_length = 0;
10791 #endif
10793 if (!context) return IE_INVALID_CONTEXT;
10794 zfree(context->long_message);
10795 if (!message) return IE_INVAL;
10796 isds_message_free(message);
10798 #if HAVE_LIBCURL
10799 /* Do request and check for success */
10800 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10801 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10802 BAD_CAST "SignedMessageDownload",
10803 message_id, &response, NULL, NULL, &code, &status_message);
10804 if (err) goto leave;
10806 /* Find signed message, extract it into raw and maybe free
10807 * response */
10808 err = find_extract_signed_data_free_response(context,
10809 (xmlChar *)message_id, &response,
10810 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10811 BAD_CAST "SignedMessageDownload",
10812 &raw, &raw_length);
10813 if (err) goto leave;
10815 /* Parse message */
10816 err = isds_load_message(context,
10817 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10818 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10819 raw, raw_length, message, BUFFER_MOVE);
10820 if (err) goto leave;
10822 raw = NULL;
10824 leave:
10825 if (err) {
10826 isds_message_free(message);
10829 free(encoded_structure);
10830 xmlXPathFreeObject(result);
10831 xmlXPathFreeContext(xpath_ctx);
10832 free(raw);
10834 free(code);
10835 free(status_message);
10836 xmlFreeDoc(response);
10838 if (!err)
10839 isds_log(ILF_ISDS, ILL_DEBUG,
10840 (outgoing) ?
10841 _("SignedSentMessageDownload request processed by server "
10842 "successfully.\n") :
10843 _("SignedMessageDownload request processed by server "
10844 "successfully.\n")
10846 #else /* not HAVE_LIBCURL */
10847 err = IE_NOTSUP;
10848 #endif
10849 return err;
10853 /* Download signed incoming message identified by ID.
10854 * @context is session context
10855 * @message_id is message identifier (you can get them from
10856 * isds_get_list_of_received_messages())
10857 * @message is automatically reallocated message retrieved from ISDS. The raw
10858 * member will be filled with PKCS#7 structure in DER format. */
10859 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10860 const char *message_id, struct isds_message **message) {
10861 return isds_get_signed_message(context, 0, message_id, message);
10865 /* Download signed outgoing message identified by ID.
10866 * @context is session context
10867 * @message_id is message identifier (you can get them from
10868 * isds_get_list_of_sent_messages())
10869 * @message is automatically reallocated message retrieved from ISDS. The raw
10870 * member will be filled with PKCS#7 structure in DER format. */
10871 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10872 const char *message_id, struct isds_message **message) {
10873 return isds_get_signed_message(context, 1, message_id, message);
10877 /* Get type and name of user who sent a message identified by ID.
10878 * @context is session context
10879 * @message_id is message identifier
10880 * @sender_type is pointer to automatically allocated type of sender detected
10881 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10882 * library or to the server, NULL will be returned. Pass NULL if you don't
10883 * care about it.
10884 * @raw_sender_type is automatically reallocated UTF-8 string describing
10885 * sender type or NULL if not known to server. Pass NULL if you don't care.
10886 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10887 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10888 isds_error isds_get_message_sender(struct isds_ctx *context,
10889 const char *message_id, isds_sender_type **sender_type,
10890 char **raw_sender_type, char **sender_name) {
10891 isds_error err = IE_SUCCESS;
10892 #if HAVE_LIBCURL
10893 xmlDocPtr response = NULL;
10894 xmlChar *code = NULL, *status_message = NULL;
10895 xmlXPathContextPtr xpath_ctx = NULL;
10896 xmlXPathObjectPtr result = NULL;
10897 char *type_string = NULL;
10898 #endif
10900 if (!context) return IE_INVALID_CONTEXT;
10901 zfree(context->long_message);
10902 if (sender_type) zfree(*sender_type);
10903 if (raw_sender_type) zfree(*raw_sender_type);
10904 if (sender_name) zfree(*sender_name);
10905 if (!message_id) return IE_INVAL;
10907 #if HAVE_LIBCURL
10908 /* Do request and check for success */
10909 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10910 BAD_CAST "GetMessageAuthor",
10911 message_id, &response, NULL, NULL, &code, &status_message);
10912 if (err) goto leave;
10914 /* Extract data */
10915 xpath_ctx = xmlXPathNewContext(response);
10916 if (!xpath_ctx) {
10917 err = IE_ERROR;
10918 goto leave;
10920 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10921 err = IE_ERROR;
10922 goto leave;
10924 result = xmlXPathEvalExpression(
10925 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10926 if (!result) {
10927 err = IE_ERROR;
10928 goto leave;
10930 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10931 isds_log_message(context,
10932 _("Missing GetMessageAuthorResponse element"));
10933 err = IE_ISDS;
10934 goto leave;
10936 if (result->nodesetval->nodeNr > 1) {
10937 isds_log_message(context,
10938 _("Multiple GetMessageAuthorResponse element"));
10939 err = IE_ISDS;
10940 goto leave;
10942 xpath_ctx->node = result->nodesetval->nodeTab[0];
10943 xmlXPathFreeObject(result); result = NULL;
10945 /* Fill output arguments in */
10946 EXTRACT_STRING("isds:userType", type_string);
10947 if (NULL != type_string) {
10948 if (NULL != sender_type) {
10949 *sender_type = calloc(1, sizeof(**sender_type));
10950 if (NULL == *sender_type) {
10951 err = IE_NOMEM;
10952 goto leave;
10955 err = string2isds_sender_type((xmlChar *)type_string,
10956 *sender_type);
10957 if (err) {
10958 zfree(*sender_type);
10959 if (err == IE_ENUM) {
10960 err = IE_SUCCESS;
10961 char *type_string_locale = _isds_utf82locale(type_string);
10962 isds_log(ILF_ISDS, ILL_WARNING,
10963 _("Unknown isds:userType value: %s"),
10964 type_string_locale);
10965 free(type_string_locale);
10970 if (NULL != sender_name)
10971 EXTRACT_STRING("isds:authorName", *sender_name);
10973 leave:
10974 if (err) {
10975 if (NULL != sender_type) zfree(*sender_type);
10976 zfree(type_string);
10977 if (NULL != sender_name) zfree(*sender_name);
10979 if (NULL != raw_sender_type) *raw_sender_type = type_string;
10981 xmlXPathFreeObject(result);
10982 xmlXPathFreeContext(xpath_ctx);
10984 free(code);
10985 free(status_message);
10986 xmlFreeDoc(response);
10988 if (!err)
10989 isds_log(ILF_ISDS, ILL_DEBUG,
10990 _("GetMessageAuthor request processed by server "
10991 "successfully.\n"));
10992 #else /* not HAVE_LIBCURL */
10993 err = IE_NOTSUP;
10994 #endif
10995 return err;
10999 /* Retrieve hash of message identified by ID stored in ISDS.
11000 * @context is session context
11001 * @message_id is message identifier
11002 * @hash is automatically reallocated message hash downloaded from ISDS.
11003 * Message must exist in system and must not be deleted. */
11004 isds_error isds_download_message_hash(struct isds_ctx *context,
11005 const char *message_id, struct isds_hash **hash) {
11007 isds_error err = IE_SUCCESS;
11008 #if HAVE_LIBCURL
11009 xmlDocPtr response = NULL;
11010 xmlChar *code = NULL, *status_message = NULL;
11011 xmlXPathContextPtr xpath_ctx = NULL;
11012 xmlXPathObjectPtr result = NULL;
11013 #endif
11015 if (!context) return IE_INVALID_CONTEXT;
11016 zfree(context->long_message);
11018 isds_hash_free(hash);
11020 #if HAVE_LIBCURL
11021 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11022 BAD_CAST "VerifyMessage", message_id,
11023 &response, NULL, NULL, &code, &status_message);
11024 if (err) goto leave;
11027 /* Extract data */
11028 xpath_ctx = xmlXPathNewContext(response);
11029 if (!xpath_ctx) {
11030 err = IE_ERROR;
11031 goto leave;
11033 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11034 err = IE_ERROR;
11035 goto leave;
11037 result = xmlXPathEvalExpression(
11038 BAD_CAST "/isds:VerifyMessageResponse",
11039 xpath_ctx);
11040 if (!result) {
11041 err = IE_ERROR;
11042 goto leave;
11044 /* Empty response */
11045 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11046 char *message_id_locale = _isds_utf82locale((char*) message_id);
11047 isds_printf_message(context,
11048 _("Server did not return any response for ID `%s' "
11049 "on VerifyMessage request"), message_id_locale);
11050 free(message_id_locale);
11051 err = IE_ISDS;
11052 goto leave;
11054 /* More responses */
11055 if (result->nodesetval->nodeNr > 1) {
11056 char *message_id_locale = _isds_utf82locale((char*) message_id);
11057 isds_printf_message(context,
11058 _("Server did return more responses for ID `%s' "
11059 "on VerifyMessage request"), message_id_locale);
11060 free(message_id_locale);
11061 err = IE_ISDS;
11062 goto leave;
11064 /* One response */
11065 xpath_ctx->node = result->nodesetval->nodeTab[0];
11067 /* Extract the hash */
11068 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11070 leave:
11071 if (err) {
11072 isds_hash_free(hash);
11075 xmlXPathFreeObject(result);
11076 xmlXPathFreeContext(xpath_ctx);
11078 free(code);
11079 free(status_message);
11080 xmlFreeDoc(response);
11082 if (!err)
11083 isds_log(ILF_ISDS, ILL_DEBUG,
11084 _("VerifyMessage request processed by server "
11085 "successfully.\n")
11087 #else /* not HAVE_LIBCURL */
11088 err = IE_NOTSUP;
11089 #endif
11090 return err;
11094 /* Erase message specified by @message_id from long term storage. Other
11095 * message cannot be erased on user request.
11096 * @context is session context
11097 * @message_id is message identifier.
11098 * @incoming is true for incoming message, false for outgoing message.
11099 * @return
11100 * IE_SUCCESS if message has ben removed
11101 * IE_INVAL if message does not exist in long term storage or message
11102 * belongs to different box
11103 * TODO: IE_NOEPRM if user has no permission to erase a message */
11104 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11105 const char *message_id, _Bool incoming) {
11106 isds_error err = IE_SUCCESS;
11107 #if HAVE_LIBCURL
11108 xmlNodePtr request = NULL, node;
11109 xmlNsPtr isds_ns = NULL;
11110 xmlDocPtr response = NULL;
11111 xmlChar *code = NULL, *status_message = NULL;
11112 #endif
11114 if (!context) return IE_INVALID_CONTEXT;
11115 zfree(context->long_message);
11116 if (NULL == message_id) return IE_INVAL;
11118 /* Check if connection is established
11119 * TODO: This check should be done downstairs. */
11120 if (!context->curl) return IE_CONNECTION_CLOSED;
11122 #if HAVE_LIBCURL
11123 /* Build request */
11124 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11125 if (!request) {
11126 isds_log_message(context,
11127 _("Could build EraseMessage request"));
11128 return IE_ERROR;
11130 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11131 if(!isds_ns) {
11132 isds_log_message(context, _("Could not create ISDS name space"));
11133 xmlFreeNode(request);
11134 return IE_ERROR;
11136 xmlSetNs(request, isds_ns);
11138 err = validate_message_id_length(context, (xmlChar *) message_id);
11139 if (err) goto leave;
11140 INSERT_STRING(request, "dmID", message_id);
11142 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11145 /* Send request */
11146 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11147 "message ID %s to ISDS\n"), message_id);
11148 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11149 xmlFreeNode(request); request = NULL;
11151 if (err) {
11152 isds_log(ILF_ISDS, ILL_DEBUG,
11153 _("Processing ISDS response on EraseMessage request "
11154 "failed\n"));
11155 goto leave;
11158 /* Check for response status */
11159 err = isds_response_status(context, SERVICE_DM_INFO, response,
11160 &code, &status_message, NULL);
11161 if (err) {
11162 isds_log(ILF_ISDS, ILL_DEBUG,
11163 _("ISDS response on EraseMessage request is missing "
11164 "status\n"));
11165 goto leave;
11168 /* Check server status code */
11169 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11170 isds_log_message(context, _("Message to erase belongs to other box"));
11171 err = IE_INVAL;
11172 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11173 isds_log_message(context, _("Message to erase is not saved in "
11174 "long term storage or the direction does not match"));
11175 err = IE_INVAL;
11176 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11177 char *code_locale = _isds_utf82locale((char*) code);
11178 char *message_locale = _isds_utf82locale((char*) status_message);
11179 isds_log(ILF_ISDS, ILL_DEBUG,
11180 _("Server refused EraseMessage request "
11181 "(code=%s, message=%s)\n"),
11182 code_locale, message_locale);
11183 isds_log_message(context, message_locale);
11184 free(code_locale);
11185 free(message_locale);
11186 err = IE_ISDS;
11187 goto leave;
11190 leave:
11191 free(code);
11192 free(status_message);
11193 xmlFreeDoc(response);
11194 xmlFreeNode(request);
11196 if (!err)
11197 isds_log(ILF_ISDS, ILL_DEBUG,
11198 _("EraseMessage request processed by server "
11199 "successfully.\n")
11201 #else /* not HAVE_LIBCURL */
11202 err = IE_NOTSUP;
11203 #endif
11204 return err;
11208 /* Mark message as read. This is a transactional commit function to acknowledge
11209 * to ISDS the message has been downloaded and processed by client properly.
11210 * @context is session context
11211 * @message_id is message identifier. */
11212 isds_error isds_mark_message_read(struct isds_ctx *context,
11213 const char *message_id) {
11215 isds_error err = IE_SUCCESS;
11216 #if HAVE_LIBCURL
11217 xmlDocPtr response = NULL;
11218 xmlChar *code = NULL, *status_message = NULL;
11219 #endif
11221 if (!context) return IE_INVALID_CONTEXT;
11222 zfree(context->long_message);
11224 #if HAVE_LIBCURL
11225 /* Do request and check for success */
11226 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11227 BAD_CAST "MarkMessageAsDownloaded", message_id,
11228 &response, NULL, NULL, &code, &status_message);
11230 free(code);
11231 free(status_message);
11232 xmlFreeDoc(response);
11234 if (!err)
11235 isds_log(ILF_ISDS, ILL_DEBUG,
11236 _("MarkMessageAsDownloaded request processed by server "
11237 "successfully.\n")
11239 #else /* not HAVE_LIBCURL */
11240 err = IE_NOTSUP;
11241 #endif
11242 return err;
11246 /* Mark message as received by recipient. This is applicable only to
11247 * commercial message. Use envelope->dmType message member to distinguish
11248 * commercial message from government message. Government message is
11249 * received automatically (by law), commercial message on recipient request.
11250 * @context is session context
11251 * @message_id is message identifier. */
11252 isds_error isds_mark_message_received(struct isds_ctx *context,
11253 const char *message_id) {
11255 isds_error err = IE_SUCCESS;
11256 #if HAVE_LIBCURL
11257 xmlDocPtr response = NULL;
11258 xmlChar *code = NULL, *status_message = NULL;
11259 #endif
11261 if (!context) return IE_INVALID_CONTEXT;
11262 zfree(context->long_message);
11264 #if HAVE_LIBCURL
11265 /* Do request and check for success */
11266 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11267 BAD_CAST "ConfirmDelivery", message_id,
11268 &response, NULL, NULL, &code, &status_message);
11270 free(code);
11271 free(status_message);
11272 xmlFreeDoc(response);
11274 if (!err)
11275 isds_log(ILF_ISDS, ILL_DEBUG,
11276 _("ConfirmDelivery request processed by server "
11277 "successfully.\n")
11279 #else /* not HAVE_LIBCURL */
11280 err = IE_NOTSUP;
11281 #endif
11282 return err;
11286 /* Send document for authorized conversion into Czech POINT system.
11287 * This is public anonymous service, no log-in necessary. Special context is
11288 * used to reuse keep-a-live HTTPS connection.
11289 * @context is Czech POINT session context. DO NOT use context connected to
11290 * ISDS server. Use new context or context used by this function previously.
11291 * @document is document to convert. Only data, data_length, dmFileDescr and
11292 * is_xml members are significant. Be ware that not all document formats can be
11293 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11294 * @id is reallocated identifier assigned by Czech POINT system to
11295 * your document on submit. Use is to tell it to Czech POINT officer.
11296 * @date is reallocated document submit date (submitted documents
11297 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11298 * value. */
11299 isds_error czp_convert_document(struct isds_ctx *context,
11300 const struct isds_document *document,
11301 char **id, struct tm **date) {
11302 isds_error err = IE_SUCCESS;
11303 #if HAVE_LIBCURL
11304 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11305 xmlNodePtr request = NULL, node;
11306 xmlDocPtr response = NULL;
11308 xmlXPathContextPtr xpath_ctx = NULL;
11309 xmlXPathObjectPtr result = NULL;
11310 long int status = -1;
11311 long int *status_ptr = &status;
11312 char *string = NULL;
11313 #endif
11316 if (!context) return IE_INVALID_CONTEXT;
11317 zfree(context->long_message);
11318 if (!document || !id || !date) return IE_INVAL;
11320 if (document->is_xml) {
11321 isds_log_message(context,
11322 _("XML documents cannot be submitted to conversion"));
11323 return IE_NOTSUP;
11326 /* Free output arguments */
11327 zfree(*id);
11328 zfree(*date);
11330 #if HAVE_LIBCURL
11331 /* Store configuration */
11332 context->type = CTX_TYPE_CZP;
11333 free(context->url);
11334 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11335 if (!(context->url))
11336 return IE_NOMEM;
11338 /* Prepare CURL handle if not yet connected */
11339 if (!context->curl) {
11340 context->curl = curl_easy_init();
11341 if (!(context->curl))
11342 return IE_ERROR;
11345 /* Build conversion request */
11346 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11347 if (!request) {
11348 isds_log_message(context,
11349 _("Could not build Czech POINT conversion request"));
11350 return IE_ERROR;
11352 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11353 if(!deposit_ns) {
11354 isds_log_message(context,
11355 _("Could not create Czech POINT deposit name space"));
11356 xmlFreeNode(request);
11357 return IE_ERROR;
11359 xmlSetNs(request, deposit_ns);
11361 /* Insert children. They are in empty namespace! */
11362 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11363 if(!empty_ns) {
11364 isds_log_message(context, _("Could not create empty name space"));
11365 err = IE_ERROR;
11366 goto leave;
11368 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11369 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11370 document->dmFileDescr);
11372 /* Document encoded in Base64 */
11373 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11374 document->data, document->data_length);
11375 if (err) goto leave;
11377 isds_log(ILF_ISDS, ILL_DEBUG,
11378 _("Submitting document for conversion into Czech POINT deposit"));
11380 /* Send conversion request */
11381 err = _czp_czpdeposit(context, request, &response);
11382 xmlFreeNode(request); request = NULL;
11384 if (err) {
11385 czp_do_close_connection(context);
11386 goto leave;
11390 /* Extract response */
11391 xpath_ctx = xmlXPathNewContext(response);
11392 if (!xpath_ctx) {
11393 err = IE_ERROR;
11394 goto leave;
11396 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11397 err = IE_ERROR;
11398 goto leave;
11400 result = xmlXPathEvalExpression(
11401 BAD_CAST "/deposit:saveDocumentResponse/return",
11402 xpath_ctx);
11403 if (!result) {
11404 err = IE_ERROR;
11405 goto leave;
11407 /* Empty response */
11408 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11409 isds_printf_message(context,
11410 _("Missing `return' element in Czech POINT deposit response"));
11411 err = IE_ISDS;
11412 goto leave;
11414 /* More responses */
11415 if (result->nodesetval->nodeNr > 1) {
11416 isds_printf_message(context,
11417 _("Multiple `return' element in Czech POINT deposit response"));
11418 err = IE_ISDS;
11419 goto leave;
11421 /* One response */
11422 xpath_ctx->node = result->nodesetval->nodeTab[0];
11424 /* Get status */
11425 EXTRACT_LONGINT("status", status_ptr, 1);
11426 if (status) {
11427 EXTRACT_STRING("statusMsg", string);
11428 char *string_locale = _isds_utf82locale(string);
11429 isds_printf_message(context,
11430 _("Czech POINT deposit refused document for conversion "
11431 "(code=%ld, message=%s)"),
11432 status, string_locale);
11433 free(string_locale);
11434 err = IE_ISDS;
11435 goto leave;
11438 /* Get document ID */
11439 EXTRACT_STRING("documentID", *id);
11441 /* Get submit date */
11442 EXTRACT_STRING("dateInserted", string);
11443 if (string) {
11444 *date = calloc(1, sizeof(**date));
11445 if (!*date) {
11446 err = IE_NOMEM;
11447 goto leave;
11449 err = _isds_datestring2tm((xmlChar *)string, *date);
11450 if (err) {
11451 if (err == IE_NOTSUP) {
11452 err = IE_ISDS;
11453 char *string_locale = _isds_utf82locale(string);
11454 isds_printf_message(context,
11455 _("Invalid dateInserted value: %s"), string_locale);
11456 free(string_locale);
11458 goto leave;
11462 leave:
11463 free(string);
11464 xmlXPathFreeObject(result);
11465 xmlXPathFreeContext(xpath_ctx);
11467 xmlFreeDoc(response);
11468 xmlFreeNode(request);
11470 if (!err) {
11471 char *id_locale = _isds_utf82locale((char *) *id);
11472 isds_log(ILF_ISDS, ILL_DEBUG,
11473 _("Document %s has been submitted for conversion "
11474 "to server successfully\n"), id_locale);
11475 free(id_locale);
11477 #else /* not HAVE_LIBCURL */
11478 err = IE_NOTSUP;
11479 #endif
11480 return err;
11484 /* Close possibly opened connection to Czech POINT document deposit.
11485 * @context is Czech POINT session context. */
11486 isds_error czp_close_connection(struct isds_ctx *context) {
11487 if (!context) return IE_INVALID_CONTEXT;
11488 zfree(context->long_message);
11489 #if HAVE_LIBCURL
11490 return czp_do_close_connection(context);
11491 #else
11492 return IE_NOTSUP;
11493 #endif
11497 /* Send request for new box creation in testing ISDS instance.
11498 * It's not possible to request for a production box currently, as it
11499 * communicates via e-mail.
11500 * XXX: This function does not work either. Server complains about invalid
11501 * e-mail address.
11502 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11503 * this function
11504 * @context is special session context for box creation request. DO NOT use
11505 * standard context as it could reveal your password. Use fresh new context or
11506 * context previously used by this function.
11507 * @box is box description to create including single primary user (in case of
11508 * FO box type). It outputs box ID assigned by ISDS in dbID element.
11509 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11510 * box, or contact address of PFO box owner). The email member is mandatory as
11511 * it will be used to deliver credentials.
11512 * @former_names is former name of box owner. Pass NULL if you don't care.
11513 * @approval is optional external approval of box manipulation
11514 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11515 * NULL, if you don't care.*/
11516 isds_error isds_request_new_testing_box(struct isds_ctx *context,
11517 struct isds_DbOwnerInfo *box, const struct isds_list *users,
11518 const char *former_names, const struct isds_approval *approval,
11519 char **refnumber) {
11520 isds_error err = IE_SUCCESS;
11521 #if HAVE_LIBCURL
11522 xmlNodePtr request = NULL;
11523 xmlDocPtr response = NULL;
11524 xmlXPathContextPtr xpath_ctx = NULL;
11525 xmlXPathObjectPtr result = NULL;
11526 #endif
11529 if (!context) return IE_INVALID_CONTEXT;
11530 zfree(context->long_message);
11531 if (!box) return IE_INVAL;
11533 #if HAVE_LIBCURL
11534 if (!box->email || box->email[0] == '\0') {
11535 isds_log_message(context, _("E-mail field is mandatory"));
11536 return IE_INVAL;
11539 /* Scratch box ID */
11540 zfree(box->dbID);
11542 /* Store configuration */
11543 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
11544 free(context->url);
11545 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
11546 if (!(context->url))
11547 return IE_NOMEM;
11549 /* Prepare CURL handle if not yet connected */
11550 if (!context->curl) {
11551 context->curl = curl_easy_init();
11552 if (!(context->curl))
11553 return IE_ERROR;
11556 /* Build CreateDataBox request */
11557 err = build_CreateDBInput_request(context,
11558 &request, BAD_CAST "CreateDataBox",
11559 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
11560 if (err) goto leave;
11562 /* Send it to server and process response */
11563 err = send_destroy_request_check_response(context,
11564 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
11565 &response, (xmlChar **) refnumber, NULL);
11566 if (err) goto leave;
11568 /* Extract box ID */
11569 xpath_ctx = xmlXPathNewContext(response);
11570 if (!xpath_ctx) {
11571 err = IE_ERROR;
11572 goto leave;
11574 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11575 err = IE_ERROR;
11576 goto leave;
11578 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
11580 leave:
11581 xmlXPathFreeObject(result);
11582 xmlXPathFreeContext(xpath_ctx);
11583 xmlFreeDoc(response);
11584 xmlFreeNode(request);
11586 if (!err) {
11587 isds_log(ILF_ISDS, ILL_DEBUG,
11588 _("CreateDataBox request processed by server successfully.\n"));
11590 #else /* not HAVE_LIBCURL */
11591 err = IE_NOTSUP;
11592 #endif
11594 return err;
11598 /* Submit CMS signed message to ISDS to verify its originality. This is
11599 * stronger form of isds_verify_message_hash() because ISDS does more checks
11600 * than simple one (potentialy old weak) hash comparison.
11601 * @context is session context
11602 * @message is memory with raw CMS signed message bit stream
11603 * @length is @message size in bytes
11604 * @return
11605 * IE_SUCCESS if message originates in ISDS
11606 * IE_NOTEQUAL if message is unknown to ISDS
11607 * other code for other errors */
11608 isds_error isds_authenticate_message(struct isds_ctx *context,
11609 const void *message, size_t length) {
11610 isds_error err = IE_SUCCESS;
11611 #if HAVE_LIBCURL
11612 xmlNsPtr isds_ns = NULL;
11613 xmlNodePtr request = NULL;
11614 xmlDocPtr response = NULL;
11615 xmlXPathContextPtr xpath_ctx = NULL;
11616 xmlXPathObjectPtr result = NULL;
11617 _Bool *authentic = NULL;
11618 #endif
11620 if (!context) return IE_INVALID_CONTEXT;
11621 zfree(context->long_message);
11622 if (!message || length == 0) return IE_INVAL;
11624 #if HAVE_LIBCURL
11625 /* Check if connection is established
11626 * TODO: This check should be done downstairs. */
11627 if (!context->curl) return IE_CONNECTION_CLOSED;
11630 /* Build AuthenticateMessage request */
11631 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11632 if (!request) {
11633 isds_log_message(context,
11634 _("Could not build AuthenticateMessage request"));
11635 return IE_ERROR;
11637 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11638 if(!isds_ns) {
11639 isds_log_message(context, _("Could not create ISDS name space"));
11640 xmlFreeNode(request);
11641 return IE_ERROR;
11643 xmlSetNs(request, isds_ns);
11645 /* Insert Base64 encoded message */
11646 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11647 message, length);
11648 if (err) goto leave;
11650 /* Send request to server and process response */
11651 err = send_destroy_request_check_response(context,
11652 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11653 &response, NULL, NULL);
11654 if (err) goto leave;
11657 /* ISDS has decided */
11658 xpath_ctx = xmlXPathNewContext(response);
11659 if (!xpath_ctx) {
11660 err = IE_ERROR;
11661 goto leave;
11663 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11664 err = IE_ERROR;
11665 goto leave;
11668 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11670 if (!authentic) {
11671 isds_log_message(context,
11672 _("Server did not return any response on "
11673 "AuthenticateMessage request"));
11674 err = IE_ISDS;
11675 goto leave;
11677 if (*authentic) {
11678 isds_log(ILF_ISDS, ILL_DEBUG,
11679 _("ISDS authenticated the message successfully\n"));
11680 } else {
11681 isds_log_message(context, _("ISDS does not know the message"));
11682 err = IE_NOTEQUAL;
11686 leave:
11687 free(authentic);
11688 xmlXPathFreeObject(result);
11689 xmlXPathFreeContext(xpath_ctx);
11691 xmlFreeDoc(response);
11692 xmlFreeNode(request);
11693 #else /* not HAVE_LIBCURL */
11694 err = IE_NOTSUP;
11695 #endif
11697 return err;
11701 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11702 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11703 * be re-signed.
11704 * @context is session context
11705 * @input_data is memory with raw CMS signed message or delivery info bit
11706 * stream to re-sign
11707 * @input_length is @input_data size in bytes
11708 * @output_data is pointer to auto-allocated memory where to store re-signed
11709 * input data blob. Caller must free it.
11710 * @output_data is pointer where to store @output_data size in bytes
11711 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11712 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11713 * @return
11714 * IE_SUCCESS if CMS blob has been re-signed successfully
11715 * other code for other errors */
11716 isds_error isds_resign_message(struct isds_ctx *context,
11717 const void *input_data, size_t input_length,
11718 void **output_data, size_t *output_length, struct tm **valid_to) {
11719 isds_error err = IE_SUCCESS;
11720 #if HAVE_LIBCURL
11721 xmlNsPtr isds_ns = NULL;
11722 xmlNodePtr request = NULL;
11723 xmlDocPtr response = NULL;
11724 xmlXPathContextPtr xpath_ctx = NULL;
11725 xmlXPathObjectPtr result = NULL;
11726 char *string = NULL;
11727 const xmlChar *codes[] = {
11728 BAD_CAST "2200",
11729 BAD_CAST "2201",
11730 BAD_CAST "2204",
11731 BAD_CAST "2207",
11732 NULL
11734 const char *meanings[] = {
11735 "Message is bad",
11736 "Message is not original",
11737 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11738 "Time stamp could not been generated in time"
11740 const isds_error errors[] = {
11741 IE_INVAL,
11742 IE_NOTUNIQ,
11743 IE_INVAL,
11744 IE_ISDS,
11746 struct code_map_isds_error map = {
11747 .codes = codes,
11748 .meanings = meanings,
11749 .errors = errors
11751 #endif
11753 if (NULL != output_data) *output_data = NULL;
11754 if (NULL != output_length) *output_length = 0;
11755 if (NULL != valid_to) *valid_to = NULL;
11757 if (NULL == context) return IE_INVALID_CONTEXT;
11758 zfree(context->long_message);
11759 if (NULL == input_data || 0 == input_length) {
11760 isds_log_message(context, _("Empty CMS blob on input"));
11761 return IE_INVAL;
11763 if (NULL == output_data || NULL == output_length) {
11764 isds_log_message(context,
11765 _("NULL pointer provided for output CMS blob"));
11766 return IE_INVAL;
11769 #if HAVE_LIBCURL
11770 /* Check if connection is established
11771 * TODO: This check should be done downstairs. */
11772 if (!context->curl) return IE_CONNECTION_CLOSED;
11775 /* Build Re-signISDSDocument request */
11776 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11777 if (!request) {
11778 isds_log_message(context,
11779 _("Could not build Re-signISDSDocument request"));
11780 return IE_ERROR;
11782 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11783 if(!isds_ns) {
11784 isds_log_message(context, _("Could not create ISDS name space"));
11785 xmlFreeNode(request);
11786 return IE_ERROR;
11788 xmlSetNs(request, isds_ns);
11790 /* Insert Base64 encoded CMS blob */
11791 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11792 input_data, input_length);
11793 if (err) goto leave;
11795 /* Send request to server and process response */
11796 err = send_destroy_request_check_response(context,
11797 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11798 &response, NULL, &map);
11799 if (err) goto leave;
11802 /* Extract re-signed data */
11803 xpath_ctx = xmlXPathNewContext(response);
11804 if (!xpath_ctx) {
11805 err = IE_ERROR;
11806 goto leave;
11808 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11809 err = IE_ERROR;
11810 goto leave;
11812 result = xmlXPathEvalExpression(
11813 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11814 if (!result) {
11815 err = IE_ERROR;
11816 goto leave;
11818 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11819 isds_log_message(context,
11820 _("Missing Re-signISDSDocumentResponse element"));
11821 err = IE_ISDS;
11822 goto leave;
11824 if (result->nodesetval->nodeNr > 1) {
11825 isds_log_message(context,
11826 _("Multiple Re-signISDSDocumentResponse element"));
11827 err = IE_ISDS;
11828 goto leave;
11830 xpath_ctx->node = result->nodesetval->nodeTab[0];
11831 xmlXPathFreeObject(result); result = NULL;
11833 EXTRACT_STRING("isds:dmResultDoc", string);
11834 /* Decode non-empty data */
11835 if (NULL != string && string[0] != '\0') {
11836 *output_length = _isds_b64decode(string, output_data);
11837 if (*output_length == (size_t) -1) {
11838 isds_log_message(context,
11839 _("Error while Base64-decoding re-signed data"));
11840 err = IE_ERROR;
11841 goto leave;
11843 } else {
11844 isds_log_message(context, _("Server did not send re-signed data"));
11845 err = IE_ISDS;
11846 goto leave;
11848 zfree(string);
11850 if (NULL != valid_to) {
11851 /* Get time stamp expiration date */
11852 EXTRACT_STRING("isds:dmValidTo", string);
11853 if (NULL != string) {
11854 *valid_to = calloc(1, sizeof(**valid_to));
11855 if (!*valid_to) {
11856 err = IE_NOMEM;
11857 goto leave;
11859 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11860 if (err) {
11861 if (err == IE_NOTSUP) {
11862 err = IE_ISDS;
11863 char *string_locale = _isds_utf82locale(string);
11864 isds_printf_message(context,
11865 _("Invalid dmValidTo value: %s"), string_locale);
11866 free(string_locale);
11868 goto leave;
11873 leave:
11874 free(string);
11876 xmlXPathFreeObject(result);
11877 xmlXPathFreeContext(xpath_ctx);
11879 xmlFreeDoc(response);
11880 xmlFreeNode(request);
11881 #else /* not HAVE_LIBCURL */
11882 err = IE_NOTSUP;
11883 #endif
11885 return err;
11888 #undef INSERT_ELEMENT
11889 #undef CHECK_FOR_STRING_LENGTH
11890 #undef INSERT_STRING_ATTRIBUTE
11891 #undef INSERT_ULONGINTNOPTR
11892 #undef INSERT_ULONGINT
11893 #undef INSERT_LONGINT
11894 #undef INSERT_BOOLEAN
11895 #undef INSERT_SCALAR_BOOLEAN
11896 #undef INSERT_STRING
11897 #undef INSERT_STRING_WITH_NS
11898 #undef EXTRACT_STRING_ATTRIBUTE
11899 #undef EXTRACT_ULONGINT
11900 #undef EXTRACT_LONGINT
11901 #undef EXTRACT_BOOLEAN
11902 #undef EXTRACT_STRING
11905 /* Compute hash of message from raw representation and store it into envelope.
11906 * Original hash structure will be destroyed in envelope.
11907 * @context is session context
11908 * @message is message carrying raw XML message blob
11909 * @algorithm is desired hash algorithm to use */
11910 isds_error isds_compute_message_hash(struct isds_ctx *context,
11911 struct isds_message *message, const isds_hash_algorithm algorithm) {
11912 isds_error err = IE_SUCCESS;
11913 const char *nsuri;
11914 void *xml_stream = NULL;
11915 size_t xml_stream_length;
11916 size_t phys_start, phys_end;
11917 char *phys_path = NULL;
11918 struct isds_hash *new_hash = NULL;
11921 if (!context) return IE_INVALID_CONTEXT;
11922 zfree(context->long_message);
11923 if (!message) return IE_INVAL;
11925 if (!message->raw) {
11926 isds_log_message(context,
11927 _("Message does not carry raw representation"));
11928 return IE_INVAL;
11931 switch (message->raw_type) {
11932 case RAWTYPE_INCOMING_MESSAGE:
11933 nsuri = ISDS_NS;
11934 xml_stream = message->raw;
11935 xml_stream_length = message->raw_length;
11936 break;
11938 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11939 nsuri = SISDS_INCOMING_NS;
11940 xml_stream = message->raw;
11941 xml_stream_length = message->raw_length;
11942 break;
11944 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11945 nsuri = SISDS_INCOMING_NS;
11946 err = _isds_extract_cms_data(context,
11947 message->raw, message->raw_length,
11948 &xml_stream, &xml_stream_length);
11949 if (err) goto leave;
11950 break;
11952 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11953 nsuri = SISDS_OUTGOING_NS;
11954 xml_stream = message->raw;
11955 xml_stream_length = message->raw_length;
11956 break;
11958 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11959 nsuri = SISDS_OUTGOING_NS;
11960 err = _isds_extract_cms_data(context,
11961 message->raw, message->raw_length,
11962 &xml_stream, &xml_stream_length);
11963 if (err) goto leave;
11964 break;
11966 default:
11967 isds_log_message(context, _("Bad raw representation type"));
11968 return IE_INVAL;
11969 break;
11973 /* XXX: Hash is computed from original string representing isds:dmDm
11974 * subtree. That means no encoding, white space, xmlns attributes changes.
11975 * In other words, input for hash can be invalid XML stream. */
11976 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
11977 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11978 PHYSXML_ELEMENT_SEPARATOR,
11979 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
11980 PHYSXML_ELEMENT_SEPARATOR
11981 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
11982 err = IE_NOMEM;
11983 goto leave;
11985 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11986 phys_path, &phys_start, &phys_end);
11987 zfree(phys_path);
11988 if (err) {
11989 isds_log_message(context,
11990 _("Substring with isds:dmDM element could not be located "
11991 "in raw message"));
11992 goto leave;
11996 /* Compute hash */
11997 new_hash = calloc(1, sizeof(*new_hash));
11998 if (!new_hash) {
11999 err = IE_NOMEM;
12000 goto leave;
12002 new_hash->algorithm = algorithm;
12003 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
12004 new_hash);
12005 if (err) {
12006 isds_log_message(context, _("Could not compute message hash"));
12007 goto leave;
12010 /* Save computed hash */
12011 if (!message->envelope) {
12012 message->envelope = calloc(1, sizeof(*message->envelope));
12013 if (!message->envelope) {
12014 err = IE_NOMEM;
12015 goto leave;
12018 isds_hash_free(&message->envelope->hash);
12019 message->envelope->hash = new_hash;
12021 leave:
12022 if (err) {
12023 isds_hash_free(&new_hash);
12026 free(phys_path);
12027 if (xml_stream != message->raw) free(xml_stream);
12028 return err;
12032 /* Compare two hashes.
12033 * @h1 is first hash
12034 * @h2 is another hash
12035 * @return
12036 * IE_SUCCESS if hashes equal
12037 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12038 * IE_ENUM if not comparable, but both structures defined
12039 * IE_INVAL if some of the structures are undefined (NULL)
12040 * IE_ERROR if internal error occurs */
12041 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
12042 if (h1 == NULL || h2 == NULL) return IE_INVAL;
12043 if (h1->algorithm != h2->algorithm) return IE_ENUM;
12044 if (h1->length != h2->length) return IE_ERROR;
12045 if (h1->length > 0 && !h1->value) return IE_ERROR;
12046 if (h2->length > 0 && !h2->value) return IE_ERROR;
12048 for (size_t i = 0; i < h1->length; i++) {
12049 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12050 return IE_NOTEQUAL;
12052 return IE_SUCCESS;
12056 /* Check message has gone through ISDS by comparing message hash stored in
12057 * ISDS and locally computed hash. You must provide message with valid raw
12058 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12059 * This is convenient wrapper for isds_download_message_hash(),
12060 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12061 * @context is session context
12062 * @message is message with valid raw and envelope member; envelope->hash
12063 * member will be changed during function run. Use envelope on heap only.
12064 * @return
12065 * IE_SUCCESS if message originates in ISDS
12066 * IE_NOTEQUAL if message is unknown to ISDS
12067 * other code for other errors */
12068 isds_error isds_verify_message_hash(struct isds_ctx *context,
12069 struct isds_message *message) {
12070 isds_error err = IE_SUCCESS;
12071 struct isds_hash *downloaded_hash = NULL;
12073 if (!context) return IE_INVALID_CONTEXT;
12074 zfree(context->long_message);
12075 if (!message) return IE_INVAL;
12077 if (!message->envelope) {
12078 isds_log_message(context,
12079 _("Given message structure is missing envelope"));
12080 return IE_INVAL;
12082 if (!message->raw) {
12083 isds_log_message(context,
12084 _("Given message structure is missing raw representation"));
12085 return IE_INVAL;
12088 err = isds_download_message_hash(context, message->envelope->dmID,
12089 &downloaded_hash);
12090 if (err) goto leave;
12092 err = isds_compute_message_hash(context, message,
12093 downloaded_hash->algorithm);
12094 if (err) goto leave;
12096 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12098 leave:
12099 isds_hash_free(&downloaded_hash);
12100 return err;
12104 /* Search for document by document ID in list of documents. IDs are compared
12105 * as UTF-8 string.
12106 * @documents is list of isds_documents
12107 * @id is document identifier
12108 * @return first matching document or NULL. */
12109 const struct isds_document *isds_find_document_by_id(
12110 const struct isds_list *documents, const char *id) {
12111 const struct isds_list *item;
12112 const struct isds_document *document;
12114 for (item = documents; item; item = item->next) {
12115 document = (struct isds_document *) item->data;
12116 if (!document) continue;
12118 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12119 return document;
12122 return NULL;
12126 /* Normalize @mime_type to be proper MIME type.
12127 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12128 * guess regular MIME type (e.g. "application/pdf").
12129 * @mime_type is UTF-8 encoded MIME type to fix
12130 * @return original @mime_type if no better interpretation exists, or
12131 * constant static UTF-8 encoded string with proper MIME type. */
12132 const char *isds_normalize_mime_type(const char *mime_type) {
12133 if (!mime_type) return NULL;
12135 for (size_t offset = 0;
12136 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12137 offset += 2) {
12138 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12139 extension_map_mime[offset]))
12140 return (const char *) extension_map_mime[offset + 1];
12143 return mime_type;
12147 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12148 struct isds_message **message);
12149 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12150 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12151 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12152 struct isds_address **address);
12154 int isds_message_free(struct isds_message **message);
12155 int isds_address_free(struct isds_address **address);
12159 /* Makes known all relevant namespaces to given XPath context
12160 * @xpath_ctx is XPath context
12161 * @message_ns selects proper message name space. Unsigned and signed
12162 * messages and delivery info's differ in prefix and URI. */
12163 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12164 const message_ns_type message_ns) {
12165 const xmlChar *message_namespace = NULL;
12167 if (!xpath_ctx) return IE_ERROR;
12169 switch(message_ns) {
12170 case MESSAGE_NS_1:
12171 message_namespace = BAD_CAST ISDS1_NS; break;
12172 case MESSAGE_NS_UNSIGNED:
12173 message_namespace = BAD_CAST ISDS_NS; break;
12174 case MESSAGE_NS_SIGNED_INCOMING:
12175 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12176 case MESSAGE_NS_SIGNED_OUTGOING:
12177 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12178 case MESSAGE_NS_SIGNED_DELIVERY:
12179 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12180 default:
12181 return IE_ENUM;
12184 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12185 return IE_ERROR;
12186 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12187 return IE_ERROR;
12188 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12189 return IE_ERROR;
12190 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12191 return IE_ERROR;
12192 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12193 return IE_ERROR;
12194 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12195 return IE_ERROR;
12196 return IE_SUCCESS;