Add AIFOTicket to isds_DbUserInfo as aifo_ticket
[libisds.git] / src / isds.c
blob06f7e3db10a2cd7f2a08b11f2c5d02a8220934c2
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 is invalid value from point of view of public
2012 * 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. Pass NULL to let server to use default value
7585 * which is DBTYPE_SYSTEM.
7586 * @page_size defines count of boxes to constitute a response page. It counts
7587 * from zero. Pass NULL to let server to use a default value (50 now).
7588 * @page_number defines ordinar number of the response page to return. It
7589 * counts from zero. Pass NULL to let server to use a default value (0 now).
7590 * @track_matches points to true for marking @query words found in the box
7591 * attributes. It points to false for not marking. Pass NULL to let the server
7592 * to use default value (false now).
7593 * @total_matching_boxes outputs reallocated number of all boxes matching the
7594 * query. Will be pointer to NULL if server did not provide the value.
7595 * Pass NULL if you don't care.
7596 * @current_page_beginning outputs reallocated ordinar number of the first box
7597 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7598 * server did not provide the value. Pass NULL if you don't care.
7599 * @current_page_size outputs reallocated count of boxes in the this @boxes
7600 * page. It will be pointer to NULL if the server did not provide the value.
7601 * Pass NULL if you don't care.
7602 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7603 * is the last one, false if more boxes match, NULL if the server did not
7604 * provude the value. Pass NULL if you don't care.
7605 * @boxes outputs reallocated list of isds_fulltext_result structures,
7606 * possibly empty.
7607 * @return:
7608 * IE_SUCCESS if search succeeded
7609 * IE_2BIG if @page_size is too large
7610 * other code if something bad happens; output arguments will be NULL. */
7611 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7612 const char *query,
7613 const isds_fulltext_target *target,
7614 const isds_DbType *box_type,
7615 const unsigned long int *page_size,
7616 const unsigned long int *page_number,
7617 const _Bool *track_matches,
7618 unsigned long int **total_matching_boxes,
7619 unsigned long int **current_page_beginning,
7620 unsigned long int **current_page_size,
7621 _Bool **last_page,
7622 struct isds_list **boxes) {
7623 isds_error err = IE_SUCCESS;
7624 #if HAVE_LIBCURL
7625 xmlNsPtr isds_ns = NULL;
7626 xmlNodePtr request = NULL;
7627 xmlDocPtr response = NULL;
7628 xmlNodePtr node;
7629 xmlXPathContextPtr xpath_ctx = NULL;
7630 xmlXPathObjectPtr result = NULL;
7631 const xmlChar *static_string = NULL;
7632 xmlChar *string = NULL;
7634 const xmlChar *codes[] = {
7635 BAD_CAST "1004",
7636 BAD_CAST "1152",
7637 BAD_CAST "1153",
7638 BAD_CAST "1154",
7639 BAD_CAST "1155",
7640 BAD_CAST "1156",
7641 BAD_CAST "9002",
7642 NULL
7644 const char *meanings[] = {
7645 N_("You are not allowed to perform the search"),
7646 N_("The query string is empty"),
7647 N_("Searched box ID is malformed"),
7648 N_("Searched organization ID is malformed"),
7649 N_("Invalid input"),
7650 N_("Requested page size is too large"),
7651 N_("Search engine internal error")
7653 const isds_error errors[] = {
7654 IE_ISDS,
7655 IE_INVAL,
7656 IE_INVAL,
7657 IE_INVAL,
7658 IE_INVAL,
7659 IE_2BIG,
7660 IE_ISDS
7662 struct code_map_isds_error map = {
7663 .codes = codes,
7664 .meanings = meanings,
7665 .errors = errors
7667 #endif
7670 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7671 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7672 if (NULL != current_page_size) zfree(*current_page_size);
7673 if (NULL != last_page) zfree(*last_page);
7674 isds_list_free(boxes);
7676 if (NULL == context) return IE_INVALID_CONTEXT;
7677 zfree(context->long_message);
7679 if (NULL == boxes) return IE_INVAL;
7681 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
7682 isds_log_message(context, _("Query string must be non-empty"));
7683 return IE_INVAL;
7686 #if HAVE_LIBCURL
7687 /* Check if connection is established
7688 * TODO: This check should be done downstairs. */
7689 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7691 /* Build FindDataBox request */
7692 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
7693 if (NULL == request) {
7694 isds_log_message(context,
7695 _("Could not build ISDSSearch2 request"));
7696 return IE_ERROR;
7698 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7699 if(NULL == isds_ns) {
7700 isds_log_message(context, _("Could not create ISDS name space"));
7701 xmlFreeNode(request);
7702 return IE_ERROR;
7704 xmlSetNs(request, isds_ns);
7706 INSERT_STRING(request, "searchText", query);
7708 if (NULL != target) {
7709 static_string = isds_fulltext_target2string(*(target));
7710 if (NULL == static_string) {
7711 isds_printf_message(context, _("Invalid target value: %d"),
7712 *(target));
7713 err = IE_ENUM;
7714 goto leave;
7717 INSERT_STRING(request, "searchType", static_string);
7718 static_string = NULL;
7720 if (NULL != box_type) {
7721 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7722 if (DBTYPE_SYSTEM == *box_type) {
7723 static_string = BAD_CAST "ALL";
7724 } else {
7725 static_string = isds_DbType2string(*(box_type));
7726 if (NULL == static_string) {
7727 isds_printf_message(context, _("Invalid box type value: %d"),
7728 *(box_type));
7729 err = IE_ENUM;
7730 goto leave;
7734 INSERT_STRING(request, "searchScope", static_string);
7735 static_string = NULL;
7737 INSERT_ULONGINT(request, "page", page_number, string);
7738 INSERT_ULONGINT(request, "pageSize", page_size, string);
7739 INSERT_BOOLEAN(request, "highlighting", track_matches);
7741 /* Send request and check response */
7742 err = send_destroy_request_check_response(context,
7743 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
7744 &request, &response, NULL, &map);
7745 if (err) goto leave;
7747 /* Parse response */
7748 xpath_ctx = xmlXPathNewContext(response);
7749 if (NULL == xpath_ctx) {
7750 err = IE_ERROR;
7751 goto leave;
7753 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7754 err = IE_ERROR;
7755 goto leave;
7757 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
7758 xpath_ctx);
7759 if (!result) {
7760 err = IE_ERROR;
7761 goto leave;
7763 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7764 isds_log_message(context, _("Missing ISDSSearch2 element"));
7765 err = IE_ISDS;
7766 goto leave;
7768 if (result->nodesetval->nodeNr > 1) {
7769 isds_log_message(context, _("Multiple ISDSSearch2 element"));
7770 err = IE_ISDS;
7771 goto leave;
7773 xpath_ctx->node = result->nodesetval->nodeTab[0];
7774 xmlXPathFreeObject(result); result = NULL;
7777 /* Extract counters */
7778 if (NULL != total_matching_boxes) {
7779 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
7781 if (NULL != current_page_size) {
7782 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
7784 if (NULL != current_page_beginning) {
7785 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
7787 if (NULL != last_page) {
7788 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
7790 xmlXPathFreeObject(result); result = NULL;
7792 /* Extract boxes if they present */
7793 result = xmlXPathEvalExpression(BAD_CAST
7794 "isds:dbResults/isds:dbResult", xpath_ctx);
7795 if (NULL == result) {
7796 err = IE_ERROR;
7797 goto leave;
7799 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7800 struct isds_list *item, *prev_item = NULL;
7801 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7802 item = calloc(1, sizeof(*item));
7803 if (!item) {
7804 err = IE_NOMEM;
7805 goto leave;
7808 item->destructor = (void (*)(void **))isds_fulltext_result_free;
7809 if (i == 0) *boxes = item;
7810 else prev_item->next = item;
7811 prev_item = item;
7813 xpath_ctx->node = result->nodesetval->nodeTab[i];
7814 err = extract_dbResult(context,
7815 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
7816 (NULL == track_matches) ? 0 : *track_matches);
7817 if (err) goto leave;
7821 leave:
7822 if (err) {
7823 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7824 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7825 if (NULL != current_page_size) zfree(*current_page_size);
7826 if (NULL != last_page) zfree(*last_page);
7827 isds_list_free(boxes);
7830 free(string);
7831 xmlFreeNode(request);
7832 xmlXPathFreeObject(result);
7833 xmlXPathFreeContext(xpath_ctx);
7834 xmlFreeDoc(response);
7836 if (!err)
7837 isds_log(ILF_ISDS, ILL_DEBUG,
7838 _("ISDSSearch2 request processed by server successfully.\n"));
7839 #else /* not HAVE_LIBCURL */
7840 err = IE_NOTSUP;
7841 #endif
7843 return err;
7847 /* Get status of a box.
7848 * @context is ISDS session context.
7849 * @box_id is UTF-8 encoded box identifier as zero terminated string
7850 * @box_status is return value of box status.
7851 * @return:
7852 * IE_SUCCESS if box has been found and its status retrieved
7853 * IE_NOEXIST if box is not known to ISDS server
7854 * or other appropriate error.
7855 * You can use isds_DbState to enumerate box status. However out of enum
7856 * range value can be returned too. This is feature because ISDS
7857 * specification leaves the set of values open.
7858 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7859 * the box has been deleted, but ISDS still lists its former existence. */
7860 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7861 long int *box_status) {
7862 isds_error err = IE_SUCCESS;
7863 #if HAVE_LIBCURL
7864 xmlNsPtr isds_ns = NULL;
7865 xmlNodePtr request = NULL, db_id;
7866 xmlDocPtr response = NULL;
7867 xmlXPathContextPtr xpath_ctx = NULL;
7868 xmlXPathObjectPtr result = NULL;
7869 xmlChar *string = NULL;
7871 const xmlChar *codes[] = {
7872 BAD_CAST "5001",
7873 BAD_CAST "1007",
7874 BAD_CAST "2011",
7875 NULL
7877 const char *meanings[] = {
7878 "The box does not exist",
7879 "Box ID is malformed",
7880 "Box ID malformed",
7882 const isds_error errors[] = {
7883 IE_NOEXIST,
7884 IE_INVAL,
7885 IE_INVAL,
7887 struct code_map_isds_error map = {
7888 .codes = codes,
7889 .meanings = meanings,
7890 .errors = errors
7892 #endif
7894 if (!context) return IE_INVALID_CONTEXT;
7895 zfree(context->long_message);
7896 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7898 #if HAVE_LIBCURL
7899 /* Check if connection is established
7900 * TODO: This check should be done downstairs. */
7901 if (!context->curl) return IE_CONNECTION_CLOSED;
7904 /* Build CheckDataBox request */
7905 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7906 if (!request) {
7907 isds_log_message(context,
7908 _("Could build CheckDataBox request"));
7909 return IE_ERROR;
7911 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7912 if(!isds_ns) {
7913 isds_log_message(context, _("Could not create ISDS name space"));
7914 xmlFreeNode(request);
7915 return IE_ERROR;
7917 xmlSetNs(request, isds_ns);
7918 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7919 if (!db_id) {
7920 isds_log_message(context, _("Could not add dbID child to "
7921 "CheckDataBox element"));
7922 xmlFreeNode(request);
7923 return IE_ERROR;
7927 /* Send request and check response*/
7928 err = send_destroy_request_check_response(context,
7929 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7930 &request, &response, NULL, &map);
7931 if (err) goto leave;
7934 /* Extract data */
7935 xpath_ctx = xmlXPathNewContext(response);
7936 if (!xpath_ctx) {
7937 err = IE_ERROR;
7938 goto leave;
7940 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7941 err = IE_ERROR;
7942 goto leave;
7944 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7945 xpath_ctx);
7946 if (!result) {
7947 err = IE_ERROR;
7948 goto leave;
7950 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7951 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7952 err = IE_ISDS;
7953 goto leave;
7955 if (result->nodesetval->nodeNr > 1) {
7956 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7957 err = IE_ISDS;
7958 goto leave;
7960 xpath_ctx->node = result->nodesetval->nodeTab[0];
7961 xmlXPathFreeObject(result); result = NULL;
7963 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7966 leave:
7967 free(string);
7968 xmlXPathFreeObject(result);
7969 xmlXPathFreeContext(xpath_ctx);
7971 xmlFreeDoc(response);
7973 if (!err)
7974 isds_log(ILF_ISDS, ILL_DEBUG,
7975 _("CheckDataBox request processed by server successfully.\n"));
7976 #else /* not HAVE_LIBCURL */
7977 err = IE_NOTSUP;
7978 #endif
7980 return err;
7984 /* Get list of permissions to send commercial messages.
7985 * @context is ISDS session context.
7986 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7987 * @permissions is a reallocated list of permissions (struct
7988 * isds_commercial_permission*) to send commercial messages from @box_id. The
7989 * order of permissions is significant as the server applies the permissions
7990 * and associated pre-paid credits in the order. Empty list means no
7991 * permission.
7992 * @return:
7993 * IE_SUCCESS if the list has been obtained correctly,
7994 * or other appropriate error. */
7995 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7996 const char *box_id, struct isds_list **permissions) {
7997 isds_error err = IE_SUCCESS;
7998 #if HAVE_LIBCURL
7999 xmlDocPtr response = NULL;
8000 xmlXPathContextPtr xpath_ctx = NULL;
8001 xmlXPathObjectPtr result = NULL;
8002 #endif
8004 if (!context) return IE_INVALID_CONTEXT;
8005 zfree(context->long_message);
8006 if (NULL == permissions) return IE_INVAL;
8007 isds_list_free(permissions);
8008 if (NULL == box_id) return IE_INVAL;
8010 #if HAVE_LIBCURL
8011 /* Check if connection is established */
8012 if (!context->curl) return IE_CONNECTION_CLOSED;
8014 /* Do request and check for success */
8015 err = build_send_dbid_request_check_response(context,
8016 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
8017 BAD_CAST box_id, NULL, &response, NULL);
8018 if (!err) {
8019 isds_log(ILF_ISDS, ILL_DEBUG,
8020 _("PDZInfo request processed by server successfully.\n"));
8023 /* Extract data */
8024 /* Prepare structure */
8025 xpath_ctx = xmlXPathNewContext(response);
8026 if (!xpath_ctx) {
8027 err = IE_ERROR;
8028 goto leave;
8030 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8031 err = IE_ERROR;
8032 goto leave;
8035 /* Set context node */
8036 result = xmlXPathEvalExpression(BAD_CAST
8037 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8038 xpath_ctx);
8039 if (!result) {
8040 err = IE_ERROR;
8041 goto leave;
8043 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8044 struct isds_list *prev_item = NULL;
8046 /* Iterate over all permission records */
8047 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8048 struct isds_list *item;
8050 /* Prepare structure */
8051 item = calloc(1, sizeof(*item));
8052 if (!item) {
8053 err = IE_NOMEM;
8054 goto leave;
8056 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8057 if (i == 0) *permissions = item;
8058 else prev_item->next = item;
8059 prev_item = item;
8061 /* Extract it */
8062 xpath_ctx->node = result->nodesetval->nodeTab[i];
8063 err = extract_DbPDZRecord(context,
8064 (struct isds_commercial_permission **) (&item->data),
8065 xpath_ctx);
8066 if (err) goto leave;
8070 leave:
8071 if (err) {
8072 isds_list_free(permissions);
8075 xmlXPathFreeObject(result);
8076 xmlXPathFreeContext(xpath_ctx);
8077 xmlFreeDoc(response);
8079 #else /* not HAVE_LIBCURL */
8080 err = IE_NOTSUP;
8081 #endif
8083 return err;
8087 /* Get details about credit for sending pre-paid commercial messages.
8088 * @context is ISDS session context.
8089 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8090 * @from_date is first day of credit history to return in @history. Only
8091 * tm_year, tm_mon and tm_mday carry sane value.
8092 * @to_date is last day of credit history to return in @history. Only
8093 * tm_year, tm_mon and tm_mday carry sane value.
8094 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8095 * if you don't care. This and all other credit values are integers in
8096 * hundredths of Czech Crowns.
8097 * @email outputs notification e-mail address where notifications about credit
8098 * are sent. This is automatically reallocated string. Pass NULL if you don't
8099 * care. It can return NULL if no address is defined.
8100 * @history outputs auto-reallocated list of pointers to struct
8101 * isds_credit_event. Events in closed interval @from_time to @to_time are
8102 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8103 * are sorted by time.
8104 * @return:
8105 * IE_SUCCESS if the credit details have been obtained correctly,
8106 * or other appropriate error. Please note that server allows to retrieve
8107 * only limited history of events. */
8108 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8109 const char *box_id,
8110 const struct tm *from_date, const struct tm *to_date,
8111 long int *credit, char **email, struct isds_list **history) {
8112 isds_error err = IE_SUCCESS;
8113 #if HAVE_LIBCURL
8114 char *box_id_locale = NULL;
8115 xmlNodePtr request = NULL, node;
8116 xmlNsPtr isds_ns = NULL;
8117 xmlChar *string = NULL;
8119 xmlDocPtr response = NULL;
8120 xmlXPathContextPtr xpath_ctx = NULL;
8121 xmlXPathObjectPtr result = NULL;
8123 const xmlChar *codes[] = {
8124 BAD_CAST "1004",
8125 BAD_CAST "2011",
8126 BAD_CAST "1093",
8127 BAD_CAST "1137",
8128 BAD_CAST "1058",
8129 NULL
8131 const char *meanings[] = {
8132 "Insufficient priviledges for the box",
8133 "The box does not exist",
8134 "Date is too long (history is not available after 15 months)",
8135 "Interval is too long (limit is 3 months)",
8136 "Invalid date"
8138 const isds_error errors[] = {
8139 IE_ISDS,
8140 IE_NOEXIST,
8141 IE_DATE,
8142 IE_DATE,
8143 IE_DATE,
8145 struct code_map_isds_error map = {
8146 .codes = codes,
8147 .meanings = meanings,
8148 .errors = errors
8150 #endif
8152 if (!context) return IE_INVALID_CONTEXT;
8153 zfree(context->long_message);
8155 /* Free output argument */
8156 if (NULL != credit) *credit = 0;
8157 if (NULL != email) zfree(*email);
8158 isds_list_free(history);
8160 if (NULL == box_id) return IE_INVAL;
8162 #if HAVE_LIBCURL
8163 /* Check if connection is established */
8164 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8166 box_id_locale = _isds_utf82locale((char*)box_id);
8167 if (NULL == box_id_locale) {
8168 err = IE_NOMEM;
8169 goto leave;
8172 /* Build request */
8173 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8174 if (NULL == request) {
8175 isds_printf_message(context,
8176 _("Could not build DataBoxCreditInfo request for %s box"),
8177 box_id_locale);
8178 err = IE_ERROR;
8179 goto leave;
8181 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8182 if(!isds_ns) {
8183 isds_log_message(context, _("Could not create ISDS name space"));
8184 err = IE_ERROR;
8185 goto leave;
8187 xmlSetNs(request, isds_ns);
8189 /* Add mandatory XSD:tIdDbInput child */
8190 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8191 /* Add mandatory dates elements with optional values */
8192 if (from_date) {
8193 err = tm2datestring(from_date, &string);
8194 if (err) {
8195 isds_log_message(context,
8196 _("Could not convert `from_date' argument to ISO date "
8197 "string"));
8198 goto leave;
8200 INSERT_STRING(request, "ciFromDate", string);
8201 zfree(string);
8202 } else {
8203 INSERT_STRING(request, "ciFromDate", NULL);
8205 if (to_date) {
8206 err = tm2datestring(to_date, &string);
8207 if (err) {
8208 isds_log_message(context,
8209 _("Could not convert `to_date' argument to ISO date "
8210 "string"));
8211 goto leave;
8213 INSERT_STRING(request, "ciTodate", string);
8214 zfree(string);
8215 } else {
8216 INSERT_STRING(request, "ciTodate", NULL);
8219 /* Send request and check response*/
8220 err = send_destroy_request_check_response(context,
8221 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8222 &request, &response, NULL, &map);
8223 if (err) goto leave;
8226 /* Extract data */
8227 /* Set context to the root */
8228 xpath_ctx = xmlXPathNewContext(response);
8229 if (!xpath_ctx) {
8230 err = IE_ERROR;
8231 goto leave;
8233 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8234 err = IE_ERROR;
8235 goto leave;
8237 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8238 xpath_ctx);
8239 if (!result) {
8240 err = IE_ERROR;
8241 goto leave;
8243 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8244 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8245 err = IE_ISDS;
8246 goto leave;
8248 if (result->nodesetval->nodeNr > 1) {
8249 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8250 err = IE_ISDS;
8251 goto leave;
8253 xpath_ctx->node = result->nodesetval->nodeTab[0];
8254 xmlXPathFreeObject(result); result = NULL;
8256 /* Extract common data */
8257 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8258 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8260 /* Extract records */
8261 if (NULL == history) goto leave;
8262 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8263 xpath_ctx);
8264 if (!result) {
8265 err = IE_ERROR;
8266 goto leave;
8268 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8269 struct isds_list *prev_item = NULL;
8271 /* Iterate over all records */
8272 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8273 struct isds_list *item;
8275 /* Prepare structure */
8276 item = calloc(1, sizeof(*item));
8277 if (!item) {
8278 err = IE_NOMEM;
8279 goto leave;
8281 item->destructor = (void(*)(void**))isds_credit_event_free;
8282 if (i == 0) *history = item;
8283 else prev_item->next = item;
8284 prev_item = item;
8286 /* Extract it */
8287 xpath_ctx->node = result->nodesetval->nodeTab[i];
8288 err = extract_CiRecord(context,
8289 (struct isds_credit_event **) (&item->data),
8290 xpath_ctx);
8291 if (err) goto leave;
8295 leave:
8296 if (!err) {
8297 isds_log(ILF_ISDS, ILL_DEBUG,
8298 _("DataBoxCreditInfo request processed by server successfully.\n"));
8300 if (err) {
8301 isds_list_free(history);
8302 if (NULL != email) zfree(*email)
8305 free(box_id_locale);
8306 xmlXPathFreeObject(result);
8307 xmlXPathFreeContext(xpath_ctx);
8308 xmlFreeDoc(response);
8310 #else /* not HAVE_LIBCURL */
8311 err = IE_NOTSUP;
8312 #endif
8314 return err;
8318 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8319 * code, destroy response and log success.
8320 * @context is ISDS session context.
8321 * @service_name is name of SERVICE_DB_MANIPULATION service
8322 * @box_id is UTF-8 encoded box identifier as zero terminated string
8323 * @approval is optional external approval of box manipulation
8324 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8325 * NULL, if you don't care. */
8326 static isds_error build_send_manipulationdbid_request_check_drop_response(
8327 struct isds_ctx *context, const xmlChar *service_name,
8328 const xmlChar *box_id, const struct isds_approval *approval,
8329 xmlChar **refnumber) {
8330 isds_error err = IE_SUCCESS;
8331 #if HAVE_LIBCURL
8332 xmlDocPtr response = NULL;
8333 #endif
8335 if (!context) return IE_INVALID_CONTEXT;
8336 zfree(context->long_message);
8337 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8339 #if HAVE_LIBCURL
8340 /* Check if connection is established */
8341 if (!context->curl) return IE_CONNECTION_CLOSED;
8343 /* Do request and check for success */
8344 err = build_send_dbid_request_check_response(context,
8345 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8346 &response, refnumber);
8347 xmlFreeDoc(response);
8349 if (!err) {
8350 char *service_name_locale = _isds_utf82locale((char *) service_name);
8351 isds_log(ILF_ISDS, ILL_DEBUG,
8352 _("%s request processed by server successfully.\n"),
8353 service_name_locale);
8354 free(service_name_locale);
8356 #else /* not HAVE_LIBCURL */
8357 err = IE_NOTSUP;
8358 #endif
8360 return err;
8364 /* Switch box into state where box can receive commercial messages (off by
8365 * default)
8366 * @context is ISDS session context.
8367 * @box_id is UTF-8 encoded box identifier as zero terminated string
8368 * @allow is true for enable, false for disable commercial messages income
8369 * @approval is optional external approval of box manipulation
8370 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8371 * NULL, if you don't care. */
8372 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8373 const char *box_id, const _Bool allow,
8374 const struct isds_approval *approval, char **refnumber) {
8375 return build_send_manipulationdbid_request_check_drop_response(context,
8376 (allow) ? BAD_CAST "SetOpenAddressing" :
8377 BAD_CAST "ClearOpenAddressing",
8378 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8382 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8383 * message acceptance). This is just a box permission. Sender must apply
8384 * such role by sending each message.
8385 * @context is ISDS session context.
8386 * @box_id is UTF-8 encoded box identifier as zero terminated string
8387 * @allow is true for enable, false for disable OVM role permission
8388 * @approval is optional external approval of box manipulation
8389 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8390 * NULL, if you don't care. */
8391 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
8392 const char *box_id, const _Bool allow,
8393 const struct isds_approval *approval, char **refnumber) {
8394 return build_send_manipulationdbid_request_check_drop_response(context,
8395 (allow) ? BAD_CAST "SetEffectiveOVM" :
8396 BAD_CAST "ClearEffectiveOVM",
8397 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8401 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8402 * code, destroy response and log success.
8403 * @context is ISDS session context.
8404 * @service_name is name of SERVICE_DB_MANIPULATION service
8405 * @owner is structure describing box
8406 * @approval is optional external approval of box manipulation
8407 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8408 * NULL, if you don't care. */
8409 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8410 struct isds_ctx *context, const xmlChar *service_name,
8411 const struct isds_DbOwnerInfo *owner,
8412 const struct isds_approval *approval, xmlChar **refnumber) {
8413 isds_error err = IE_SUCCESS;
8414 #if HAVE_LIBCURL
8415 char *service_name_locale = NULL;
8416 xmlNodePtr request = NULL, db_owner_info;
8417 xmlNsPtr isds_ns = NULL;
8418 #endif
8421 if (!context) return IE_INVALID_CONTEXT;
8422 zfree(context->long_message);
8423 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8425 #if HAVE_LIBCURL
8426 service_name_locale = _isds_utf82locale((char*)service_name);
8427 if (!service_name_locale) {
8428 err = IE_NOMEM;
8429 goto leave;
8432 /* Build request */
8433 request = xmlNewNode(NULL, service_name);
8434 if (!request) {
8435 isds_printf_message(context,
8436 _("Could not build %s request"), service_name_locale);
8437 err = IE_ERROR;
8438 goto leave;
8440 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8441 if(!isds_ns) {
8442 isds_log_message(context, _("Could not create ISDS name space"));
8443 err = IE_ERROR;
8444 goto leave;
8446 xmlSetNs(request, isds_ns);
8449 /* Add XSD:tOwnerInfoInput child*/
8450 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8451 err = insert_DbOwnerInfo(context, owner, db_owner_info);
8452 if (err) goto leave;
8454 /* Add XSD:gExtApproval*/
8455 err = insert_GExtApproval(context, approval, request);
8456 if (err) goto leave;
8458 /* Send it to server and process response */
8459 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8460 service_name, &request, refnumber);
8462 leave:
8463 xmlFreeNode(request);
8464 free(service_name_locale);
8465 #else /* not HAVE_LIBCURL */
8466 err = IE_NOTSUP;
8467 #endif
8469 return err;
8473 /* Switch box accessibility state on request of box owner.
8474 * Despite the name, owner must do the request off-line. This function is
8475 * designed for such off-line meeting points (e.g. Czech POINT).
8476 * @context is ISDS session context.
8477 * @box identifies box to switch accessibility state.
8478 * @allow is true for making accessible, false to disallow access.
8479 * @approval is optional external approval of box manipulation
8480 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8481 * NULL, if you don't care. */
8482 isds_error isds_switch_box_accessibility_on_owner_request(
8483 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8484 const _Bool allow, const struct isds_approval *approval,
8485 char **refnumber) {
8486 return build_send_manipulationdbowner_request_check_drop_response(context,
8487 (allow) ? BAD_CAST "EnableOwnDataBox" :
8488 BAD_CAST "DisableOwnDataBox",
8489 box, approval, (xmlChar **) refnumber);
8493 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8494 * date.
8495 * @context is ISDS session context.
8496 * @box identifies box to switch accessibility state.
8497 * @since is date since accessibility has been denied. This can be past too.
8498 * Only tm_year, tm_mon and tm_mday carry sane value.
8499 * @approval is optional external approval of box manipulation
8500 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8501 * NULL, if you don't care. */
8502 isds_error isds_disable_box_accessibility_externaly(
8503 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8504 const struct tm *since, const struct isds_approval *approval,
8505 char **refnumber) {
8506 isds_error err = IE_SUCCESS;
8507 #if HAVE_LIBCURL
8508 char *service_name_locale = NULL;
8509 xmlNodePtr request = NULL, node;
8510 xmlNsPtr isds_ns = NULL;
8511 xmlChar *string = NULL;
8512 #endif
8515 if (!context) return IE_INVALID_CONTEXT;
8516 zfree(context->long_message);
8517 if (!box || !since) return IE_INVAL;
8519 #if HAVE_LIBCURL
8520 /* Build request */
8521 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
8522 if (!request) {
8523 isds_printf_message(context,
8524 _("Could not build %s request"), "DisableDataBoxExternally");
8525 err = IE_ERROR;
8526 goto leave;
8528 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8529 if(!isds_ns) {
8530 isds_log_message(context, _("Could not create ISDS name space"));
8531 err = IE_ERROR;
8532 goto leave;
8534 xmlSetNs(request, isds_ns);
8537 /* Add @box identification */
8538 INSERT_ELEMENT(node, request, "dbOwnerInfo");
8539 err = insert_DbOwnerInfo(context, box, node);
8540 if (err) goto leave;
8542 /* Add @since date */
8543 err = tm2datestring(since, &string);
8544 if(err) {
8545 isds_log_message(context,
8546 _("Could not convert `since' argument to ISO date string"));
8547 goto leave;
8549 INSERT_STRING(request, "dbOwnerDisableDate", string);
8550 zfree(string);
8552 /* Add @approval */
8553 err = insert_GExtApproval(context, approval, request);
8554 if (err) goto leave;
8556 /* Send it to server and process response */
8557 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8558 BAD_CAST "DisableDataBoxExternally", &request,
8559 (xmlChar **) refnumber);
8561 leave:
8562 free(string);
8563 xmlFreeNode(request);
8564 free(service_name_locale);
8565 #else /* not HAVE_LIBCURL */
8566 err = IE_NOTSUP;
8567 #endif
8569 return err;
8573 #if HAVE_LIBCURL
8574 /* Insert struct isds_message data (envelope (recipient data optional) and
8575 * documents into XML tree
8576 * @context is session context
8577 * @outgoing_message is libisds structure with message data
8578 * @create_message is XML CreateMessage or CreateMultipleMessage element
8579 * @process_recipient true for recipient data serialization, false for no
8580 * serialization */
8581 static isds_error insert_envelope_files(struct isds_ctx *context,
8582 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8583 const _Bool process_recipient) {
8585 isds_error err = IE_SUCCESS;
8586 xmlNodePtr envelope, dm_files, node;
8587 xmlChar *string = NULL;
8589 if (!context) return IE_INVALID_CONTEXT;
8590 if (!outgoing_message || !create_message) return IE_INVAL;
8593 /* Build envelope */
8594 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8595 if (!envelope) {
8596 isds_printf_message(context, _("Could not add dmEnvelope child to "
8597 "%s element"), create_message->name);
8598 return IE_ERROR;
8601 if (!outgoing_message->envelope) {
8602 isds_log_message(context, _("Outgoing message is missing envelope"));
8603 err = IE_INVAL;
8604 goto leave;
8607 /* Insert optional message type */
8608 err = insert_message_type(context, outgoing_message->envelope->dmType,
8609 envelope);
8610 if (err) goto leave;
8612 INSERT_STRING(envelope, "dmSenderOrgUnit",
8613 outgoing_message->envelope->dmSenderOrgUnit);
8614 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8615 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8617 if (process_recipient) {
8618 if (!outgoing_message->envelope->dbIDRecipient) {
8619 isds_log_message(context,
8620 _("Outgoing message is missing recipient box identifier"));
8621 err = IE_INVAL;
8622 goto leave;
8624 INSERT_STRING(envelope, "dbIDRecipient",
8625 outgoing_message->envelope->dbIDRecipient);
8627 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8628 outgoing_message->envelope->dmRecipientOrgUnit);
8629 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8630 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8631 INSERT_STRING(envelope, "dmToHands",
8632 outgoing_message->envelope->dmToHands);
8635 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8636 "dmAnnotation");
8637 INSERT_STRING(envelope, "dmAnnotation",
8638 outgoing_message->envelope->dmAnnotation);
8640 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8641 0, 50, "dmRecipientRefNumber");
8642 INSERT_STRING(envelope, "dmRecipientRefNumber",
8643 outgoing_message->envelope->dmRecipientRefNumber);
8645 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8646 0, 50, "dmSenderRefNumber");
8647 INSERT_STRING(envelope, "dmSenderRefNumber",
8648 outgoing_message->envelope->dmSenderRefNumber);
8650 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8651 0, 50, "dmRecipientIdent");
8652 INSERT_STRING(envelope, "dmRecipientIdent",
8653 outgoing_message->envelope->dmRecipientIdent);
8655 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8656 0, 50, "dmSenderIdent");
8657 INSERT_STRING(envelope, "dmSenderIdent",
8658 outgoing_message->envelope->dmSenderIdent);
8660 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8661 outgoing_message->envelope->dmLegalTitleLaw, string);
8662 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8663 outgoing_message->envelope->dmLegalTitleYear, string);
8664 INSERT_STRING(envelope, "dmLegalTitleSect",
8665 outgoing_message->envelope->dmLegalTitleSect);
8666 INSERT_STRING(envelope, "dmLegalTitlePar",
8667 outgoing_message->envelope->dmLegalTitlePar);
8668 INSERT_STRING(envelope, "dmLegalTitlePoint",
8669 outgoing_message->envelope->dmLegalTitlePoint);
8671 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8672 outgoing_message->envelope->dmPersonalDelivery);
8673 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8674 outgoing_message->envelope->dmAllowSubstDelivery);
8676 /* ???: Should we require value for dbEffectiveOVM sender?
8677 * ISDS has default as true */
8678 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8679 INSERT_BOOLEAN(envelope, "dmOVM",
8680 outgoing_message->envelope->dmPublishOwnID);
8683 /* Append dmFiles */
8684 if (!outgoing_message->documents) {
8685 isds_log_message(context,
8686 _("Outgoing message is missing list of documents"));
8687 err = IE_INVAL;
8688 goto leave;
8690 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8691 if (!dm_files) {
8692 isds_printf_message(context, _("Could not add dmFiles child to "
8693 "%s element"), create_message->name);
8694 err = IE_ERROR;
8695 goto leave;
8698 /* Check for document hierarchy */
8699 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8700 if (err) goto leave;
8702 /* Process each document */
8703 for (struct isds_list *item =
8704 (struct isds_list *) outgoing_message->documents;
8705 item; item = item->next) {
8706 if (!item->data) {
8707 isds_log_message(context,
8708 _("List of documents contains empty item"));
8709 err = IE_INVAL;
8710 goto leave;
8712 /* FIXME: Check for dmFileMetaType and for document references.
8713 * Only first document can be of MAIN type */
8714 err = insert_document(context, (struct isds_document*) item->data,
8715 dm_files);
8717 if (err) goto leave;
8720 leave:
8721 free(string);
8722 return err;
8724 #endif /* HAVE_LIBCURL */
8727 /* Send a message via ISDS to a recipient
8728 * @context is session context
8729 * @outgoing_message is message to send; Some members are mandatory (like
8730 * dbIDRecipient), some are optional and some are irrelevant (especially data
8731 * about sender). Included pointer to isds_list documents must contain at
8732 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8733 * members will be filled with valid data from ISDS. Exact list of write
8734 * members is subject to change. Currently dmID is changed.
8735 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8736 isds_error isds_send_message(struct isds_ctx *context,
8737 struct isds_message *outgoing_message) {
8739 isds_error err = IE_SUCCESS;
8740 #if HAVE_LIBCURL
8741 xmlNsPtr isds_ns = NULL;
8742 xmlNodePtr request = NULL;
8743 xmlDocPtr response = NULL;
8744 xmlChar *code = NULL, *message = NULL;
8745 xmlXPathContextPtr xpath_ctx = NULL;
8746 xmlXPathObjectPtr result = NULL;
8747 /*_Bool message_is_complete = 0;*/
8748 #endif
8750 if (!context) return IE_INVALID_CONTEXT;
8751 zfree(context->long_message);
8752 if (!outgoing_message) return IE_INVAL;
8754 #if HAVE_LIBCURL
8755 /* Check if connection is established
8756 * TODO: This check should be done downstairs. */
8757 if (!context->curl) return IE_CONNECTION_CLOSED;
8760 /* Build CreateMessage request */
8761 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8762 if (!request) {
8763 isds_log_message(context,
8764 _("Could not build CreateMessage request"));
8765 return IE_ERROR;
8767 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8768 if(!isds_ns) {
8769 isds_log_message(context, _("Could not create ISDS name space"));
8770 xmlFreeNode(request);
8771 return IE_ERROR;
8773 xmlSetNs(request, isds_ns);
8775 /* Append envelope and files */
8776 err = insert_envelope_files(context, outgoing_message, request, 1);
8777 if (err) goto leave;
8780 /* Signal we can serialize message since now */
8781 /*message_is_complete = 1;*/
8784 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8786 /* Sent request */
8787 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8789 /* Don't' destroy request, we want to provide it to application later */
8791 if (err) {
8792 isds_log(ILF_ISDS, ILL_DEBUG,
8793 _("Processing ISDS response on CreateMessage "
8794 "request failed\n"));
8795 goto leave;
8798 /* Check for response status */
8799 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8800 &code, &message, NULL);
8801 if (err) {
8802 isds_log(ILF_ISDS, ILL_DEBUG,
8803 _("ISDS response on CreateMessage request "
8804 "is missing status\n"));
8805 goto leave;
8808 /* Request processed, but refused by server or server failed */
8809 if (xmlStrcmp(code, BAD_CAST "0000")) {
8810 char *box_id_locale =
8811 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8812 char *code_locale = _isds_utf82locale((char*)code);
8813 char *message_locale = _isds_utf82locale((char*)message);
8814 isds_log(ILF_ISDS, ILL_DEBUG,
8815 _("Server did not accept message for %s on CreateMessage "
8816 "request (code=%s, message=%s)\n"),
8817 box_id_locale, code_locale, message_locale);
8818 isds_log_message(context, message_locale);
8819 free(box_id_locale);
8820 free(code_locale);
8821 free(message_locale);
8822 err = IE_ISDS;
8823 goto leave;
8827 /* Extract data */
8828 xpath_ctx = xmlXPathNewContext(response);
8829 if (!xpath_ctx) {
8830 err = IE_ERROR;
8831 goto leave;
8833 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8834 err = IE_ERROR;
8835 goto leave;
8837 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8838 xpath_ctx);
8839 if (!result) {
8840 err = IE_ERROR;
8841 goto leave;
8843 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8844 isds_log_message(context, _("Missing CreateMessageResponse element"));
8845 err = IE_ISDS;
8846 goto leave;
8848 if (result->nodesetval->nodeNr > 1) {
8849 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8850 err = IE_ISDS;
8851 goto leave;
8853 xpath_ctx->node = result->nodesetval->nodeTab[0];
8854 xmlXPathFreeObject(result); result = NULL;
8856 if (outgoing_message->envelope->dmID) {
8857 free(outgoing_message->envelope->dmID);
8858 outgoing_message->envelope->dmID = NULL;
8860 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8861 if (!outgoing_message->envelope->dmID) {
8862 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8863 "but did not return assigned message ID\n"));
8866 leave:
8867 /* TODO: Serialize message into structure member raw */
8868 /* XXX: Each web service transport message in different format.
8869 * Therefore it's not possible to save them directly.
8870 * To save them, one must figure out common format.
8871 * We can leave it on application, or we can implement the ESS format. */
8872 /*if (message_is_complete) {
8873 if (outgoing_message->envelope->dmID) {
8875 /* Add assigned message ID as first child*/
8876 /*xmlNodePtr dmid_text = xmlNewText(
8877 (xmlChar *) outgoing_message->envelope->dmID);
8878 if (!dmid_text) goto serialization_failed;
8880 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8881 BAD_CAST "dmID");
8882 if (!dmid_element) {
8883 xmlFreeNode(dmid_text);
8884 goto serialization_failed;
8887 xmlNodePtr dmid_element_with_text =
8888 xmlAddChild(dmid_element, dmid_text);
8889 if (!dmid_element_with_text) {
8890 xmlFreeNode(dmid_element);
8891 xmlFreeNode(dmid_text);
8892 goto serialization_failed;
8895 node = xmlAddPrevSibling(envelope->childern,
8896 dmid_element_with_text);
8897 if (!node) {
8898 xmlFreeNodeList(dmid_element_with_text);
8899 goto serialization_failed;
8903 /* Serialize message with ID into raw */
8904 /*buffer = serialize_element(envelope)*/
8905 /* }
8907 serialization_failed:
8911 /* Clean up */
8912 xmlXPathFreeObject(result);
8913 xmlXPathFreeContext(xpath_ctx);
8915 free(code);
8916 free(message);
8917 xmlFreeDoc(response);
8918 xmlFreeNode(request);
8920 if (!err)
8921 isds_log(ILF_ISDS, ILL_DEBUG,
8922 _("CreateMessage request processed by server "
8923 "successfully.\n"));
8924 #else /* not HAVE_LIBCURL */
8925 err = IE_NOTSUP;
8926 #endif
8928 return err;
8932 /* Send a message via ISDS to a multiple recipients
8933 * @context is session context
8934 * @outgoing_message is message to send; Some members are mandatory,
8935 * some are optional and some are irrelevant (especially data
8936 * about sender). Data about recipient will be substituted by ISDS from
8937 * @copies. Included pointer to isds_list documents must
8938 * contain at least one document of FILEMETATYPE_MAIN.
8939 * @copies is list of isds_message_copy structures addressing all desired
8940 * recipients. This is read-write structure, some members will be filled with
8941 * valid data from ISDS (message IDs, error codes, error descriptions).
8942 * @return
8943 * ISDS_SUCCESS if all messages have been sent
8944 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8945 * succeeded messages can be identified by copies->data->error),
8946 * or other error code if something other goes wrong. */
8947 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8948 const struct isds_message *outgoing_message,
8949 struct isds_list *copies) {
8951 isds_error err = IE_SUCCESS;
8952 #if HAVE_LIBCURL
8953 isds_error append_err;
8954 xmlNsPtr isds_ns = NULL;
8955 xmlNodePtr request = NULL, recipients, recipient, node;
8956 struct isds_list *item;
8957 struct isds_message_copy *copy;
8958 xmlDocPtr response = NULL;
8959 xmlChar *code = NULL, *message = NULL;
8960 xmlXPathContextPtr xpath_ctx = NULL;
8961 xmlXPathObjectPtr result = NULL;
8962 xmlChar *string = NULL;
8963 int i;
8964 #endif
8966 if (!context) return IE_INVALID_CONTEXT;
8967 zfree(context->long_message);
8968 if (!outgoing_message || !copies) return IE_INVAL;
8970 #if HAVE_LIBCURL
8971 /* Check if connection is established
8972 * TODO: This check should be done downstairs. */
8973 if (!context->curl) return IE_CONNECTION_CLOSED;
8976 /* Build CreateMultipleMessage request */
8977 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
8978 if (!request) {
8979 isds_log_message(context,
8980 _("Could not build CreateMultipleMessage request"));
8981 return IE_ERROR;
8983 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8984 if(!isds_ns) {
8985 isds_log_message(context, _("Could not create ISDS name space"));
8986 xmlFreeNode(request);
8987 return IE_ERROR;
8989 xmlSetNs(request, isds_ns);
8992 /* Build recipients */
8993 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8994 if (!recipients) {
8995 isds_log_message(context, _("Could not add dmRecipients child to "
8996 "CreateMultipleMessage element"));
8997 xmlFreeNode(request);
8998 return IE_ERROR;
9001 /* Insert each recipient */
9002 for (item = copies; item; item = item->next) {
9003 copy = (struct isds_message_copy *) item->data;
9004 if (!copy) {
9005 isds_log_message(context,
9006 _("`copies' list item contains empty data"));
9007 err = IE_INVAL;
9008 goto leave;
9011 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
9012 if (!recipient) {
9013 isds_log_message(context, _("Could not add dmRecipient child to "
9014 "dmRecipients element"));
9015 err = IE_ERROR;
9016 goto leave;
9019 if (!copy->dbIDRecipient) {
9020 isds_log_message(context,
9021 _("Message copy is missing recipient box identifier"));
9022 err = IE_INVAL;
9023 goto leave;
9025 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
9026 INSERT_STRING(recipient, "dmRecipientOrgUnit",
9027 copy->dmRecipientOrgUnit);
9028 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
9029 copy->dmRecipientOrgUnitNum, string);
9030 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
9033 /* Append envelope and files */
9034 err = insert_envelope_files(context, outgoing_message, request, 0);
9035 if (err) goto leave;
9038 isds_log(ILF_ISDS, ILL_DEBUG,
9039 _("Sending CreateMultipleMessage request to ISDS\n"));
9041 /* Sent request */
9042 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9043 if (err) {
9044 isds_log(ILF_ISDS, ILL_DEBUG,
9045 _("Processing ISDS response on CreateMultipleMessage "
9046 "request failed\n"));
9047 goto leave;
9050 /* Check for response status */
9051 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9052 &code, &message, NULL);
9053 if (err) {
9054 isds_log(ILF_ISDS, ILL_DEBUG,
9055 _("ISDS response on CreateMultipleMessage request "
9056 "is missing status\n"));
9057 goto leave;
9060 /* Request processed, but some copies failed */
9061 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9062 char *box_id_locale =
9063 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9064 char *code_locale = _isds_utf82locale((char*)code);
9065 char *message_locale = _isds_utf82locale((char*)message);
9066 isds_log(ILF_ISDS, ILL_DEBUG,
9067 _("Server did accept message for multiple recipients "
9068 "on CreateMultipleMessage request but delivery to "
9069 "some of them failed (code=%s, message=%s)\n"),
9070 box_id_locale, code_locale, message_locale);
9071 isds_log_message(context, message_locale);
9072 free(box_id_locale);
9073 free(code_locale);
9074 free(message_locale);
9075 err = IE_PARTIAL_SUCCESS;
9078 /* Request refused by server as whole */
9079 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9080 char *box_id_locale =
9081 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9082 char *code_locale = _isds_utf82locale((char*)code);
9083 char *message_locale = _isds_utf82locale((char*)message);
9084 isds_log(ILF_ISDS, ILL_DEBUG,
9085 _("Server did not accept message for multiple recipients "
9086 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9087 box_id_locale, code_locale, message_locale);
9088 isds_log_message(context, message_locale);
9089 free(box_id_locale);
9090 free(code_locale);
9091 free(message_locale);
9092 err = IE_ISDS;
9093 goto leave;
9097 /* Extract data */
9098 xpath_ctx = xmlXPathNewContext(response);
9099 if (!xpath_ctx) {
9100 err = IE_ERROR;
9101 goto leave;
9103 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9104 err = IE_ERROR;
9105 goto leave;
9107 result = xmlXPathEvalExpression(
9108 BAD_CAST "/isds:CreateMultipleMessageResponse"
9109 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9110 xpath_ctx);
9111 if (!result) {
9112 err = IE_ERROR;
9113 goto leave;
9115 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9116 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9117 err = IE_ISDS;
9118 goto leave;
9121 /* Extract message ID and delivery status for each copy */
9122 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9123 item = item->next, i++) {
9124 copy = (struct isds_message_copy *) item->data;
9125 xpath_ctx->node = result->nodesetval->nodeTab[i];
9127 append_err = append_TMStatus(context, copy, xpath_ctx);
9128 if (append_err) {
9129 err = append_err;
9130 goto leave;
9133 if (item || i < result->nodesetval->nodeNr) {
9134 isds_printf_message(context, _("ISDS returned unexpected number of "
9135 "message copy delivery states: %d"),
9136 result->nodesetval->nodeNr);
9137 err = IE_ISDS;
9138 goto leave;
9142 leave:
9143 /* Clean up */
9144 free(string);
9145 xmlXPathFreeObject(result);
9146 xmlXPathFreeContext(xpath_ctx);
9148 free(code);
9149 free(message);
9150 xmlFreeDoc(response);
9151 xmlFreeNode(request);
9153 if (!err)
9154 isds_log(ILF_ISDS, ILL_DEBUG,
9155 _("CreateMultipleMessageResponse request processed by server "
9156 "successfully.\n"));
9157 #else /* not HAVE_LIBCURL */
9158 err = IE_NOTSUP;
9159 #endif
9161 return err;
9165 /* Get list of messages. This is common core for getting sent or received
9166 * messages.
9167 * Any criterion argument can be NULL, if you don't care about it.
9168 * @context is session context. Must not be NULL.
9169 * @outgoing_direction is true if you want list of outgoing messages,
9170 * it's false if you want incoming messages.
9171 * @from_time is minimal time and date of message sending inclusive.
9172 * @to_time is maximal time and date of message sending inclusive
9173 * @organization_unit_number is number of sender/recipient respectively.
9174 * @status_filter is bit field of isds_message_status values. Use special
9175 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9176 * all values, you can use bit-wise arithmetic if you want.)
9177 * @offset is index of first message we are interested in. First message is 1.
9178 * Set to 0 (or 1) if you don't care.
9179 * @number is maximal length of list you want to get as input value, outputs
9180 * number of messages matching these criteria. Can be NULL if you don't care
9181 * (applies to output value either).
9182 * @messages is automatically reallocated list of isds_message's. Be ware that
9183 * it returns only brief overview (envelope and some other fields) about each
9184 * message, not the complete message. FIXME: Specify exact fields.
9185 * The list is sorted by delivery time in ascending order.
9186 * Use NULL if you don't care about don't need the data (useful if you want to
9187 * know only the @number). If you provide &NULL, list will be allocated on
9188 * heap, if you provide pointer to non-NULL, list will be freed automatically
9189 * at first. Also in case of error the list will be NULLed.
9190 * @return IE_SUCCESS or appropriate error code. */
9191 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9192 _Bool outgoing_direction,
9193 const struct timeval *from_time, const struct timeval *to_time,
9194 const long int *organization_unit_number,
9195 const unsigned int status_filter,
9196 const unsigned long int offset, unsigned long int *number,
9197 struct isds_list **messages) {
9199 isds_error err = IE_SUCCESS;
9200 #if HAVE_LIBCURL
9201 xmlNsPtr isds_ns = NULL;
9202 xmlNodePtr request = NULL, node;
9203 xmlDocPtr response = NULL;
9204 xmlChar *code = NULL, *message = NULL;
9205 xmlXPathContextPtr xpath_ctx = NULL;
9206 xmlXPathObjectPtr result = NULL;
9207 xmlChar *string = NULL;
9208 int count = 0;
9209 #endif
9211 if (!context) return IE_INVALID_CONTEXT;
9212 zfree(context->long_message);
9214 /* Free former message list if any */
9215 if (messages) isds_list_free(messages);
9217 #if HAVE_LIBCURL
9218 /* Check if connection is established
9219 * TODO: This check should be done downstairs. */
9220 if (!context->curl) return IE_CONNECTION_CLOSED;
9222 /* Build GetListOf*Messages request */
9223 request = xmlNewNode(NULL,
9224 (outgoing_direction) ?
9225 BAD_CAST "GetListOfSentMessages" :
9226 BAD_CAST "GetListOfReceivedMessages"
9228 if (!request) {
9229 isds_log_message(context,
9230 (outgoing_direction) ?
9231 _("Could not build GetListOfSentMessages request") :
9232 _("Could not build GetListOfReceivedMessages request")
9234 return IE_ERROR;
9236 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9237 if(!isds_ns) {
9238 isds_log_message(context, _("Could not create ISDS name space"));
9239 xmlFreeNode(request);
9240 return IE_ERROR;
9242 xmlSetNs(request, isds_ns);
9245 if (from_time) {
9246 err = timeval2timestring(from_time, &string);
9247 if (err) goto leave;
9249 INSERT_STRING(request, "dmFromTime", string);
9250 free(string); string = NULL;
9252 if (to_time) {
9253 err = timeval2timestring(to_time, &string);
9254 if (err) goto leave;
9256 INSERT_STRING(request, "dmToTime", string);
9257 free(string); string = NULL;
9259 if (outgoing_direction) {
9260 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9261 organization_unit_number, string);
9262 } else {
9263 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9264 organization_unit_number, string);
9267 if (status_filter > MESSAGESTATE_ANY) {
9268 isds_printf_message(context,
9269 _("Invalid message state filter value: %ld"), status_filter);
9270 err = IE_INVAL;
9271 goto leave;
9273 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9275 if (offset > 0 ) {
9276 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9277 } else {
9278 INSERT_STRING(request, "dmOffset", "1");
9281 /* number 0 means no limit */
9282 if (number && *number == 0) {
9283 INSERT_STRING(request, "dmLimit", NULL);
9284 } else {
9285 INSERT_ULONGINT(request, "dmLimit", number, string);
9289 isds_log(ILF_ISDS, ILL_DEBUG,
9290 (outgoing_direction) ?
9291 _("Sending GetListOfSentMessages request to ISDS\n") :
9292 _("Sending GetListOfReceivedMessages request to ISDS\n")
9295 /* Sent request */
9296 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9297 xmlFreeNode(request); request = NULL;
9299 if (err) {
9300 isds_log(ILF_ISDS, ILL_DEBUG,
9301 (outgoing_direction) ?
9302 _("Processing ISDS response on GetListOfSentMessages "
9303 "request failed\n") :
9304 _("Processing ISDS response on GetListOfReceivedMessages "
9305 "request failed\n")
9307 goto leave;
9310 /* Check for response status */
9311 err = isds_response_status(context, SERVICE_DM_INFO, response,
9312 &code, &message, NULL);
9313 if (err) {
9314 isds_log(ILF_ISDS, ILL_DEBUG,
9315 (outgoing_direction) ?
9316 _("ISDS response on GetListOfSentMessages request "
9317 "is missing status\n") :
9318 _("ISDS response on GetListOfReceivedMessages request "
9319 "is missing status\n")
9321 goto leave;
9324 /* Request processed, but nothing found */
9325 if (xmlStrcmp(code, BAD_CAST "0000")) {
9326 char *code_locale = _isds_utf82locale((char*)code);
9327 char *message_locale = _isds_utf82locale((char*)message);
9328 isds_log(ILF_ISDS, ILL_DEBUG,
9329 (outgoing_direction) ?
9330 _("Server refused GetListOfSentMessages request "
9331 "(code=%s, message=%s)\n") :
9332 _("Server refused GetListOfReceivedMessages request "
9333 "(code=%s, message=%s)\n"),
9334 code_locale, message_locale);
9335 isds_log_message(context, message_locale);
9336 free(code_locale);
9337 free(message_locale);
9338 err = IE_ISDS;
9339 goto leave;
9343 /* Extract data */
9344 xpath_ctx = xmlXPathNewContext(response);
9345 if (!xpath_ctx) {
9346 err = IE_ERROR;
9347 goto leave;
9349 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9350 err = IE_ERROR;
9351 goto leave;
9353 result = xmlXPathEvalExpression(
9354 (outgoing_direction) ?
9355 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9356 "isds:dmRecords/isds:dmRecord" :
9357 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9358 "isds:dmRecords/isds:dmRecord",
9359 xpath_ctx);
9360 if (!result) {
9361 err = IE_ERROR;
9362 goto leave;
9365 /* Fill output arguments in */
9366 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9367 struct isds_envelope *envelope;
9368 struct isds_list *item = NULL, *last_item = NULL;
9370 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9371 /* Create new message */
9372 item = calloc(1, sizeof(*item));
9373 if (!item) {
9374 err = IE_NOMEM;
9375 goto leave;
9377 item->destructor = (void(*)(void**)) &isds_message_free;
9378 item->data = calloc(1, sizeof(struct isds_message));
9379 if (!item->data) {
9380 isds_list_free(&item);
9381 err = IE_NOMEM;
9382 goto leave;
9385 /* Extract envelope data */
9386 xpath_ctx->node = result->nodesetval->nodeTab[count];
9387 envelope = NULL;
9388 err = extract_DmRecord(context, &envelope, xpath_ctx);
9389 if (err) {
9390 isds_list_free(&item);
9391 goto leave;
9394 /* Attach extracted envelope */
9395 ((struct isds_message *) item->data)->envelope = envelope;
9397 /* Append new message into the list */
9398 if (!*messages) {
9399 *messages = last_item = item;
9400 } else {
9401 last_item->next = item;
9402 last_item = item;
9406 if (number) *number = count;
9408 leave:
9409 if (err) {
9410 isds_list_free(messages);
9413 free(string);
9414 xmlXPathFreeObject(result);
9415 xmlXPathFreeContext(xpath_ctx);
9417 free(code);
9418 free(message);
9419 xmlFreeDoc(response);
9420 xmlFreeNode(request);
9422 if (!err)
9423 isds_log(ILF_ISDS, ILL_DEBUG,
9424 (outgoing_direction) ?
9425 _("GetListOfSentMessages request processed by server "
9426 "successfully.\n") :
9427 _("GetListOfReceivedMessages request processed by server "
9428 "successfully.\n")
9430 #else /* not HAVE_LIBCURL */
9431 err = IE_NOTSUP;
9432 #endif
9433 return err;
9437 /* Get list of outgoing (already sent) messages.
9438 * Any criterion argument can be NULL, if you don't care about it.
9439 * @context is session context. Must not be NULL.
9440 * @from_time is minimal time and date of message sending inclusive.
9441 * @to_time is maximal time and date of message sending inclusive
9442 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9443 * @status_filter is bit field of isds_message_status values. Use special
9444 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9445 * all values, you can use bit-wise arithmetic if you want.)
9446 * @offset is index of first message we are interested in. First message is 1.
9447 * Set to 0 (or 1) if you don't care.
9448 * @number is maximal length of list you want to get as input value, outputs
9449 * number of messages matching these criteria. Can be NULL if you don't care
9450 * (applies to output value either).
9451 * @messages is automatically reallocated list of isds_message's. Be ware that
9452 * it returns only brief overview (envelope and some other fields) about each
9453 * message, not the complete message. FIXME: Specify exact fields.
9454 * The list is sorted by delivery time in ascending order.
9455 * Use NULL if you don't care about the meta data (useful if you want to know
9456 * only the @number). If you provide &NULL, list will be allocated on heap,
9457 * if you provide pointer to non-NULL, list will be freed automatically at
9458 * first. Also in case of error the list will be NULLed.
9459 * @return IE_SUCCESS or appropriate error code. */
9460 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9461 const struct timeval *from_time, const struct timeval *to_time,
9462 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9463 const unsigned long int offset, unsigned long int *number,
9464 struct isds_list **messages) {
9466 return isds_get_list_of_messages(
9467 context, 1,
9468 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9469 offset, number,
9470 messages);
9474 /* Get list of incoming (addressed to you) messages.
9475 * Any criterion argument can be NULL, if you don't care about it.
9476 * @context is session context. Must not be NULL.
9477 * @from_time is minimal time and date of message sending inclusive.
9478 * @to_time is maximal time and date of message sending inclusive
9479 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9480 * @status_filter is bit field of isds_message_status values. Use special
9481 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9482 * all values, you can use bit-wise arithmetic if you want.)
9483 * @offset is index of first message we are interested in. First message is 1.
9484 * Set to 0 (or 1) if you don't care.
9485 * @number is maximal length of list you want to get as input value, outputs
9486 * number of messages matching these criteria. Can be NULL if you don't care
9487 * (applies to output value either).
9488 * @messages is automatically reallocated list of isds_message's. Be ware that
9489 * it returns only brief overview (envelope and some other fields) about each
9490 * message, not the complete message. FIXME: Specify exact fields.
9491 * Use NULL if you don't care about the meta data (useful if you want to know
9492 * only the @number). If you provide &NULL, list will be allocated on heap,
9493 * if you provide pointer to non-NULL, list will be freed automatically at
9494 * first. Also in case of error the list will be NULLed.
9495 * @return IE_SUCCESS or appropriate error code. */
9496 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9497 const struct timeval *from_time, const struct timeval *to_time,
9498 const long int *dmRecipientOrgUnitNum,
9499 const unsigned int status_filter,
9500 const unsigned long int offset, unsigned long int *number,
9501 struct isds_list **messages) {
9503 return isds_get_list_of_messages(
9504 context, 0,
9505 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9506 offset, number,
9507 messages);
9511 /* Get list of sent message state changes.
9512 * Any criterion argument can be NULL, if you don't care about it.
9513 * @context is session context. Must not be NULL.
9514 * @from_time is minimal time and date of status changes inclusive
9515 * @to_time is maximal time and date of status changes inclusive
9516 * @changed_states is automatically reallocated list of
9517 * isds_message_status_change's. If you provide &NULL, list will be allocated
9518 * on heap, if you provide pointer to non-NULL, list will be freed
9519 * automatically at first. Also in case of error the list will be NULLed.
9520 * XXX: The list item ordering is not specified.
9521 * XXX: Server provides only `recent' changes.
9522 * @return IE_SUCCESS or appropriate error code. */
9523 isds_error isds_get_list_of_sent_message_state_changes(
9524 struct isds_ctx *context,
9525 const struct timeval *from_time, const struct timeval *to_time,
9526 struct isds_list **changed_states) {
9528 isds_error err = IE_SUCCESS;
9529 #if HAVE_LIBCURL
9530 xmlNsPtr isds_ns = NULL;
9531 xmlNodePtr request = NULL, node;
9532 xmlDocPtr response = NULL;
9533 xmlXPathContextPtr xpath_ctx = NULL;
9534 xmlXPathObjectPtr result = NULL;
9535 xmlChar *string = NULL;
9536 int count = 0;
9537 #endif
9539 if (!context) return IE_INVALID_CONTEXT;
9540 zfree(context->long_message);
9542 /* Free former message list if any */
9543 isds_list_free(changed_states);
9545 #if HAVE_LIBCURL
9546 /* Check if connection is established
9547 * TODO: This check should be done downstairs. */
9548 if (!context->curl) return IE_CONNECTION_CLOSED;
9550 /* Build GetMessageStateChanges request */
9551 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
9552 if (!request) {
9553 isds_log_message(context,
9554 _("Could not build GetMessageStateChanges request"));
9555 return IE_ERROR;
9557 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9558 if(!isds_ns) {
9559 isds_log_message(context, _("Could not create ISDS name space"));
9560 xmlFreeNode(request);
9561 return IE_ERROR;
9563 xmlSetNs(request, isds_ns);
9566 if (from_time) {
9567 err = timeval2timestring(from_time, &string);
9568 if (err) goto leave;
9570 INSERT_STRING(request, "dmFromTime", string);
9571 zfree(string);
9573 if (to_time) {
9574 err = timeval2timestring(to_time, &string);
9575 if (err) goto leave;
9577 INSERT_STRING(request, "dmToTime", string);
9578 zfree(string);
9581 /* Sent request */
9582 err = send_destroy_request_check_response(context,
9583 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9584 &response, NULL, NULL);
9585 if (err) goto leave;
9588 /* Extract data */
9589 xpath_ctx = xmlXPathNewContext(response);
9590 if (!xpath_ctx) {
9591 err = IE_ERROR;
9592 goto leave;
9594 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9595 err = IE_ERROR;
9596 goto leave;
9598 result = xmlXPathEvalExpression(
9599 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9600 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9601 if (!result) {
9602 err = IE_ERROR;
9603 goto leave;
9606 /* Fill output arguments in */
9607 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9608 struct isds_list *item = NULL, *last_item = NULL;
9610 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9611 /* Create new status change */
9612 item = calloc(1, sizeof(*item));
9613 if (!item) {
9614 err = IE_NOMEM;
9615 goto leave;
9617 item->destructor =
9618 (void(*)(void**)) &isds_message_status_change_free;
9620 /* Extract message status change */
9621 xpath_ctx->node = result->nodesetval->nodeTab[count];
9622 err = extract_StateChangesRecord(context,
9623 (struct isds_message_status_change **) &item->data,
9624 xpath_ctx);
9625 if (err) {
9626 isds_list_free(&item);
9627 goto leave;
9630 /* Append new message status change into the list */
9631 if (!*changed_states) {
9632 *changed_states = last_item = item;
9633 } else {
9634 last_item->next = item;
9635 last_item = item;
9640 leave:
9641 if (err) {
9642 isds_list_free(changed_states);
9645 free(string);
9646 xmlXPathFreeObject(result);
9647 xmlXPathFreeContext(xpath_ctx);
9648 xmlFreeDoc(response);
9649 xmlFreeNode(request);
9651 if (!err)
9652 isds_log(ILF_ISDS, ILL_DEBUG,
9653 _("GetMessageStateChanges request processed by server "
9654 "successfully.\n"));
9655 #else /* not HAVE_LIBCURL */
9656 err = IE_NOTSUP;
9657 #endif
9658 return err;
9662 #if HAVE_LIBCURL
9663 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9664 * code
9665 * @context is session context
9666 * @service is ISDS WS service handler
9667 * @service_name is name of SERVICE_DM_OPERATIONS
9668 * @message_id is message ID to send as service argument to ISDS
9669 * @response is reallocated server SOAP body response as XML document
9670 * @raw_response is reallocated bit stream with response body. Use
9671 * NULL if you don't care
9672 * @raw_response_length is size of @raw_response in bytes
9673 * @code is reallocated ISDS status code
9674 * @status_message is reallocated ISDS status message
9675 * @return error coded from lower layer, context message will be set up
9676 * appropriately. */
9677 static isds_error build_send_check_message_request(struct isds_ctx *context,
9678 const isds_service service, const xmlChar *service_name,
9679 const char *message_id,
9680 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9681 xmlChar **code, xmlChar **status_message) {
9683 isds_error err = IE_SUCCESS;
9684 char *service_name_locale = NULL, *message_id_locale = NULL;
9685 xmlNodePtr request = NULL, node;
9686 xmlNsPtr isds_ns = NULL;
9688 if (!context) return IE_INVALID_CONTEXT;
9689 if (!service_name || !message_id) return IE_INVAL;
9690 if (!response || !code || !status_message) return IE_INVAL;
9691 if (!raw_response_length && raw_response) return IE_INVAL;
9693 /* Free output argument */
9694 xmlFreeDoc(*response); *response = NULL;
9695 if (raw_response) zfree(*raw_response);
9696 zfree(*code);
9697 zfree(*status_message);
9700 /* Check if connection is established
9701 * TODO: This check should be done downstairs. */
9702 if (!context->curl) return IE_CONNECTION_CLOSED;
9704 service_name_locale = _isds_utf82locale((char*)service_name);
9705 message_id_locale = _isds_utf82locale(message_id);
9706 if (!service_name_locale || !message_id_locale) {
9707 err = IE_NOMEM;
9708 goto leave;
9711 /* Build request */
9712 request = xmlNewNode(NULL, service_name);
9713 if (!request) {
9714 isds_printf_message(context,
9715 _("Could not build %s request for %s message ID"),
9716 service_name_locale, message_id_locale);
9717 err = IE_ERROR;
9718 goto leave;
9720 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9721 if(!isds_ns) {
9722 isds_log_message(context, _("Could not create ISDS name space"));
9723 err = IE_ERROR;
9724 goto leave;
9726 xmlSetNs(request, isds_ns);
9729 /* Add requested ID */
9730 err = validate_message_id_length(context, (xmlChar *) message_id);
9731 if (err) goto leave;
9732 INSERT_STRING(request, "dmID", message_id);
9735 isds_log(ILF_ISDS, ILL_DEBUG,
9736 _("Sending %s request for %s message ID to ISDS\n"),
9737 service_name_locale, message_id_locale);
9739 /* Send request */
9740 err = _isds(context, service, request, response,
9741 raw_response, raw_response_length);
9742 xmlFreeNode(request); request = NULL;
9744 if (err) {
9745 isds_log(ILF_ISDS, ILL_DEBUG,
9746 _("Processing ISDS response on %s request failed\n"),
9747 service_name_locale);
9748 goto leave;
9751 /* Check for response status */
9752 err = isds_response_status(context, service, *response,
9753 code, status_message, NULL);
9754 if (err) {
9755 isds_log(ILF_ISDS, ILL_DEBUG,
9756 _("ISDS response on %s request is missing status\n"),
9757 service_name_locale);
9758 goto leave;
9761 /* Request processed, but nothing found */
9762 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9763 char *code_locale = _isds_utf82locale((char*) *code);
9764 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9765 isds_log(ILF_ISDS, ILL_DEBUG,
9766 _("Server refused %s request for %s message ID "
9767 "(code=%s, message=%s)\n"),
9768 service_name_locale, message_id_locale,
9769 code_locale, status_message_locale);
9770 isds_log_message(context, status_message_locale);
9771 free(code_locale);
9772 free(status_message_locale);
9773 err = IE_ISDS;
9774 goto leave;
9777 leave:
9778 free(message_id_locale);
9779 free(service_name_locale);
9780 xmlFreeNode(request);
9781 return err;
9785 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9786 * signed data and free ISDS response.
9787 * @context is session context
9788 * @message_id is UTF-8 encoded message ID for logging purpose
9789 * @response is parsed XML document. It will be freed and NULLed in the middle
9790 * of function run to save memory. This is not guaranteed in case of error.
9791 * @request_name is name of ISDS request used to construct response root
9792 * element name and for logging purpose.
9793 * @raw is reallocated output buffer with DER encoded CMS data
9794 * @raw_length is size of @raw buffer in bytes
9795 * @returns standard error codes, in case of error, @raw will be freed and
9796 * NULLed, @response sometimes. */
9797 static isds_error find_extract_signed_data_free_response(
9798 struct isds_ctx *context, const xmlChar *message_id,
9799 xmlDocPtr *response, const xmlChar *request_name,
9800 void **raw, size_t *raw_length) {
9802 isds_error err = IE_SUCCESS;
9803 char *xpath_expression = NULL;
9804 xmlXPathContextPtr xpath_ctx = NULL;
9805 xmlXPathObjectPtr result = NULL;
9806 char *encoded_structure = NULL;
9808 if (!context) return IE_INVALID_CONTEXT;
9809 if (!raw) return IE_INVAL;
9810 zfree(*raw);
9811 if (!message_id || !response || !*response || !request_name || !raw_length)
9812 return IE_INVAL;
9814 /* Build XPath expression */
9815 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9816 "Response/isds:dmSignature");
9817 if (!xpath_expression) return IE_NOMEM;
9819 /* Extract data */
9820 xpath_ctx = xmlXPathNewContext(*response);
9821 if (!xpath_ctx) {
9822 err = IE_ERROR;
9823 goto leave;
9825 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9826 err = IE_ERROR;
9827 goto leave;
9829 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9830 if (!result) {
9831 err = IE_ERROR;
9832 goto leave;
9834 /* Empty response */
9835 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9836 char *message_id_locale = _isds_utf82locale((char*) message_id);
9837 isds_printf_message(context,
9838 _("Server did not return any signed data for message ID `%s' "
9839 "on %s request"),
9840 message_id_locale, request_name);
9841 free(message_id_locale);
9842 err = IE_ISDS;
9843 goto leave;
9845 /* More responses */
9846 if (result->nodesetval->nodeNr > 1) {
9847 char *message_id_locale = _isds_utf82locale((char*) message_id);
9848 isds_printf_message(context,
9849 _("Server did return more signed data for message ID `%s' "
9850 "on %s request"),
9851 message_id_locale, request_name);
9852 free(message_id_locale);
9853 err = IE_ISDS;
9854 goto leave;
9856 /* One response */
9857 xpath_ctx->node = result->nodesetval->nodeTab[0];
9859 /* Extract PKCS#7 structure */
9860 EXTRACT_STRING(".", encoded_structure);
9861 if (!encoded_structure) {
9862 isds_log_message(context, _("dmSignature element is empty"));
9865 /* Here we have delivery info as standalone CMS in encoded_structure.
9866 * We don't need any other data, free them: */
9867 xmlXPathFreeObject(result); result = NULL;
9868 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9869 xmlFreeDoc(*response); *response = NULL;
9872 /* Decode PKCS#7 to DER format */
9873 *raw_length = _isds_b64decode(encoded_structure, raw);
9874 if (*raw_length == (size_t) -1) {
9875 isds_log_message(context,
9876 _("Error while Base64-decoding PKCS#7 structure"));
9877 err = IE_ERROR;
9878 goto leave;
9881 leave:
9882 if (err) {
9883 zfree(*raw);
9884 raw_length = 0;
9887 free(encoded_structure);
9888 xmlXPathFreeObject(result);
9889 xmlXPathFreeContext(xpath_ctx);
9890 free(xpath_expression);
9892 return err;
9894 #endif /* HAVE_LIBCURL */
9897 /* Download incoming message envelope identified by ID.
9898 * @context is session context
9899 * @message_id is message identifier (you can get them from
9900 * isds_get_list_of_received_messages())
9901 * @message is automatically reallocated message retrieved from ISDS.
9902 * It will miss documents per se. Use isds_get_received_message(), if you are
9903 * interested in documents (content) too.
9904 * Returned hash and timestamp require documents to be verifiable. */
9905 isds_error isds_get_received_envelope(struct isds_ctx *context,
9906 const char *message_id, struct isds_message **message) {
9908 isds_error err = IE_SUCCESS;
9909 #if HAVE_LIBCURL
9910 xmlDocPtr response = NULL;
9911 xmlChar *code = NULL, *status_message = NULL;
9912 xmlXPathContextPtr xpath_ctx = NULL;
9913 xmlXPathObjectPtr result = NULL;
9914 #endif
9916 if (!context) return IE_INVALID_CONTEXT;
9917 zfree(context->long_message);
9919 /* Free former message if any */
9920 if (!message) return IE_INVAL;
9921 isds_message_free(message);
9923 #if HAVE_LIBCURL
9924 /* Do request and check for success */
9925 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9926 BAD_CAST "MessageEnvelopeDownload", message_id,
9927 &response, NULL, NULL, &code, &status_message);
9928 if (err) goto leave;
9930 /* Extract data */
9931 xpath_ctx = xmlXPathNewContext(response);
9932 if (!xpath_ctx) {
9933 err = IE_ERROR;
9934 goto leave;
9936 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9937 err = IE_ERROR;
9938 goto leave;
9940 result = xmlXPathEvalExpression(
9941 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9942 "isds:dmReturnedMessageEnvelope",
9943 xpath_ctx);
9944 if (!result) {
9945 err = IE_ERROR;
9946 goto leave;
9948 /* Empty response */
9949 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9950 char *message_id_locale = _isds_utf82locale((char*) message_id);
9951 isds_printf_message(context,
9952 _("Server did not return any envelope for ID `%s' "
9953 "on MessageEnvelopeDownload request"), message_id_locale);
9954 free(message_id_locale);
9955 err = IE_ISDS;
9956 goto leave;
9958 /* More envelops */
9959 if (result->nodesetval->nodeNr > 1) {
9960 char *message_id_locale = _isds_utf82locale((char*) message_id);
9961 isds_printf_message(context,
9962 _("Server did return more envelopes for ID `%s' "
9963 "on MessageEnvelopeDownload request"), message_id_locale);
9964 free(message_id_locale);
9965 err = IE_ISDS;
9966 goto leave;
9968 /* One message */
9969 xpath_ctx->node = result->nodesetval->nodeTab[0];
9971 /* Extract the envelope (= message without documents, hence 0) */
9972 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9973 if (err) goto leave;
9975 /* Save XML blob */
9976 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9977 &(*message)->raw_length);
9979 leave:
9980 if (err) {
9981 isds_message_free(message);
9984 xmlXPathFreeObject(result);
9985 xmlXPathFreeContext(xpath_ctx);
9987 free(code);
9988 free(status_message);
9989 if (!*message || !(*message)->xml) {
9990 xmlFreeDoc(response);
9993 if (!err)
9994 isds_log(ILF_ISDS, ILL_DEBUG,
9995 _("MessageEnvelopeDownload request processed by server "
9996 "successfully.\n")
9998 #else /* not HAVE_LIBCURL */
9999 err = IE_NOTSUP;
10000 #endif
10001 return err;
10005 /* Load delivery info of any format from buffer.
10006 * @context is session context
10007 * @raw_type advertises format of @buffer content. Only delivery info types
10008 * are accepted.
10009 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10010 * retrieve such data from message->raw after calling
10011 * isds_get_signed_delivery_info().
10012 * @length is length of buffer in bytes.
10013 * @message is automatically reallocated message parsed from @buffer.
10014 * @strategy selects how buffer will be attached into raw isds_message member.
10015 * */
10016 isds_error isds_load_delivery_info(struct isds_ctx *context,
10017 const isds_raw_type raw_type,
10018 const void *buffer, const size_t length,
10019 struct isds_message **message, const isds_buffer_strategy strategy) {
10021 isds_error err = IE_SUCCESS;
10022 message_ns_type message_ns;
10023 xmlDocPtr message_doc = NULL;
10024 xmlXPathContextPtr xpath_ctx = NULL;
10025 xmlXPathObjectPtr result = NULL;
10026 void *xml_stream = NULL;
10027 size_t xml_stream_length = 0;
10029 if (!context) return IE_INVALID_CONTEXT;
10030 zfree(context->long_message);
10031 if (!message) return IE_INVAL;
10032 isds_message_free(message);
10033 if (!buffer) return IE_INVAL;
10036 /* Select buffer format and extract XML from CMS*/
10037 switch (raw_type) {
10038 case RAWTYPE_DELIVERYINFO:
10039 message_ns = MESSAGE_NS_UNSIGNED;
10040 xml_stream = (void *) buffer;
10041 xml_stream_length = length;
10042 break;
10044 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
10045 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10046 xml_stream = (void *) buffer;
10047 xml_stream_length = length;
10048 break;
10050 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10051 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10052 err = _isds_extract_cms_data(context, buffer, length,
10053 &xml_stream, &xml_stream_length);
10054 if (err) goto leave;
10055 break;
10057 default:
10058 isds_log_message(context, _("Bad raw delivery representation type"));
10059 return IE_INVAL;
10060 break;
10063 isds_log(ILF_ISDS, ILL_DEBUG,
10064 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10065 xml_stream_length, xml_stream);
10067 /* Convert delivery info XML stream into XPath context */
10068 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10069 if (!message_doc) {
10070 err = IE_XML;
10071 goto leave;
10073 xpath_ctx = xmlXPathNewContext(message_doc);
10074 if (!xpath_ctx) {
10075 err = IE_ERROR;
10076 goto leave;
10078 /* XXX: Name spaces mangled for signed delivery info:
10079 * http://isds.czechpoint.cz/v20/delivery:
10081 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10082 * <q:dmDelivery>
10083 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10084 * <p:dmID>170272</p:dmID>
10085 * ...
10086 * </p:dmDm>
10087 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10088 * ...
10089 * </q:dmEvents>...</q:dmEvents>
10090 * </q:dmDelivery>
10091 * </q:GetDeliveryInfoResponse>
10092 * */
10093 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10094 err = IE_ERROR;
10095 goto leave;
10097 result = xmlXPathEvalExpression(
10098 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10099 xpath_ctx);
10100 if (!result) {
10101 err = IE_ERROR;
10102 goto leave;
10104 /* Empty delivery info */
10105 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10106 isds_printf_message(context,
10107 _("XML document is not sisds:dmDelivery document"));
10108 err = IE_ISDS;
10109 goto leave;
10111 /* More delivery info's */
10112 if (result->nodesetval->nodeNr > 1) {
10113 isds_printf_message(context,
10114 _("XML document has more sisds:dmDelivery elements"));
10115 err = IE_ISDS;
10116 goto leave;
10118 /* One delivery info */
10119 xpath_ctx->node = result->nodesetval->nodeTab[0];
10121 /* Extract the envelope (= message without documents, hence 0).
10122 * XXX: extract_TReturnedMessage() can obtain attachments size,
10123 * but delivery info carries none. It's coded as option elements,
10124 * so it should work. */
10125 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10126 if (err) goto leave;
10128 /* Extract events */
10129 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10130 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10131 if (err) { err = IE_ERROR; goto leave; }
10132 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10133 if (err) goto leave;
10135 /* Append raw CMS structure into message */
10136 (*message)->raw_type = raw_type;
10137 switch (strategy) {
10138 case BUFFER_DONT_STORE:
10139 break;
10140 case BUFFER_COPY:
10141 (*message)->raw = malloc(length);
10142 if (!(*message)->raw) {
10143 err = IE_NOMEM;
10144 goto leave;
10146 memcpy((*message)->raw, buffer, length);
10147 (*message)->raw_length = length;
10148 break;
10149 case BUFFER_MOVE:
10150 (*message)->raw = (void *) buffer;
10151 (*message)->raw_length = length;
10152 break;
10153 default:
10154 err = IE_ENUM;
10155 goto leave;
10158 leave:
10159 if (err) {
10160 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10161 isds_message_free(message);
10164 xmlXPathFreeObject(result);
10165 xmlXPathFreeContext(xpath_ctx);
10166 if (!*message || !(*message)->xml) {
10167 xmlFreeDoc(message_doc);
10169 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10171 if (!err)
10172 isds_log(ILF_ISDS, ILL_DEBUG,
10173 _("Delivery info loaded successfully.\n"));
10174 return err;
10178 /* Download signed delivery info-sheet of given message identified by ID.
10179 * @context is session context
10180 * @message_id is message identifier (you can get them from
10181 * isds_get_list_of_{sent,received}_messages())
10182 * @message is automatically reallocated message retrieved from ISDS.
10183 * It will miss documents per se. Use isds_get_signed_received_message(),
10184 * if you are interested in documents (content). OTOH, only this function
10185 * can get list events message has gone through. */
10186 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10187 const char *message_id, struct isds_message **message) {
10189 isds_error err = IE_SUCCESS;
10190 #if HAVE_LIBCURL
10191 xmlDocPtr response = NULL;
10192 xmlChar *code = NULL, *status_message = NULL;
10193 void *raw = NULL;
10194 size_t raw_length = 0;
10195 #endif
10197 if (!context) return IE_INVALID_CONTEXT;
10198 zfree(context->long_message);
10200 /* Free former message if any */
10201 if (!message) return IE_INVAL;
10202 isds_message_free(message);
10204 #if HAVE_LIBCURL
10205 /* Do request and check for success */
10206 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10207 BAD_CAST "GetSignedDeliveryInfo", message_id,
10208 &response, NULL, NULL, &code, &status_message);
10209 if (err) goto leave;
10211 /* Find signed delivery info, extract it into raw and maybe free
10212 * response */
10213 err = find_extract_signed_data_free_response(context,
10214 (xmlChar *)message_id, &response,
10215 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10216 if (err) goto leave;
10218 /* Parse delivery info */
10219 err = isds_load_delivery_info(context,
10220 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10221 message, BUFFER_MOVE);
10222 if (err) goto leave;
10224 raw = NULL;
10226 leave:
10227 if (err) {
10228 isds_message_free(message);
10231 free(raw);
10232 free(code);
10233 free(status_message);
10234 xmlFreeDoc(response);
10236 if (!err)
10237 isds_log(ILF_ISDS, ILL_DEBUG,
10238 _("GetSignedDeliveryInfo request processed by server "
10239 "successfully.\n")
10241 #else /* not HAVE_LIBCURL */
10242 err = IE_NOTSUP;
10243 #endif
10244 return err;
10248 /* Download delivery info-sheet of given message identified by ID.
10249 * @context is session context
10250 * @message_id is message identifier (you can get them from
10251 * isds_get_list_of_{sent,received}_messages())
10252 * @message is automatically reallocated message retrieved from ISDS.
10253 * It will miss documents per se. Use isds_get_received_message(), if you are
10254 * interested in documents (content). OTOH, only this function can get list
10255 * of events message has gone through. */
10256 isds_error isds_get_delivery_info(struct isds_ctx *context,
10257 const char *message_id, struct isds_message **message) {
10259 isds_error err = IE_SUCCESS;
10260 #if HAVE_LIBCURL
10261 xmlDocPtr response = NULL;
10262 xmlChar *code = NULL, *status_message = NULL;
10263 xmlNodePtr delivery_node = NULL;
10264 void *raw = NULL;
10265 size_t raw_length = 0;
10266 #endif
10268 if (!context) return IE_INVALID_CONTEXT;
10269 zfree(context->long_message);
10271 /* Free former message if any */
10272 if (!message) return IE_INVAL;
10273 isds_message_free(message);
10275 #if HAVE_LIBCURL
10276 /* Do request and check for success */
10277 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10278 BAD_CAST "GetDeliveryInfo", message_id,
10279 &response, NULL, NULL, &code, &status_message);
10280 if (err) goto leave;
10283 /* Serialize delivery info */
10284 delivery_node = xmlDocGetRootElement(response);
10285 if (!delivery_node) {
10286 char *message_id_locale = _isds_utf82locale((char*) message_id);
10287 isds_printf_message(context,
10288 _("Server did not return any delivery info for ID `%s' "
10289 "on GetDeliveryInfo request"), message_id_locale);
10290 free(message_id_locale);
10291 err = IE_ISDS;
10292 goto leave;
10294 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10295 if (err) goto leave;
10297 /* Parse delivery info */
10298 /* TODO: Here we parse the response second time. We could single delivery
10299 * parser from isds_load_delivery_info() to make things faster. */
10300 err = isds_load_delivery_info(context,
10301 RAWTYPE_DELIVERYINFO, raw, raw_length,
10302 message, BUFFER_MOVE);
10303 if (err) goto leave;
10305 raw = NULL;
10308 leave:
10309 if (err) {
10310 isds_message_free(message);
10313 free(raw);
10314 free(code);
10315 free(status_message);
10316 xmlFreeDoc(response);
10318 if (!err)
10319 isds_log(ILF_ISDS, ILL_DEBUG,
10320 _("GetDeliveryInfo request processed by server "
10321 "successfully.\n")
10323 #else /* not HAVE_LIBCURL */
10324 err = IE_NOTSUP;
10325 #endif
10326 return err;
10330 /* Download incoming message identified by ID.
10331 * @context is session context
10332 * @message_id is message identifier (you can get them from
10333 * isds_get_list_of_received_messages())
10334 * @message is automatically reallocated message retrieved from ISDS */
10335 isds_error isds_get_received_message(struct isds_ctx *context,
10336 const char *message_id, struct isds_message **message) {
10338 isds_error err = IE_SUCCESS;
10339 #if HAVE_LIBCURL
10340 xmlDocPtr response = NULL;
10341 void *xml_stream = NULL;
10342 size_t xml_stream_length;
10343 xmlChar *code = NULL, *status_message = NULL;
10344 xmlXPathContextPtr xpath_ctx = NULL;
10345 xmlXPathObjectPtr result = NULL;
10346 char *phys_path = NULL;
10347 size_t phys_start, phys_end;
10348 #endif
10350 if (!context) return IE_INVALID_CONTEXT;
10351 zfree(context->long_message);
10353 /* Free former message if any */
10354 if (NULL == message) return IE_INVAL;
10355 if (message) isds_message_free(message);
10357 #if HAVE_LIBCURL
10358 /* Do request and check for success */
10359 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10360 BAD_CAST "MessageDownload", message_id,
10361 &response, &xml_stream, &xml_stream_length,
10362 &code, &status_message);
10363 if (err) goto leave;
10365 /* Extract data */
10366 xpath_ctx = xmlXPathNewContext(response);
10367 if (!xpath_ctx) {
10368 err = IE_ERROR;
10369 goto leave;
10371 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10372 err = IE_ERROR;
10373 goto leave;
10375 result = xmlXPathEvalExpression(
10376 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10377 xpath_ctx);
10378 if (!result) {
10379 err = IE_ERROR;
10380 goto leave;
10382 /* Empty response */
10383 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10384 char *message_id_locale = _isds_utf82locale((char*) message_id);
10385 isds_printf_message(context,
10386 _("Server did not return any message for ID `%s' "
10387 "on MessageDownload request"), message_id_locale);
10388 free(message_id_locale);
10389 err = IE_ISDS;
10390 goto leave;
10392 /* More messages */
10393 if (result->nodesetval->nodeNr > 1) {
10394 char *message_id_locale = _isds_utf82locale((char*) message_id);
10395 isds_printf_message(context,
10396 _("Server did return more messages for ID `%s' "
10397 "on MessageDownload request"), message_id_locale);
10398 free(message_id_locale);
10399 err = IE_ISDS;
10400 goto leave;
10402 /* One message */
10403 xpath_ctx->node = result->nodesetval->nodeTab[0];
10405 /* Extract the message */
10406 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10407 if (err) goto leave;
10409 /* Locate raw XML blob */
10410 phys_path = strdup(
10411 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10412 PHYSXML_ELEMENT_SEPARATOR
10413 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10414 PHYSXML_ELEMENT_SEPARATOR
10415 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10417 if (!phys_path) {
10418 err = IE_NOMEM;
10419 goto leave;
10421 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10422 phys_path, &phys_start, &phys_end);
10423 zfree(phys_path);
10424 if (err) {
10425 isds_log_message(context,
10426 _("Substring with isds:MessageDownloadResponse element "
10427 "could not be located in raw SOAP message"));
10428 goto leave;
10430 /* Save XML blob */
10431 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10432 &(*message)->raw_length);*/
10433 /* TODO: Store name space declarations from ancestors */
10434 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10435 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10436 (*message)->raw_length = phys_end - phys_start + 1;
10437 (*message)->raw = malloc((*message)->raw_length);
10438 if (!(*message)->raw) {
10439 err = IE_NOMEM;
10440 goto leave;
10442 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10445 leave:
10446 if (err) {
10447 isds_message_free(message);
10450 free(phys_path);
10452 xmlXPathFreeObject(result);
10453 xmlXPathFreeContext(xpath_ctx);
10455 free(code);
10456 free(status_message);
10457 free(xml_stream);
10458 if (!*message || !(*message)->xml) {
10459 xmlFreeDoc(response);
10462 if (!err)
10463 isds_log(ILF_ISDS, ILL_DEBUG,
10464 _("MessageDownload request processed by server "
10465 "successfully.\n")
10467 #else /* not HAVE_LIBCURL */
10468 err = IE_NOTSUP;
10469 #endif
10470 return err;
10474 /* Load message of any type from buffer.
10475 * @context is session context
10476 * @raw_type defines content type of @buffer. Only message types are allowed.
10477 * @buffer is message raw representation. Format (CMS, plain signed,
10478 * message direction) is defined in @raw_type. You can retrieve such data
10479 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10480 * @length is length of buffer in bytes.
10481 * @message is automatically reallocated message parsed from @buffer.
10482 * @strategy selects how buffer will be attached into raw isds_message member.
10483 * */
10484 isds_error isds_load_message(struct isds_ctx *context,
10485 const isds_raw_type raw_type, const void *buffer, const size_t length,
10486 struct isds_message **message, const isds_buffer_strategy strategy) {
10488 isds_error err = IE_SUCCESS;
10489 void *xml_stream = NULL;
10490 size_t xml_stream_length = 0;
10491 message_ns_type message_ns;
10492 xmlDocPtr message_doc = NULL;
10493 xmlXPathContextPtr xpath_ctx = NULL;
10494 xmlXPathObjectPtr result = NULL;
10496 if (!context) return IE_INVALID_CONTEXT;
10497 zfree(context->long_message);
10498 if (!message) return IE_INVAL;
10499 isds_message_free(message);
10500 if (!buffer) return IE_INVAL;
10503 /* Select buffer format and extract XML from CMS*/
10504 switch (raw_type) {
10505 case RAWTYPE_INCOMING_MESSAGE:
10506 message_ns = MESSAGE_NS_UNSIGNED;
10507 xml_stream = (void *) buffer;
10508 xml_stream_length = length;
10509 break;
10511 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10512 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10513 xml_stream = (void *) buffer;
10514 xml_stream_length = length;
10515 break;
10517 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10518 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10519 err = _isds_extract_cms_data(context, buffer, length,
10520 &xml_stream, &xml_stream_length);
10521 if (err) goto leave;
10522 break;
10524 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10525 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10526 xml_stream = (void *) buffer;
10527 xml_stream_length = length;
10528 break;
10530 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10531 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10532 err = _isds_extract_cms_data(context, buffer, length,
10533 &xml_stream, &xml_stream_length);
10534 if (err) goto leave;
10535 break;
10537 default:
10538 isds_log_message(context, _("Bad raw message representation type"));
10539 return IE_INVAL;
10540 break;
10543 isds_log(ILF_ISDS, ILL_DEBUG,
10544 _("Loading message:\n%.*s\nEnd of message\n"),
10545 xml_stream_length, xml_stream);
10547 /* Convert messages XML stream into XPath context */
10548 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10549 if (!message_doc) {
10550 err = IE_XML;
10551 goto leave;
10553 xpath_ctx = xmlXPathNewContext(message_doc);
10554 if (!xpath_ctx) {
10555 err = IE_ERROR;
10556 goto leave;
10558 /* XXX: Standard name space for unsigned incoming direction:
10559 * http://isds.czechpoint.cz/v20/
10561 * XXX: Name spaces mangled for signed outgoing direction:
10562 * http://isds.czechpoint.cz/v20/SentMessage:
10564 * <q:MessageDownloadResponse
10565 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10566 * <q:dmReturnedMessage>
10567 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10568 * <p:dmID>151916</p:dmID>
10569 * ...
10570 * </p:dmDm>
10571 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10572 * ...
10573 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10574 * </q:dmReturnedMessage>
10575 * </q:MessageDownloadResponse>
10577 * XXX: Name spaces mangled for signed incoming direction:
10578 * http://isds.czechpoint.cz/v20/message:
10580 * <q:MessageDownloadResponse
10581 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10582 * <q:dmReturnedMessage>
10583 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10584 * <p:dmID>151916</p:dmID>
10585 * ...
10586 * </p:dmDm>
10587 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10588 * ...
10589 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10590 * </q:dmReturnedMessage>
10591 * </q:MessageDownloadResponse>
10593 * Stupidity of ISDS developers is unlimited */
10594 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10595 err = IE_ERROR;
10596 goto leave;
10598 result = xmlXPathEvalExpression(
10599 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10600 xpath_ctx);
10601 if (!result) {
10602 err = IE_ERROR;
10603 goto leave;
10605 /* Empty message */
10606 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10607 isds_printf_message(context,
10608 _("XML document does not contain "
10609 "sisds:dmReturnedMessage element"));
10610 err = IE_ISDS;
10611 goto leave;
10613 /* More messages */
10614 if (result->nodesetval->nodeNr > 1) {
10615 isds_printf_message(context,
10616 _("XML document has more sisds:dmReturnedMessage elements"));
10617 err = IE_ISDS;
10618 goto leave;
10620 /* One message */
10621 xpath_ctx->node = result->nodesetval->nodeTab[0];
10623 /* Extract the message */
10624 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10625 if (err) goto leave;
10627 /* Append raw buffer into message */
10628 (*message)->raw_type = raw_type;
10629 switch (strategy) {
10630 case BUFFER_DONT_STORE:
10631 break;
10632 case BUFFER_COPY:
10633 (*message)->raw = malloc(length);
10634 if (!(*message)->raw) {
10635 err = IE_NOMEM;
10636 goto leave;
10638 memcpy((*message)->raw, buffer, length);
10639 (*message)->raw_length = length;
10640 break;
10641 case BUFFER_MOVE:
10642 (*message)->raw = (void *) buffer;
10643 (*message)->raw_length = length;
10644 break;
10645 default:
10646 err = IE_ENUM;
10647 goto leave;
10651 leave:
10652 if (err) {
10653 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10654 isds_message_free(message);
10657 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10658 xmlXPathFreeObject(result);
10659 xmlXPathFreeContext(xpath_ctx);
10660 if (!*message || !(*message)->xml) {
10661 xmlFreeDoc(message_doc);
10664 if (!err)
10665 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10666 return err;
10670 /* Determine type of raw message or delivery info according some heuristics.
10671 * It does not validate the raw blob.
10672 * @context is session context
10673 * @raw_type returns content type of @buffer. Valid only if exit code of this
10674 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10675 * reallocated memory.
10676 * @buffer is message raw representation.
10677 * @length is length of buffer in bytes. */
10678 isds_error isds_guess_raw_type(struct isds_ctx *context,
10679 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10680 isds_error err;
10681 void *xml_stream = NULL;
10682 size_t xml_stream_length = 0;
10683 xmlDocPtr document = NULL;
10684 xmlNodePtr root = NULL;
10686 if (!context) return IE_INVALID_CONTEXT;
10687 zfree(context->long_message);
10688 if (length == 0 || !buffer) return IE_INVAL;
10689 if (!raw_type) return IE_INVAL;
10691 /* Try CMS */
10692 err = _isds_extract_cms_data(context, buffer, length,
10693 &xml_stream, &xml_stream_length);
10694 if (err) {
10695 xml_stream = (void *) buffer;
10696 xml_stream_length = (size_t) length;
10697 err = IE_SUCCESS;
10700 /* Try XML */
10701 document = xmlParseMemory(xml_stream, xml_stream_length);
10702 if (!document) {
10703 isds_printf_message(context,
10704 _("Could not parse data as XML document"));
10705 err = IE_NOTSUP;
10706 goto leave;
10709 /* Get root element */
10710 root = xmlDocGetRootElement(document);
10711 if (!root) {
10712 isds_printf_message(context,
10713 _("XML document is missing root element"));
10714 err = IE_XML;
10715 goto leave;
10718 if (!root->ns || !root->ns->href) {
10719 isds_printf_message(context,
10720 _("Root element does not belong to any name space"));
10721 err = IE_NOTSUP;
10722 goto leave;
10725 /* Test name space */
10726 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10727 if (xml_stream == buffer)
10728 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10729 else
10730 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10731 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10732 if (xml_stream == buffer)
10733 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10734 else
10735 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10736 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10737 if (xml_stream == buffer)
10738 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10739 else
10740 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10741 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10742 if (xml_stream != buffer) {
10743 isds_printf_message(context,
10744 _("Document in ISDS name space is encapsulated into CMS" ));
10745 err = IE_NOTSUP;
10746 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10747 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10748 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10749 *raw_type = RAWTYPE_DELIVERYINFO;
10750 else {
10751 isds_printf_message(context,
10752 _("Unknown root element in ISDS name space"));
10753 err = IE_NOTSUP;
10755 } else {
10756 isds_printf_message(context,
10757 _("Unknown name space"));
10758 err = IE_NOTSUP;
10761 leave:
10762 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10763 xmlFreeDoc(document);
10764 return err;
10768 /* Download signed incoming/outgoing message identified by ID.
10769 * @context is session context
10770 * @output is true for outgoing message, false for incoming message
10771 * @message_id is message identifier (you can get them from
10772 * isds_get_list_of_{sent,received}_messages())
10773 * @message is automatically reallocated message retrieved from ISDS. The raw
10774 * member will be filled with PKCS#7 structure in DER format. */
10775 static isds_error isds_get_signed_message(struct isds_ctx *context,
10776 const _Bool outgoing, const char *message_id,
10777 struct isds_message **message) {
10779 isds_error err = IE_SUCCESS;
10780 #if HAVE_LIBCURL
10781 xmlDocPtr response = NULL;
10782 xmlChar *code = NULL, *status_message = NULL;
10783 xmlXPathContextPtr xpath_ctx = NULL;
10784 xmlXPathObjectPtr result = NULL;
10785 char *encoded_structure = NULL;
10786 void *raw = NULL;
10787 size_t raw_length = 0;
10788 #endif
10790 if (!context) return IE_INVALID_CONTEXT;
10791 zfree(context->long_message);
10792 if (!message) return IE_INVAL;
10793 isds_message_free(message);
10795 #if HAVE_LIBCURL
10796 /* Do request and check for success */
10797 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10798 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10799 BAD_CAST "SignedMessageDownload",
10800 message_id, &response, NULL, NULL, &code, &status_message);
10801 if (err) goto leave;
10803 /* Find signed message, extract it into raw and maybe free
10804 * response */
10805 err = find_extract_signed_data_free_response(context,
10806 (xmlChar *)message_id, &response,
10807 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10808 BAD_CAST "SignedMessageDownload",
10809 &raw, &raw_length);
10810 if (err) goto leave;
10812 /* Parse message */
10813 err = isds_load_message(context,
10814 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10815 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10816 raw, raw_length, message, BUFFER_MOVE);
10817 if (err) goto leave;
10819 raw = NULL;
10821 leave:
10822 if (err) {
10823 isds_message_free(message);
10826 free(encoded_structure);
10827 xmlXPathFreeObject(result);
10828 xmlXPathFreeContext(xpath_ctx);
10829 free(raw);
10831 free(code);
10832 free(status_message);
10833 xmlFreeDoc(response);
10835 if (!err)
10836 isds_log(ILF_ISDS, ILL_DEBUG,
10837 (outgoing) ?
10838 _("SignedSentMessageDownload request processed by server "
10839 "successfully.\n") :
10840 _("SignedMessageDownload request processed by server "
10841 "successfully.\n")
10843 #else /* not HAVE_LIBCURL */
10844 err = IE_NOTSUP;
10845 #endif
10846 return err;
10850 /* Download signed incoming message identified by ID.
10851 * @context is session context
10852 * @message_id is message identifier (you can get them from
10853 * isds_get_list_of_received_messages())
10854 * @message is automatically reallocated message retrieved from ISDS. The raw
10855 * member will be filled with PKCS#7 structure in DER format. */
10856 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10857 const char *message_id, struct isds_message **message) {
10858 return isds_get_signed_message(context, 0, message_id, message);
10862 /* Download signed outgoing message identified by ID.
10863 * @context is session context
10864 * @message_id is message identifier (you can get them from
10865 * isds_get_list_of_sent_messages())
10866 * @message is automatically reallocated message retrieved from ISDS. The raw
10867 * member will be filled with PKCS#7 structure in DER format. */
10868 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10869 const char *message_id, struct isds_message **message) {
10870 return isds_get_signed_message(context, 1, message_id, message);
10874 /* Get type and name of user who sent a message identified by ID.
10875 * @context is session context
10876 * @message_id is message identifier
10877 * @sender_type is pointer to automatically allocated type of sender detected
10878 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10879 * library or to the server, NULL will be returned. Pass NULL if you don't
10880 * care about it.
10881 * @raw_sender_type is automatically reallocated UTF-8 string describing
10882 * sender type or NULL if not known to server. Pass NULL if you don't care.
10883 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10884 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10885 isds_error isds_get_message_sender(struct isds_ctx *context,
10886 const char *message_id, isds_sender_type **sender_type,
10887 char **raw_sender_type, char **sender_name) {
10888 isds_error err = IE_SUCCESS;
10889 #if HAVE_LIBCURL
10890 xmlDocPtr response = NULL;
10891 xmlChar *code = NULL, *status_message = NULL;
10892 xmlXPathContextPtr xpath_ctx = NULL;
10893 xmlXPathObjectPtr result = NULL;
10894 char *type_string = NULL;
10895 #endif
10897 if (!context) return IE_INVALID_CONTEXT;
10898 zfree(context->long_message);
10899 if (sender_type) zfree(*sender_type);
10900 if (raw_sender_type) zfree(*raw_sender_type);
10901 if (sender_name) zfree(*sender_name);
10902 if (!message_id) return IE_INVAL;
10904 #if HAVE_LIBCURL
10905 /* Do request and check for success */
10906 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10907 BAD_CAST "GetMessageAuthor",
10908 message_id, &response, NULL, NULL, &code, &status_message);
10909 if (err) goto leave;
10911 /* Extract data */
10912 xpath_ctx = xmlXPathNewContext(response);
10913 if (!xpath_ctx) {
10914 err = IE_ERROR;
10915 goto leave;
10917 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10918 err = IE_ERROR;
10919 goto leave;
10921 result = xmlXPathEvalExpression(
10922 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10923 if (!result) {
10924 err = IE_ERROR;
10925 goto leave;
10927 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10928 isds_log_message(context,
10929 _("Missing GetMessageAuthorResponse element"));
10930 err = IE_ISDS;
10931 goto leave;
10933 if (result->nodesetval->nodeNr > 1) {
10934 isds_log_message(context,
10935 _("Multiple GetMessageAuthorResponse element"));
10936 err = IE_ISDS;
10937 goto leave;
10939 xpath_ctx->node = result->nodesetval->nodeTab[0];
10940 xmlXPathFreeObject(result); result = NULL;
10942 /* Fill output arguments in */
10943 EXTRACT_STRING("isds:userType", type_string);
10944 if (NULL != type_string) {
10945 if (NULL != sender_type) {
10946 *sender_type = calloc(1, sizeof(**sender_type));
10947 if (NULL == *sender_type) {
10948 err = IE_NOMEM;
10949 goto leave;
10952 err = string2isds_sender_type((xmlChar *)type_string,
10953 *sender_type);
10954 if (err) {
10955 zfree(*sender_type);
10956 if (err == IE_ENUM) {
10957 err = IE_SUCCESS;
10958 char *type_string_locale = _isds_utf82locale(type_string);
10959 isds_log(ILF_ISDS, ILL_WARNING,
10960 _("Unknown isds:userType value: %s"),
10961 type_string_locale);
10962 free(type_string_locale);
10967 if (NULL != sender_name)
10968 EXTRACT_STRING("isds:authorName", *sender_name);
10970 leave:
10971 if (err) {
10972 if (NULL != sender_type) zfree(*sender_type);
10973 zfree(type_string);
10974 if (NULL != sender_name) zfree(*sender_name);
10976 if (NULL != raw_sender_type) *raw_sender_type = type_string;
10978 xmlXPathFreeObject(result);
10979 xmlXPathFreeContext(xpath_ctx);
10981 free(code);
10982 free(status_message);
10983 xmlFreeDoc(response);
10985 if (!err)
10986 isds_log(ILF_ISDS, ILL_DEBUG,
10987 _("GetMessageAuthor request processed by server "
10988 "successfully.\n"));
10989 #else /* not HAVE_LIBCURL */
10990 err = IE_NOTSUP;
10991 #endif
10992 return err;
10996 /* Retrieve hash of message identified by ID stored in ISDS.
10997 * @context is session context
10998 * @message_id is message identifier
10999 * @hash is automatically reallocated message hash downloaded from ISDS.
11000 * Message must exist in system and must not be deleted. */
11001 isds_error isds_download_message_hash(struct isds_ctx *context,
11002 const char *message_id, struct isds_hash **hash) {
11004 isds_error err = IE_SUCCESS;
11005 #if HAVE_LIBCURL
11006 xmlDocPtr response = NULL;
11007 xmlChar *code = NULL, *status_message = NULL;
11008 xmlXPathContextPtr xpath_ctx = NULL;
11009 xmlXPathObjectPtr result = NULL;
11010 #endif
11012 if (!context) return IE_INVALID_CONTEXT;
11013 zfree(context->long_message);
11015 isds_hash_free(hash);
11017 #if HAVE_LIBCURL
11018 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11019 BAD_CAST "VerifyMessage", message_id,
11020 &response, NULL, NULL, &code, &status_message);
11021 if (err) goto leave;
11024 /* Extract data */
11025 xpath_ctx = xmlXPathNewContext(response);
11026 if (!xpath_ctx) {
11027 err = IE_ERROR;
11028 goto leave;
11030 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11031 err = IE_ERROR;
11032 goto leave;
11034 result = xmlXPathEvalExpression(
11035 BAD_CAST "/isds:VerifyMessageResponse",
11036 xpath_ctx);
11037 if (!result) {
11038 err = IE_ERROR;
11039 goto leave;
11041 /* Empty response */
11042 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11043 char *message_id_locale = _isds_utf82locale((char*) message_id);
11044 isds_printf_message(context,
11045 _("Server did not return any response for ID `%s' "
11046 "on VerifyMessage request"), message_id_locale);
11047 free(message_id_locale);
11048 err = IE_ISDS;
11049 goto leave;
11051 /* More responses */
11052 if (result->nodesetval->nodeNr > 1) {
11053 char *message_id_locale = _isds_utf82locale((char*) message_id);
11054 isds_printf_message(context,
11055 _("Server did return more responses for ID `%s' "
11056 "on VerifyMessage request"), message_id_locale);
11057 free(message_id_locale);
11058 err = IE_ISDS;
11059 goto leave;
11061 /* One response */
11062 xpath_ctx->node = result->nodesetval->nodeTab[0];
11064 /* Extract the hash */
11065 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11067 leave:
11068 if (err) {
11069 isds_hash_free(hash);
11072 xmlXPathFreeObject(result);
11073 xmlXPathFreeContext(xpath_ctx);
11075 free(code);
11076 free(status_message);
11077 xmlFreeDoc(response);
11079 if (!err)
11080 isds_log(ILF_ISDS, ILL_DEBUG,
11081 _("VerifyMessage request processed by server "
11082 "successfully.\n")
11084 #else /* not HAVE_LIBCURL */
11085 err = IE_NOTSUP;
11086 #endif
11087 return err;
11091 /* Erase message specified by @message_id from long term storage. Other
11092 * message cannot be erased on user request.
11093 * @context is session context
11094 * @message_id is message identifier.
11095 * @incoming is true for incoming message, false for outgoing message.
11096 * @return
11097 * IE_SUCCESS if message has ben removed
11098 * IE_INVAL if message does not exist in long term storage or message
11099 * belongs to different box
11100 * TODO: IE_NOEPRM if user has no permission to erase a message */
11101 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11102 const char *message_id, _Bool incoming) {
11103 isds_error err = IE_SUCCESS;
11104 #if HAVE_LIBCURL
11105 xmlNodePtr request = NULL, node;
11106 xmlNsPtr isds_ns = NULL;
11107 xmlDocPtr response = NULL;
11108 xmlChar *code = NULL, *status_message = NULL;
11109 #endif
11111 if (!context) return IE_INVALID_CONTEXT;
11112 zfree(context->long_message);
11113 if (NULL == message_id) return IE_INVAL;
11115 /* Check if connection is established
11116 * TODO: This check should be done downstairs. */
11117 if (!context->curl) return IE_CONNECTION_CLOSED;
11119 #if HAVE_LIBCURL
11120 /* Build request */
11121 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11122 if (!request) {
11123 isds_log_message(context,
11124 _("Could build EraseMessage request"));
11125 return IE_ERROR;
11127 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11128 if(!isds_ns) {
11129 isds_log_message(context, _("Could not create ISDS name space"));
11130 xmlFreeNode(request);
11131 return IE_ERROR;
11133 xmlSetNs(request, isds_ns);
11135 err = validate_message_id_length(context, (xmlChar *) message_id);
11136 if (err) goto leave;
11137 INSERT_STRING(request, "dmID", message_id);
11139 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11142 /* Send request */
11143 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11144 "message ID %s to ISDS\n"), message_id);
11145 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11146 xmlFreeNode(request); request = NULL;
11148 if (err) {
11149 isds_log(ILF_ISDS, ILL_DEBUG,
11150 _("Processing ISDS response on EraseMessage request "
11151 "failed\n"));
11152 goto leave;
11155 /* Check for response status */
11156 err = isds_response_status(context, SERVICE_DM_INFO, response,
11157 &code, &status_message, NULL);
11158 if (err) {
11159 isds_log(ILF_ISDS, ILL_DEBUG,
11160 _("ISDS response on EraseMessage request is missing "
11161 "status\n"));
11162 goto leave;
11165 /* Check server status code */
11166 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11167 isds_log_message(context, _("Message to erase belongs to other box"));
11168 err = IE_INVAL;
11169 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11170 isds_log_message(context, _("Message to erase is not saved in "
11171 "long term storage or the direction does not match"));
11172 err = IE_INVAL;
11173 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11174 char *code_locale = _isds_utf82locale((char*) code);
11175 char *message_locale = _isds_utf82locale((char*) status_message);
11176 isds_log(ILF_ISDS, ILL_DEBUG,
11177 _("Server refused EraseMessage request "
11178 "(code=%s, message=%s)\n"),
11179 code_locale, message_locale);
11180 isds_log_message(context, message_locale);
11181 free(code_locale);
11182 free(message_locale);
11183 err = IE_ISDS;
11184 goto leave;
11187 leave:
11188 free(code);
11189 free(status_message);
11190 xmlFreeDoc(response);
11191 xmlFreeNode(request);
11193 if (!err)
11194 isds_log(ILF_ISDS, ILL_DEBUG,
11195 _("EraseMessage request processed by server "
11196 "successfully.\n")
11198 #else /* not HAVE_LIBCURL */
11199 err = IE_NOTSUP;
11200 #endif
11201 return err;
11205 /* Mark message as read. This is a transactional commit function to acknowledge
11206 * to ISDS the message has been downloaded and processed by client properly.
11207 * @context is session context
11208 * @message_id is message identifier. */
11209 isds_error isds_mark_message_read(struct isds_ctx *context,
11210 const char *message_id) {
11212 isds_error err = IE_SUCCESS;
11213 #if HAVE_LIBCURL
11214 xmlDocPtr response = NULL;
11215 xmlChar *code = NULL, *status_message = NULL;
11216 #endif
11218 if (!context) return IE_INVALID_CONTEXT;
11219 zfree(context->long_message);
11221 #if HAVE_LIBCURL
11222 /* Do request and check for success */
11223 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11224 BAD_CAST "MarkMessageAsDownloaded", message_id,
11225 &response, NULL, NULL, &code, &status_message);
11227 free(code);
11228 free(status_message);
11229 xmlFreeDoc(response);
11231 if (!err)
11232 isds_log(ILF_ISDS, ILL_DEBUG,
11233 _("MarkMessageAsDownloaded request processed by server "
11234 "successfully.\n")
11236 #else /* not HAVE_LIBCURL */
11237 err = IE_NOTSUP;
11238 #endif
11239 return err;
11243 /* Mark message as received by recipient. This is applicable only to
11244 * commercial message. Use envelope->dmType message member to distinguish
11245 * commercial message from government message. Government message is
11246 * received automatically (by law), commercial message on recipient request.
11247 * @context is session context
11248 * @message_id is message identifier. */
11249 isds_error isds_mark_message_received(struct isds_ctx *context,
11250 const char *message_id) {
11252 isds_error err = IE_SUCCESS;
11253 #if HAVE_LIBCURL
11254 xmlDocPtr response = NULL;
11255 xmlChar *code = NULL, *status_message = NULL;
11256 #endif
11258 if (!context) return IE_INVALID_CONTEXT;
11259 zfree(context->long_message);
11261 #if HAVE_LIBCURL
11262 /* Do request and check for success */
11263 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11264 BAD_CAST "ConfirmDelivery", message_id,
11265 &response, NULL, NULL, &code, &status_message);
11267 free(code);
11268 free(status_message);
11269 xmlFreeDoc(response);
11271 if (!err)
11272 isds_log(ILF_ISDS, ILL_DEBUG,
11273 _("ConfirmDelivery request processed by server "
11274 "successfully.\n")
11276 #else /* not HAVE_LIBCURL */
11277 err = IE_NOTSUP;
11278 #endif
11279 return err;
11283 /* Send document for authorized conversion into Czech POINT system.
11284 * This is public anonymous service, no log-in necessary. Special context is
11285 * used to reuse keep-a-live HTTPS connection.
11286 * @context is Czech POINT session context. DO NOT use context connected to
11287 * ISDS server. Use new context or context used by this function previously.
11288 * @document is document to convert. Only data, data_length, dmFileDescr and
11289 * is_xml members are significant. Be ware that not all document formats can be
11290 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11291 * @id is reallocated identifier assigned by Czech POINT system to
11292 * your document on submit. Use is to tell it to Czech POINT officer.
11293 * @date is reallocated document submit date (submitted documents
11294 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11295 * value. */
11296 isds_error czp_convert_document(struct isds_ctx *context,
11297 const struct isds_document *document,
11298 char **id, struct tm **date) {
11299 isds_error err = IE_SUCCESS;
11300 #if HAVE_LIBCURL
11301 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11302 xmlNodePtr request = NULL, node;
11303 xmlDocPtr response = NULL;
11305 xmlXPathContextPtr xpath_ctx = NULL;
11306 xmlXPathObjectPtr result = NULL;
11307 long int status = -1;
11308 long int *status_ptr = &status;
11309 char *string = NULL;
11310 #endif
11313 if (!context) return IE_INVALID_CONTEXT;
11314 zfree(context->long_message);
11315 if (!document || !id || !date) return IE_INVAL;
11317 if (document->is_xml) {
11318 isds_log_message(context,
11319 _("XML documents cannot be submitted to conversion"));
11320 return IE_NOTSUP;
11323 /* Free output arguments */
11324 zfree(*id);
11325 zfree(*date);
11327 #if HAVE_LIBCURL
11328 /* Store configuration */
11329 context->type = CTX_TYPE_CZP;
11330 free(context->url);
11331 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11332 if (!(context->url))
11333 return IE_NOMEM;
11335 /* Prepare CURL handle if not yet connected */
11336 if (!context->curl) {
11337 context->curl = curl_easy_init();
11338 if (!(context->curl))
11339 return IE_ERROR;
11342 /* Build conversion request */
11343 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11344 if (!request) {
11345 isds_log_message(context,
11346 _("Could not build Czech POINT conversion request"));
11347 return IE_ERROR;
11349 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11350 if(!deposit_ns) {
11351 isds_log_message(context,
11352 _("Could not create Czech POINT deposit name space"));
11353 xmlFreeNode(request);
11354 return IE_ERROR;
11356 xmlSetNs(request, deposit_ns);
11358 /* Insert children. They are in empty namespace! */
11359 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11360 if(!empty_ns) {
11361 isds_log_message(context, _("Could not create empty name space"));
11362 err = IE_ERROR;
11363 goto leave;
11365 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11366 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11367 document->dmFileDescr);
11369 /* Document encoded in Base64 */
11370 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11371 document->data, document->data_length);
11372 if (err) goto leave;
11374 isds_log(ILF_ISDS, ILL_DEBUG,
11375 _("Submitting document for conversion into Czech POINT deposit"));
11377 /* Send conversion request */
11378 err = _czp_czpdeposit(context, request, &response);
11379 xmlFreeNode(request); request = NULL;
11381 if (err) {
11382 czp_do_close_connection(context);
11383 goto leave;
11387 /* Extract response */
11388 xpath_ctx = xmlXPathNewContext(response);
11389 if (!xpath_ctx) {
11390 err = IE_ERROR;
11391 goto leave;
11393 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11394 err = IE_ERROR;
11395 goto leave;
11397 result = xmlXPathEvalExpression(
11398 BAD_CAST "/deposit:saveDocumentResponse/return",
11399 xpath_ctx);
11400 if (!result) {
11401 err = IE_ERROR;
11402 goto leave;
11404 /* Empty response */
11405 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11406 isds_printf_message(context,
11407 _("Missing `return' element in Czech POINT deposit response"));
11408 err = IE_ISDS;
11409 goto leave;
11411 /* More responses */
11412 if (result->nodesetval->nodeNr > 1) {
11413 isds_printf_message(context,
11414 _("Multiple `return' element in Czech POINT deposit response"));
11415 err = IE_ISDS;
11416 goto leave;
11418 /* One response */
11419 xpath_ctx->node = result->nodesetval->nodeTab[0];
11421 /* Get status */
11422 EXTRACT_LONGINT("status", status_ptr, 1);
11423 if (status) {
11424 EXTRACT_STRING("statusMsg", string);
11425 char *string_locale = _isds_utf82locale(string);
11426 isds_printf_message(context,
11427 _("Czech POINT deposit refused document for conversion "
11428 "(code=%ld, message=%s)"),
11429 status, string_locale);
11430 free(string_locale);
11431 err = IE_ISDS;
11432 goto leave;
11435 /* Get document ID */
11436 EXTRACT_STRING("documentID", *id);
11438 /* Get submit date */
11439 EXTRACT_STRING("dateInserted", string);
11440 if (string) {
11441 *date = calloc(1, sizeof(**date));
11442 if (!*date) {
11443 err = IE_NOMEM;
11444 goto leave;
11446 err = _isds_datestring2tm((xmlChar *)string, *date);
11447 if (err) {
11448 if (err == IE_NOTSUP) {
11449 err = IE_ISDS;
11450 char *string_locale = _isds_utf82locale(string);
11451 isds_printf_message(context,
11452 _("Invalid dateInserted value: %s"), string_locale);
11453 free(string_locale);
11455 goto leave;
11459 leave:
11460 free(string);
11461 xmlXPathFreeObject(result);
11462 xmlXPathFreeContext(xpath_ctx);
11464 xmlFreeDoc(response);
11465 xmlFreeNode(request);
11467 if (!err) {
11468 char *id_locale = _isds_utf82locale((char *) *id);
11469 isds_log(ILF_ISDS, ILL_DEBUG,
11470 _("Document %s has been submitted for conversion "
11471 "to server successfully\n"), id_locale);
11472 free(id_locale);
11474 #else /* not HAVE_LIBCURL */
11475 err = IE_NOTSUP;
11476 #endif
11477 return err;
11481 /* Close possibly opened connection to Czech POINT document deposit.
11482 * @context is Czech POINT session context. */
11483 isds_error czp_close_connection(struct isds_ctx *context) {
11484 if (!context) return IE_INVALID_CONTEXT;
11485 zfree(context->long_message);
11486 #if HAVE_LIBCURL
11487 return czp_do_close_connection(context);
11488 #else
11489 return IE_NOTSUP;
11490 #endif
11494 /* Send request for new box creation in testing ISDS instance.
11495 * It's not possible to request for a production box currently, as it
11496 * communicates via e-mail.
11497 * XXX: This function does not work either. Server complains about invalid
11498 * e-mail address.
11499 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11500 * this function
11501 * @context is special session context for box creation request. DO NOT use
11502 * standard context as it could reveal your password. Use fresh new context or
11503 * context previously used by this function.
11504 * @box is box description to create including single primary user (in case of
11505 * FO box type). It outputs box ID assigned by ISDS in dbID element.
11506 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11507 * box, or contact address of PFO box owner). The email member is mandatory as
11508 * it will be used to deliver credentials.
11509 * @former_names is former name of box owner. Pass NULL if you don't care.
11510 * @approval is optional external approval of box manipulation
11511 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11512 * NULL, if you don't care.*/
11513 isds_error isds_request_new_testing_box(struct isds_ctx *context,
11514 struct isds_DbOwnerInfo *box, const struct isds_list *users,
11515 const char *former_names, const struct isds_approval *approval,
11516 char **refnumber) {
11517 isds_error err = IE_SUCCESS;
11518 #if HAVE_LIBCURL
11519 xmlNodePtr request = NULL;
11520 xmlDocPtr response = NULL;
11521 xmlXPathContextPtr xpath_ctx = NULL;
11522 xmlXPathObjectPtr result = NULL;
11523 #endif
11526 if (!context) return IE_INVALID_CONTEXT;
11527 zfree(context->long_message);
11528 if (!box) return IE_INVAL;
11530 #if HAVE_LIBCURL
11531 if (!box->email || box->email[0] == '\0') {
11532 isds_log_message(context, _("E-mail field is mandatory"));
11533 return IE_INVAL;
11536 /* Scratch box ID */
11537 zfree(box->dbID);
11539 /* Store configuration */
11540 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
11541 free(context->url);
11542 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
11543 if (!(context->url))
11544 return IE_NOMEM;
11546 /* Prepare CURL handle if not yet connected */
11547 if (!context->curl) {
11548 context->curl = curl_easy_init();
11549 if (!(context->curl))
11550 return IE_ERROR;
11553 /* Build CreateDataBox request */
11554 err = build_CreateDBInput_request(context,
11555 &request, BAD_CAST "CreateDataBox",
11556 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
11557 if (err) goto leave;
11559 /* Send it to server and process response */
11560 err = send_destroy_request_check_response(context,
11561 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
11562 &response, (xmlChar **) refnumber, NULL);
11563 if (err) goto leave;
11565 /* Extract box ID */
11566 xpath_ctx = xmlXPathNewContext(response);
11567 if (!xpath_ctx) {
11568 err = IE_ERROR;
11569 goto leave;
11571 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11572 err = IE_ERROR;
11573 goto leave;
11575 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
11577 leave:
11578 xmlXPathFreeObject(result);
11579 xmlXPathFreeContext(xpath_ctx);
11580 xmlFreeDoc(response);
11581 xmlFreeNode(request);
11583 if (!err) {
11584 isds_log(ILF_ISDS, ILL_DEBUG,
11585 _("CreateDataBox request processed by server successfully.\n"));
11587 #else /* not HAVE_LIBCURL */
11588 err = IE_NOTSUP;
11589 #endif
11591 return err;
11595 /* Submit CMS signed message to ISDS to verify its originality. This is
11596 * stronger form of isds_verify_message_hash() because ISDS does more checks
11597 * than simple one (potentialy old weak) hash comparison.
11598 * @context is session context
11599 * @message is memory with raw CMS signed message bit stream
11600 * @length is @message size in bytes
11601 * @return
11602 * IE_SUCCESS if message originates in ISDS
11603 * IE_NOTEQUAL if message is unknown to ISDS
11604 * other code for other errors */
11605 isds_error isds_authenticate_message(struct isds_ctx *context,
11606 const void *message, size_t length) {
11607 isds_error err = IE_SUCCESS;
11608 #if HAVE_LIBCURL
11609 xmlNsPtr isds_ns = NULL;
11610 xmlNodePtr request = NULL;
11611 xmlDocPtr response = NULL;
11612 xmlXPathContextPtr xpath_ctx = NULL;
11613 xmlXPathObjectPtr result = NULL;
11614 _Bool *authentic = NULL;
11615 #endif
11617 if (!context) return IE_INVALID_CONTEXT;
11618 zfree(context->long_message);
11619 if (!message || length == 0) return IE_INVAL;
11621 #if HAVE_LIBCURL
11622 /* Check if connection is established
11623 * TODO: This check should be done downstairs. */
11624 if (!context->curl) return IE_CONNECTION_CLOSED;
11627 /* Build AuthenticateMessage request */
11628 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11629 if (!request) {
11630 isds_log_message(context,
11631 _("Could not build AuthenticateMessage request"));
11632 return IE_ERROR;
11634 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11635 if(!isds_ns) {
11636 isds_log_message(context, _("Could not create ISDS name space"));
11637 xmlFreeNode(request);
11638 return IE_ERROR;
11640 xmlSetNs(request, isds_ns);
11642 /* Insert Base64 encoded message */
11643 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11644 message, length);
11645 if (err) goto leave;
11647 /* Send request to server and process response */
11648 err = send_destroy_request_check_response(context,
11649 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11650 &response, NULL, NULL);
11651 if (err) goto leave;
11654 /* ISDS has decided */
11655 xpath_ctx = xmlXPathNewContext(response);
11656 if (!xpath_ctx) {
11657 err = IE_ERROR;
11658 goto leave;
11660 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11661 err = IE_ERROR;
11662 goto leave;
11665 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11667 if (!authentic) {
11668 isds_log_message(context,
11669 _("Server did not return any response on "
11670 "AuthenticateMessage request"));
11671 err = IE_ISDS;
11672 goto leave;
11674 if (*authentic) {
11675 isds_log(ILF_ISDS, ILL_DEBUG,
11676 _("ISDS authenticated the message successfully\n"));
11677 } else {
11678 isds_log_message(context, _("ISDS does not know the message"));
11679 err = IE_NOTEQUAL;
11683 leave:
11684 free(authentic);
11685 xmlXPathFreeObject(result);
11686 xmlXPathFreeContext(xpath_ctx);
11688 xmlFreeDoc(response);
11689 xmlFreeNode(request);
11690 #else /* not HAVE_LIBCURL */
11691 err = IE_NOTSUP;
11692 #endif
11694 return err;
11698 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11699 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11700 * be re-signed.
11701 * @context is session context
11702 * @input_data is memory with raw CMS signed message or delivery info bit
11703 * stream to re-sign
11704 * @input_length is @input_data size in bytes
11705 * @output_data is pointer to auto-allocated memory where to store re-signed
11706 * input data blob. Caller must free it.
11707 * @output_data is pointer where to store @output_data size in bytes
11708 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11709 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11710 * @return
11711 * IE_SUCCESS if CMS blob has been re-signed successfully
11712 * other code for other errors */
11713 isds_error isds_resign_message(struct isds_ctx *context,
11714 const void *input_data, size_t input_length,
11715 void **output_data, size_t *output_length, struct tm **valid_to) {
11716 isds_error err = IE_SUCCESS;
11717 #if HAVE_LIBCURL
11718 xmlNsPtr isds_ns = NULL;
11719 xmlNodePtr request = NULL;
11720 xmlDocPtr response = NULL;
11721 xmlXPathContextPtr xpath_ctx = NULL;
11722 xmlXPathObjectPtr result = NULL;
11723 char *string = NULL;
11724 const xmlChar *codes[] = {
11725 BAD_CAST "2200",
11726 BAD_CAST "2201",
11727 BAD_CAST "2204",
11728 BAD_CAST "2207",
11729 NULL
11731 const char *meanings[] = {
11732 "Message is bad",
11733 "Message is not original",
11734 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11735 "Time stamp could not been generated in time"
11737 const isds_error errors[] = {
11738 IE_INVAL,
11739 IE_NOTUNIQ,
11740 IE_INVAL,
11741 IE_ISDS,
11743 struct code_map_isds_error map = {
11744 .codes = codes,
11745 .meanings = meanings,
11746 .errors = errors
11748 #endif
11750 if (NULL != output_data) *output_data = NULL;
11751 if (NULL != output_length) *output_length = 0;
11752 if (NULL != valid_to) *valid_to = NULL;
11754 if (NULL == context) return IE_INVALID_CONTEXT;
11755 zfree(context->long_message);
11756 if (NULL == input_data || 0 == input_length) {
11757 isds_log_message(context, _("Empty CMS blob on input"));
11758 return IE_INVAL;
11760 if (NULL == output_data || NULL == output_length) {
11761 isds_log_message(context,
11762 _("NULL pointer provided for output CMS blob"));
11763 return IE_INVAL;
11766 #if HAVE_LIBCURL
11767 /* Check if connection is established
11768 * TODO: This check should be done downstairs. */
11769 if (!context->curl) return IE_CONNECTION_CLOSED;
11772 /* Build Re-signISDSDocument request */
11773 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11774 if (!request) {
11775 isds_log_message(context,
11776 _("Could not build Re-signISDSDocument request"));
11777 return IE_ERROR;
11779 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11780 if(!isds_ns) {
11781 isds_log_message(context, _("Could not create ISDS name space"));
11782 xmlFreeNode(request);
11783 return IE_ERROR;
11785 xmlSetNs(request, isds_ns);
11787 /* Insert Base64 encoded CMS blob */
11788 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11789 input_data, input_length);
11790 if (err) goto leave;
11792 /* Send request to server and process response */
11793 err = send_destroy_request_check_response(context,
11794 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11795 &response, NULL, &map);
11796 if (err) goto leave;
11799 /* Extract re-signed data */
11800 xpath_ctx = xmlXPathNewContext(response);
11801 if (!xpath_ctx) {
11802 err = IE_ERROR;
11803 goto leave;
11805 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11806 err = IE_ERROR;
11807 goto leave;
11809 result = xmlXPathEvalExpression(
11810 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11811 if (!result) {
11812 err = IE_ERROR;
11813 goto leave;
11815 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11816 isds_log_message(context,
11817 _("Missing Re-signISDSDocumentResponse element"));
11818 err = IE_ISDS;
11819 goto leave;
11821 if (result->nodesetval->nodeNr > 1) {
11822 isds_log_message(context,
11823 _("Multiple Re-signISDSDocumentResponse element"));
11824 err = IE_ISDS;
11825 goto leave;
11827 xpath_ctx->node = result->nodesetval->nodeTab[0];
11828 xmlXPathFreeObject(result); result = NULL;
11830 EXTRACT_STRING("isds:dmResultDoc", string);
11831 /* Decode non-empty data */
11832 if (NULL != string && string[0] != '\0') {
11833 *output_length = _isds_b64decode(string, output_data);
11834 if (*output_length == (size_t) -1) {
11835 isds_log_message(context,
11836 _("Error while Base64-decoding re-signed data"));
11837 err = IE_ERROR;
11838 goto leave;
11840 } else {
11841 isds_log_message(context, _("Server did not send re-signed data"));
11842 err = IE_ISDS;
11843 goto leave;
11845 zfree(string);
11847 if (NULL != valid_to) {
11848 /* Get time stamp expiration date */
11849 EXTRACT_STRING("isds:dmValidTo", string);
11850 if (NULL != string) {
11851 *valid_to = calloc(1, sizeof(**valid_to));
11852 if (!*valid_to) {
11853 err = IE_NOMEM;
11854 goto leave;
11856 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11857 if (err) {
11858 if (err == IE_NOTSUP) {
11859 err = IE_ISDS;
11860 char *string_locale = _isds_utf82locale(string);
11861 isds_printf_message(context,
11862 _("Invalid dmValidTo value: %s"), string_locale);
11863 free(string_locale);
11865 goto leave;
11870 leave:
11871 free(string);
11873 xmlXPathFreeObject(result);
11874 xmlXPathFreeContext(xpath_ctx);
11876 xmlFreeDoc(response);
11877 xmlFreeNode(request);
11878 #else /* not HAVE_LIBCURL */
11879 err = IE_NOTSUP;
11880 #endif
11882 return err;
11885 #undef INSERT_ELEMENT
11886 #undef CHECK_FOR_STRING_LENGTH
11887 #undef INSERT_STRING_ATTRIBUTE
11888 #undef INSERT_ULONGINTNOPTR
11889 #undef INSERT_ULONGINT
11890 #undef INSERT_LONGINT
11891 #undef INSERT_BOOLEAN
11892 #undef INSERT_SCALAR_BOOLEAN
11893 #undef INSERT_STRING
11894 #undef INSERT_STRING_WITH_NS
11895 #undef EXTRACT_STRING_ATTRIBUTE
11896 #undef EXTRACT_ULONGINT
11897 #undef EXTRACT_LONGINT
11898 #undef EXTRACT_BOOLEAN
11899 #undef EXTRACT_STRING
11902 /* Compute hash of message from raw representation and store it into envelope.
11903 * Original hash structure will be destroyed in envelope.
11904 * @context is session context
11905 * @message is message carrying raw XML message blob
11906 * @algorithm is desired hash algorithm to use */
11907 isds_error isds_compute_message_hash(struct isds_ctx *context,
11908 struct isds_message *message, const isds_hash_algorithm algorithm) {
11909 isds_error err = IE_SUCCESS;
11910 const char *nsuri;
11911 void *xml_stream = NULL;
11912 size_t xml_stream_length;
11913 size_t phys_start, phys_end;
11914 char *phys_path = NULL;
11915 struct isds_hash *new_hash = NULL;
11918 if (!context) return IE_INVALID_CONTEXT;
11919 zfree(context->long_message);
11920 if (!message) return IE_INVAL;
11922 if (!message->raw) {
11923 isds_log_message(context,
11924 _("Message does not carry raw representation"));
11925 return IE_INVAL;
11928 switch (message->raw_type) {
11929 case RAWTYPE_INCOMING_MESSAGE:
11930 nsuri = ISDS_NS;
11931 xml_stream = message->raw;
11932 xml_stream_length = message->raw_length;
11933 break;
11935 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11936 nsuri = SISDS_INCOMING_NS;
11937 xml_stream = message->raw;
11938 xml_stream_length = message->raw_length;
11939 break;
11941 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11942 nsuri = SISDS_INCOMING_NS;
11943 err = _isds_extract_cms_data(context,
11944 message->raw, message->raw_length,
11945 &xml_stream, &xml_stream_length);
11946 if (err) goto leave;
11947 break;
11949 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11950 nsuri = SISDS_OUTGOING_NS;
11951 xml_stream = message->raw;
11952 xml_stream_length = message->raw_length;
11953 break;
11955 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11956 nsuri = SISDS_OUTGOING_NS;
11957 err = _isds_extract_cms_data(context,
11958 message->raw, message->raw_length,
11959 &xml_stream, &xml_stream_length);
11960 if (err) goto leave;
11961 break;
11963 default:
11964 isds_log_message(context, _("Bad raw representation type"));
11965 return IE_INVAL;
11966 break;
11970 /* XXX: Hash is computed from original string representing isds:dmDm
11971 * subtree. That means no encoding, white space, xmlns attributes changes.
11972 * In other words, input for hash can be invalid XML stream. */
11973 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
11974 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11975 PHYSXML_ELEMENT_SEPARATOR,
11976 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
11977 PHYSXML_ELEMENT_SEPARATOR
11978 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
11979 err = IE_NOMEM;
11980 goto leave;
11982 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11983 phys_path, &phys_start, &phys_end);
11984 zfree(phys_path);
11985 if (err) {
11986 isds_log_message(context,
11987 _("Substring with isds:dmDM element could not be located "
11988 "in raw message"));
11989 goto leave;
11993 /* Compute hash */
11994 new_hash = calloc(1, sizeof(*new_hash));
11995 if (!new_hash) {
11996 err = IE_NOMEM;
11997 goto leave;
11999 new_hash->algorithm = algorithm;
12000 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
12001 new_hash);
12002 if (err) {
12003 isds_log_message(context, _("Could not compute message hash"));
12004 goto leave;
12007 /* Save computed hash */
12008 if (!message->envelope) {
12009 message->envelope = calloc(1, sizeof(*message->envelope));
12010 if (!message->envelope) {
12011 err = IE_NOMEM;
12012 goto leave;
12015 isds_hash_free(&message->envelope->hash);
12016 message->envelope->hash = new_hash;
12018 leave:
12019 if (err) {
12020 isds_hash_free(&new_hash);
12023 free(phys_path);
12024 if (xml_stream != message->raw) free(xml_stream);
12025 return err;
12029 /* Compare two hashes.
12030 * @h1 is first hash
12031 * @h2 is another hash
12032 * @return
12033 * IE_SUCCESS if hashes equal
12034 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12035 * IE_ENUM if not comparable, but both structures defined
12036 * IE_INVAL if some of the structures are undefined (NULL)
12037 * IE_ERROR if internal error occurs */
12038 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
12039 if (h1 == NULL || h2 == NULL) return IE_INVAL;
12040 if (h1->algorithm != h2->algorithm) return IE_ENUM;
12041 if (h1->length != h2->length) return IE_ERROR;
12042 if (h1->length > 0 && !h1->value) return IE_ERROR;
12043 if (h2->length > 0 && !h2->value) return IE_ERROR;
12045 for (size_t i = 0; i < h1->length; i++) {
12046 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12047 return IE_NOTEQUAL;
12049 return IE_SUCCESS;
12053 /* Check message has gone through ISDS by comparing message hash stored in
12054 * ISDS and locally computed hash. You must provide message with valid raw
12055 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12056 * This is convenient wrapper for isds_download_message_hash(),
12057 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12058 * @context is session context
12059 * @message is message with valid raw and envelope member; envelope->hash
12060 * member will be changed during function run. Use envelope on heap only.
12061 * @return
12062 * IE_SUCCESS if message originates in ISDS
12063 * IE_NOTEQUAL if message is unknown to ISDS
12064 * other code for other errors */
12065 isds_error isds_verify_message_hash(struct isds_ctx *context,
12066 struct isds_message *message) {
12067 isds_error err = IE_SUCCESS;
12068 struct isds_hash *downloaded_hash = NULL;
12070 if (!context) return IE_INVALID_CONTEXT;
12071 zfree(context->long_message);
12072 if (!message) return IE_INVAL;
12074 if (!message->envelope) {
12075 isds_log_message(context,
12076 _("Given message structure is missing envelope"));
12077 return IE_INVAL;
12079 if (!message->raw) {
12080 isds_log_message(context,
12081 _("Given message structure is missing raw representation"));
12082 return IE_INVAL;
12085 err = isds_download_message_hash(context, message->envelope->dmID,
12086 &downloaded_hash);
12087 if (err) goto leave;
12089 err = isds_compute_message_hash(context, message,
12090 downloaded_hash->algorithm);
12091 if (err) goto leave;
12093 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12095 leave:
12096 isds_hash_free(&downloaded_hash);
12097 return err;
12101 /* Search for document by document ID in list of documents. IDs are compared
12102 * as UTF-8 string.
12103 * @documents is list of isds_documents
12104 * @id is document identifier
12105 * @return first matching document or NULL. */
12106 const struct isds_document *isds_find_document_by_id(
12107 const struct isds_list *documents, const char *id) {
12108 const struct isds_list *item;
12109 const struct isds_document *document;
12111 for (item = documents; item; item = item->next) {
12112 document = (struct isds_document *) item->data;
12113 if (!document) continue;
12115 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12116 return document;
12119 return NULL;
12123 /* Normalize @mime_type to be proper MIME type.
12124 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12125 * guess regular MIME type (e.g. "application/pdf").
12126 * @mime_type is UTF-8 encoded MIME type to fix
12127 * @return original @mime_type if no better interpretation exists, or
12128 * constant static UTF-8 encoded string with proper MIME type. */
12129 const char *isds_normalize_mime_type(const char *mime_type) {
12130 if (!mime_type) return NULL;
12132 for (size_t offset = 0;
12133 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12134 offset += 2) {
12135 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12136 extension_map_mime[offset]))
12137 return (const char *) extension_map_mime[offset + 1];
12140 return mime_type;
12144 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12145 struct isds_message **message);
12146 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12147 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12148 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12149 struct isds_address **address);
12151 int isds_message_free(struct isds_message **message);
12152 int isds_address_free(struct isds_address **address);
12156 /* Makes known all relevant namespaces to given XPath context
12157 * @xpath_ctx is XPath context
12158 * @message_ns selects proper message name space. Unsigned and signed
12159 * messages and delivery info's differ in prefix and URI. */
12160 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12161 const message_ns_type message_ns) {
12162 const xmlChar *message_namespace = NULL;
12164 if (!xpath_ctx) return IE_ERROR;
12166 switch(message_ns) {
12167 case MESSAGE_NS_1:
12168 message_namespace = BAD_CAST ISDS1_NS; break;
12169 case MESSAGE_NS_UNSIGNED:
12170 message_namespace = BAD_CAST ISDS_NS; break;
12171 case MESSAGE_NS_SIGNED_INCOMING:
12172 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12173 case MESSAGE_NS_SIGNED_OUTGOING:
12174 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12175 case MESSAGE_NS_SIGNED_DELIVERY:
12176 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12177 default:
12178 return IE_ENUM;
12181 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12182 return IE_ERROR;
12183 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12184 return IE_ERROR;
12185 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12186 return IE_ERROR;
12187 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12188 return IE_ERROR;
12189 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12190 return IE_ERROR;
12191 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12192 return IE_ERROR;
12193 return IE_SUCCESS;