Serialize all mandatory tdbOwnerInfo members
[libisds.git] / src / isds.c
blob4794fd9f0e20c56cfd6f9ca6b540115b8cc4c5b3
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)->adCode);
191 free((*address)->adCity);
192 free((*address)->adDistrict);
193 free((*address)->adStreet);
194 free((*address)->adNumberInStreet);
195 free((*address)->adNumberInMunicipality);
196 free((*address)->adZipCode);
197 free((*address)->adState);
199 free(*address);
200 *address = NULL;
204 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
205 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
206 if (!db_owner_info || !*db_owner_info) return;
208 free((*db_owner_info)->dbID);
209 free((*db_owner_info)->dbType);
210 free((*db_owner_info)->ic);
211 isds_PersonName_free(&((*db_owner_info)->personName));
212 free((*db_owner_info)->firmName);
213 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
214 isds_Address_free(&((*db_owner_info)->address));
215 free((*db_owner_info)->nationality);
216 free((*db_owner_info)->email);
217 free((*db_owner_info)->telNumber);
218 free((*db_owner_info)->identifier);
219 free((*db_owner_info)->aifoIsds);
220 free((*db_owner_info)->registryCode);
221 free((*db_owner_info)->dbState);
222 free((*db_owner_info)->dbEffectiveOVM);
223 free((*db_owner_info)->dbOpenAddressing);
225 free(*db_owner_info);
226 *db_owner_info = NULL;
229 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
230 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
231 if (!db_user_info || !*db_user_info) return;
233 free((*db_user_info)->userID);
234 free((*db_user_info)->userType);
235 free((*db_user_info)->userPrivils);
236 isds_PersonName_free(&((*db_user_info)->personName));
237 isds_Address_free(&((*db_user_info)->address));
238 free((*db_user_info)->biDate);
239 free((*db_user_info)->ic);
240 free((*db_user_info)->firmName);
241 free((*db_user_info)->caStreet);
242 free((*db_user_info)->caCity);
243 free((*db_user_info)->caZipCode);
244 free((*db_user_info)->caState);
245 free((*db_user_info)->aifo_ticket);
247 zfree(*db_user_info);
251 /* Deallocate struct isds_event recursively and NULL it */
252 void isds_event_free(struct isds_event **event) {
253 if (!event || !*event) return;
255 free((*event)->time);
256 free((*event)->type);
257 free((*event)->description);
258 zfree(*event);
262 /* Deallocate struct isds_envelope recursively and NULL it */
263 void isds_envelope_free(struct isds_envelope **envelope) {
264 if (!envelope || !*envelope) return;
266 free((*envelope)->dmID);
267 free((*envelope)->dbIDSender);
268 free((*envelope)->dmSender);
269 free((*envelope)->dmSenderAddress);
270 free((*envelope)->dmSenderType);
271 free((*envelope)->dmRecipient);
272 free((*envelope)->dmRecipientAddress);
273 free((*envelope)->dmAmbiguousRecipient);
274 free((*envelope)->dmType);
276 free((*envelope)->dmOrdinal);
277 free((*envelope)->dmMessageStatus);
278 free((*envelope)->dmDeliveryTime);
279 free((*envelope)->dmAcceptanceTime);
280 isds_hash_free(&(*envelope)->hash);
281 free((*envelope)->timestamp);
282 isds_list_free(&(*envelope)->events);
284 free((*envelope)->dmSenderOrgUnit);
285 free((*envelope)->dmSenderOrgUnitNum);
286 free((*envelope)->dbIDRecipient);
287 free((*envelope)->dmRecipientOrgUnit);
288 free((*envelope)->dmRecipientOrgUnitNum);
289 free((*envelope)->dmToHands);
290 free((*envelope)->dmAnnotation);
291 free((*envelope)->dmRecipientRefNumber);
292 free((*envelope)->dmSenderRefNumber);
293 free((*envelope)->dmRecipientIdent);
294 free((*envelope)->dmSenderIdent);
296 free((*envelope)->dmLegalTitleLaw);
297 free((*envelope)->dmLegalTitleYear);
298 free((*envelope)->dmLegalTitleSect);
299 free((*envelope)->dmLegalTitlePar);
300 free((*envelope)->dmLegalTitlePoint);
302 free((*envelope)->dmPersonalDelivery);
303 free((*envelope)->dmAllowSubstDelivery);
305 free((*envelope)->dmOVM);
306 free((*envelope)->dmPublishOwnID);
308 free(*envelope);
309 *envelope = NULL;
313 /* Deallocate struct isds_message recursively and NULL it */
314 void isds_message_free(struct isds_message **message) {
315 if (!message || !*message) return;
317 free((*message)->raw);
318 isds_envelope_free(&((*message)->envelope));
319 isds_list_free(&((*message)->documents));
320 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
322 free(*message);
323 *message = NULL;
327 /* Deallocate struct isds_document recursively and NULL it */
328 void isds_document_free(struct isds_document **document) {
329 if (!document || !*document) return;
331 if (!(*document)->is_xml) {
332 free((*document)->data);
334 free((*document)->dmMimeType);
335 free((*document)->dmFileGuid);
336 free((*document)->dmUpFileGuid);
337 free((*document)->dmFileDescr);
338 free((*document)->dmFormat);
340 free(*document);
341 *document = NULL;
345 /* Deallocate struct isds_message_copy recursively and NULL it */
346 void isds_message_copy_free(struct isds_message_copy **copy) {
347 if (!copy || !*copy) return;
349 free((*copy)->dbIDRecipient);
350 free((*copy)->dmRecipientOrgUnit);
351 free((*copy)->dmRecipientOrgUnitNum);
352 free((*copy)->dmToHands);
354 free((*copy)->dmStatus);
355 free((*copy)->dmID);
357 zfree(*copy);
361 /* Deallocate struct isds_message_status_change recursively and NULL it */
362 void isds_message_status_change_free(
363 struct isds_message_status_change **message_status_change) {
364 if (!message_status_change || !*message_status_change) return;
366 free((*message_status_change)->dmID);
367 free((*message_status_change)->time);
368 free((*message_status_change)->dmMessageStatus);
370 zfree(*message_status_change);
374 /* Deallocate struct isds_approval recursively and NULL it */
375 void isds_approval_free(struct isds_approval **approval) {
376 if (!approval || !*approval) return;
378 free((*approval)->refference);
380 zfree(*approval);
384 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
385 * The email string is deallocated too. */
386 void isds_credentials_delivery_free(
387 struct isds_credentials_delivery **credentials_delivery) {
388 if (!credentials_delivery || !*credentials_delivery) return;
390 free((*credentials_delivery)->email);
391 free((*credentials_delivery)->token);
392 free((*credentials_delivery)->new_user_name);
394 zfree(*credentials_delivery);
398 /* Deallocate struct isds_commercial_permission recursively and NULL it */
399 void isds_commercial_permission_free(
400 struct isds_commercial_permission **permission) {
401 if (NULL == permission || NULL == *permission) return;
403 free((*permission)->recipient);
404 free((*permission)->payer);
405 free((*permission)->expiration);
406 free((*permission)->count);
407 free((*permission)->reply_identifier);
409 zfree(*permission);
413 /* Deallocate struct isds_credit_event recursively and NULL it */
414 void isds_credit_event_free(struct isds_credit_event **event) {
415 if (NULL == event || NULL == *event) return;
417 free((*event)->time);
418 switch ((*event)->type) {
419 case ISDS_CREDIT_CHARGED:
420 free((*event)->details.charged.transaction);
421 break;
422 case ISDS_CREDIT_DISCHARGED:
423 free((*event)->details.discharged.transaction);
424 break;
425 case ISDS_CREDIT_MESSAGE_SENT:
426 free((*event)->details.message_sent.recipient);
427 free((*event)->details.message_sent.message_id);
428 break;
429 case ISDS_CREDIT_STORAGE_SET:
430 free((*event)->details.storage_set.new_valid_from);
431 free((*event)->details.storage_set.new_valid_to);
432 free((*event)->details.storage_set.old_capacity);
433 free((*event)->details.storage_set.old_valid_from);
434 free((*event)->details.storage_set.old_valid_to);
435 free((*event)->details.storage_set.initiator);
436 break;
437 case ISDS_CREDIT_EXPIRED:
438 break;
441 zfree(*event);
445 /* Deallocate struct isds_fulltext_result recursively and NULL it */
446 void isds_fulltext_result_free(
447 struct isds_fulltext_result **result) {
448 if (NULL == result || NULL == *result) return;
450 free((*result)->dbID);
451 free((*result)->name);
452 isds_list_free(&((*result)->name_match_start));
453 isds_list_free(&((*result)->name_match_end));
454 free((*result)->address);
455 isds_list_free(&((*result)->address_match_start));
456 isds_list_free(&((*result)->address_match_end));
457 free((*result)->ic);
458 free((*result)->biDate);
460 zfree(*result);
464 /* *DUP_OR_ERROR macros needs error label */
465 #define STRDUP_OR_ERROR(new, template) { \
466 if (!template) { \
467 (new) = NULL; \
468 } else { \
469 (new) = strdup(template); \
470 if (!new) goto error; \
474 #define FLATDUP_OR_ERROR(new, template) { \
475 if (!template) { \
476 (new) = NULL; \
477 } else { \
478 (new) = malloc(sizeof(*(new))); \
479 if (!new) goto error; \
480 memcpy((new), (template), sizeof(*(template))); \
484 /* Copy structure isds_pki_credentials recursively. */
485 struct isds_pki_credentials *isds_pki_credentials_duplicate(
486 const struct isds_pki_credentials *template) {
487 struct isds_pki_credentials *new = NULL;
489 if(!template) return NULL;
491 new = calloc(1, sizeof(*new));
492 if (!new) return NULL;
494 STRDUP_OR_ERROR(new->engine, template->engine);
495 new->certificate_format = template->certificate_format;
496 STRDUP_OR_ERROR(new->certificate, template->certificate);
497 new->key_format = template->key_format;
498 STRDUP_OR_ERROR(new->key, template->key);
499 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
501 return new;
503 error:
504 isds_pki_credentials_free(&new);
505 return NULL;
509 /* Copy structure isds_PersonName recursively */
510 struct isds_PersonName *isds_PersonName_duplicate(
511 const struct isds_PersonName *src) {
512 struct isds_PersonName *new = NULL;
514 if (!src) return NULL;
516 new = calloc(1, sizeof(*new));
517 if (!new) return NULL;
519 STRDUP_OR_ERROR(new->pnFirstName, src->pnFirstName);
520 STRDUP_OR_ERROR(new->pnMiddleName, src->pnMiddleName);
521 STRDUP_OR_ERROR(new->pnLastName, src->pnLastName);
522 STRDUP_OR_ERROR(new->pnLastNameAtBirth, src->pnLastNameAtBirth);
524 return new;
526 error:
527 isds_PersonName_free(&new);
528 return NULL;
532 /* Copy structure isds_BirthInfo recursively */
533 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
534 const struct isds_BirthInfo *template) {
535 struct isds_BirthInfo *new = NULL;
537 if (!template) return NULL;
539 new = calloc(1, sizeof(*new));
540 if (!new) return NULL;
542 FLATDUP_OR_ERROR(new->biDate, template->biDate);
543 STRDUP_OR_ERROR(new->biCity, template->biCity);
544 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
545 STRDUP_OR_ERROR(new->biState, template->biState);
547 return new;
549 error:
550 isds_BirthInfo_free(&new);
551 return NULL;
555 /* Copy structure isds_Address recursively */
556 struct isds_Address *isds_Address_duplicate(
557 const struct isds_Address *src) {
558 struct isds_Address *new = NULL;
560 if (!src) return NULL;
562 new = calloc(1, sizeof(*new));
563 if (!new) return NULL;
565 FLATDUP_OR_ERROR(new->adCode, src->adCode);
566 STRDUP_OR_ERROR(new->adCity, src->adCity);
567 STRDUP_OR_ERROR(new->adDistrict, src->adDistrict);
568 STRDUP_OR_ERROR(new->adStreet, src->adStreet);
569 STRDUP_OR_ERROR(new->adNumberInStreet, src->adNumberInStreet);
570 STRDUP_OR_ERROR(new->adNumberInMunicipality,
571 src->adNumberInMunicipality);
572 STRDUP_OR_ERROR(new->adZipCode, src->adZipCode);
573 STRDUP_OR_ERROR(new->adState, src->adState);
575 return new;
577 error:
578 isds_Address_free(&new);
579 return NULL;
583 /* Copy structure isds_DbOwnerInfo recursively */
584 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
585 const struct isds_DbOwnerInfo *src) {
586 struct isds_DbOwnerInfo *new = NULL;
587 if (!src) return NULL;
589 new = calloc(1, sizeof(*new));
590 if (!new) return NULL;
592 STRDUP_OR_ERROR(new->dbID, src->dbID);
593 FLATDUP_OR_ERROR(new->dbType, src->dbType);
594 STRDUP_OR_ERROR(new->ic, src->ic);
596 if (src->personName) {
597 if (!(new->personName =
598 isds_PersonName_duplicate(src->personName)))
599 goto error;
602 STRDUP_OR_ERROR(new->firmName, src->firmName);
604 if (src->birthInfo) {
605 if (!(new->birthInfo =
606 isds_BirthInfo_duplicate(src->birthInfo)))
607 goto error;
610 if (src->address) {
611 if (!(new->address = isds_Address_duplicate(src->address)))
612 goto error;
615 STRDUP_OR_ERROR(new->nationality, src->nationality);
616 STRDUP_OR_ERROR(new->email, src->email);
617 STRDUP_OR_ERROR(new->telNumber, src->telNumber);
618 STRDUP_OR_ERROR(new->identifier, src->identifier);
619 FLATDUP_OR_ERROR(new->aifoIsds, src->aifoIsds);
620 STRDUP_OR_ERROR(new->registryCode, src->registryCode);
621 FLATDUP_OR_ERROR(new->dbState, src->dbState);
622 FLATDUP_OR_ERROR(new->dbEffectiveOVM, src->dbEffectiveOVM);
623 FLATDUP_OR_ERROR(new->dbOpenAddressing, src->dbOpenAddressing);
625 return new;
627 error:
628 isds_DbOwnerInfo_free(&new);
629 return NULL;
633 /* Copy structure isds_DbUserInfo recursively */
634 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
635 const struct isds_DbUserInfo *src) {
636 struct isds_DbUserInfo *new = NULL;
637 if (!src) return NULL;
639 new = calloc(1, sizeof(*new));
640 if (!new) return NULL;
642 STRDUP_OR_ERROR(new->userID, src->userID);
643 FLATDUP_OR_ERROR(new->userType, src->userType);
644 FLATDUP_OR_ERROR(new->userPrivils, src->userPrivils);
646 if (src->personName) {
647 if (!(new->personName =
648 isds_PersonName_duplicate(src->personName)))
649 goto error;
652 if (src->address) {
653 if (!(new->address = isds_Address_duplicate(src->address)))
654 goto error;
657 FLATDUP_OR_ERROR(new->biDate, src->biDate);
658 STRDUP_OR_ERROR(new->ic, src->ic);
659 STRDUP_OR_ERROR(new->firmName, src->firmName);
660 STRDUP_OR_ERROR(new->caStreet, src->caStreet);
661 STRDUP_OR_ERROR(new->caCity, src->caCity);
662 STRDUP_OR_ERROR(new->caZipCode, src->caZipCode);
663 STRDUP_OR_ERROR(new->caState, src->caState);
664 STRDUP_OR_ERROR(new->aifo_ticket, src->aifo_ticket);
666 return new;
668 error:
669 isds_DbUserInfo_free(&new);
670 return NULL;
673 #undef FLATDUP_OR_ERROR
674 #undef STRDUP_OR_ERROR
677 /* Logs libxml2 errors. Should be registered to libxml2 library.
678 * @ctx is unused currently
679 * @msg is printf-like formated message from libxml2 (UTF-8?)
680 * @... are variadic arguments for @msg */
681 static void log_xml(void *ctx, const char *msg, ...) {
682 va_list ap;
683 char *text = NULL;
685 /* Silent warning for unused function argument.
686 * The prototype is an API of libxml2's xmlSetGenericErrorFunc(). */
687 (void)ctx;
689 if (!msg) return;
691 va_start(ap, msg);
692 isds_vasprintf(&text, msg, ap);
693 va_end(ap);
695 if (text)
696 isds_log(ILF_XML, ILL_ERR, "%s", text);
697 free(text);
701 /* Initialize ISDS library.
702 * Global function, must be called before other functions.
703 * If it fails you can not use ISDS library and must call isds_cleanup() to
704 * free partially initialized global variables. */
705 isds_error isds_init(void) {
706 /* NULL global variables */
707 log_facilities = ILF_ALL;
708 log_level = ILL_WARNING;
709 log_callback = NULL;
710 log_callback_data = NULL;
712 #if ENABLE_NLS
713 /* Initialize gettext */
714 bindtextdomain(PACKAGE, LOCALEDIR);
715 #endif
717 #if HAVE_LIBCURL
718 /* Initialize CURL */
719 if (curl_global_init(CURL_GLOBAL_ALL)) {
720 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
721 return IE_ERROR;
723 #endif /* HAVE_LIBCURL */
725 /* Initialise cryptographic back-ends. */
726 if (IE_SUCCESS != _isds_init_crypto()) {
727 isds_log(ILF_ISDS, ILL_CRIT,
728 _("Initialization of cryptographic back-end failed\n"));
729 return IE_ERROR;
732 /* This can _exit() current program. Find not so assertive check. */
733 LIBXML_TEST_VERSION;
734 xmlSetGenericErrorFunc(NULL, log_xml);
736 /* Check expat */
737 if (_isds_init_expat(&version_expat)) {
738 isds_log(ILF_ISDS, ILL_CRIT,
739 _("expat library initialization failed\n"));
740 return IE_ERROR;
743 /* Allocate global variables */
746 return IE_SUCCESS;
750 /* Deinitialize ISDS library.
751 * Global function, must be called as last library function. */
752 isds_error isds_cleanup(void) {
753 /* XML */
754 xmlCleanupParser();
756 #if HAVE_LIBCURL
757 /* Curl */
758 curl_global_cleanup();
759 #endif
761 return IE_SUCCESS;
765 /* Return version string of this library. Version of dependencies can be
766 * embedded. Do no try to parse it. You must free it. */
767 char *isds_version(void) {
768 char *buffer = NULL;
770 isds_asprintf(&buffer,
771 #if HAVE_LIBCURL
772 # ifndef USE_OPENSSL_BACKEND
773 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
774 # else
775 _("%s (%s, %s, %s, libxml2 %s)"),
776 # endif
777 #else
778 # ifndef USE_OPENSSL_BACKEND
779 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
780 # else
781 _("%s (%s, %s, libxml2 %s)"),
782 # endif
783 #endif
784 PACKAGE_VERSION,
785 #if HAVE_LIBCURL
786 curl_version(),
787 #endif
788 #ifndef USE_OPENSSL_BACKEND
789 version_gpgme, version_gcrypt,
790 #else
791 version_openssl,
792 #endif
793 version_expat, xmlParserVersion);
794 return buffer;
798 /* Return text description of ISDS error */
799 const char *isds_strerror(const isds_error error) {
800 switch (error) {
801 case IE_SUCCESS:
802 return(_("Success")); break;
803 case IE_ERROR:
804 return(_("Unspecified error")); break;
805 case IE_NOTSUP:
806 return(_("Not supported")); break;
807 case IE_INVAL:
808 return(_("Invalid value")); break;
809 case IE_INVALID_CONTEXT:
810 return(_("Invalid context")); break;
811 case IE_NOT_LOGGED_IN:
812 return(_("Not logged in")); break;
813 case IE_CONNECTION_CLOSED:
814 return(_("Connection closed")); break;
815 case IE_TIMED_OUT:
816 return(_("Timed out")); break;
817 case IE_NOEXIST:
818 return(_("Not exist")); break;
819 case IE_NOMEM:
820 return(_("Out of memory")); break;
821 case IE_NETWORK:
822 return(_("Network problem")); break;
823 case IE_HTTP:
824 return(_("HTTP problem")); break;
825 case IE_SOAP:
826 return(_("SOAP problem")); break;
827 case IE_XML:
828 return(_("XML problem")); break;
829 case IE_ISDS:
830 return(_("ISDS server problem")); break;
831 case IE_ENUM:
832 return(_("Invalid enum value")); break;
833 case IE_DATE:
834 return(_("Invalid date value")); break;
835 case IE_2BIG:
836 return(_("Too big")); break;
837 case IE_2SMALL:
838 return(_("Too small")); break;
839 case IE_NOTUNIQ:
840 return(_("Value not unique")); break;
841 case IE_NOTEQUAL:
842 return(_("Values not equal")); break;
843 case IE_PARTIAL_SUCCESS:
844 return(_("Some suboperations failed")); break;
845 case IE_ABORTED:
846 return(_("Operation aborted")); break;
847 case IE_SECURITY:
848 return(_("Security problem")); break;
849 default:
850 return(_("Unknown error"));
855 /* Create ISDS context.
856 * Each context can be used for different sessions to (possibly) different
857 * ISDS server with different credentials. */
858 struct isds_ctx *isds_ctx_create(void) {
859 struct isds_ctx *context;
860 context = malloc(sizeof(*context));
861 if (context) memset(context, 0, sizeof(*context));
862 return context;
865 #if HAVE_LIBCURL
866 /* Close possibly opened connection to Czech POINT document deposit without
867 * resetting long_message buffer.
868 * XXX: Do not use czp_close_connection() if you do not want to destroy log
869 * message.
870 * @context is Czech POINT session context. */
871 static isds_error czp_do_close_connection(struct isds_ctx *context) {
872 if (!context) return IE_INVALID_CONTEXT;
873 _isds_close_connection(context);
874 return IE_SUCCESS;
878 /* Discard credentials.
879 * @context is ISDS context
880 * @discard_saved_username is true for removing saved username, false for
881 * keeping it.
882 * Only that. It does not cause log out, connection close or similar. */
883 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context,
884 _Bool discard_saved_username) {
885 if(!context) return IE_INVALID_CONTEXT;
887 if (context->username) {
888 memset(context->username, 0, strlen(context->username));
889 zfree(context->username);
891 if (context->password) {
892 memset(context->password, 0, strlen(context->password));
893 zfree(context->password);
895 isds_pki_credentials_free(&context->pki_credentials);
896 if (discard_saved_username && context->saved_username) {
897 memset(context->saved_username, 0, strlen(context->saved_username));
898 zfree(context->saved_username);
901 return IE_SUCCESS;
903 #endif /* HAVE_LIBCURL */
906 /* Destroy ISDS context and free memory.
907 * @context will be NULLed on success. */
908 isds_error isds_ctx_free(struct isds_ctx **context) {
909 if (!context || !*context) {
910 return IE_INVALID_CONTEXT;
913 #if HAVE_LIBCURL
914 /* Discard credentials and close connection */
915 switch ((*context)->type) {
916 case CTX_TYPE_NONE: break;
917 case CTX_TYPE_ISDS: isds_logout(*context); break;
918 case CTX_TYPE_CZP:
919 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
920 czp_do_close_connection(*context); break;
923 /* For sure */
924 _isds_discard_credentials(*context, 1);
926 /* Free other structures */
927 free((*context)->url);
928 free((*context)->tls_verify_server);
929 free((*context)->tls_ca_file);
930 free((*context)->tls_ca_dir);
931 free((*context)->tls_crl_file);
932 #endif /* HAVE_LIBCURL */
933 free((*context)->long_message);
935 free(*context);
936 *context = NULL;
937 return IE_SUCCESS;
941 /* Return long message text produced by library function, e.g. detailed error
942 * message. Returned pointer is only valid until new library function is
943 * called for the same context. Could be NULL, especially if NULL context is
944 * supplied. Return string is locale encoded. */
945 char *isds_long_message(const struct isds_ctx *context) {
946 if (!context) return NULL;
947 return context->long_message;
951 /* Stores message into context' long_message buffer.
952 * Application can pick the message up using isds_long_message().
953 * NULL @message truncates the buffer but does not deallocate it.
954 * @message is coded in locale encoding */
955 _hidden isds_error isds_log_message(struct isds_ctx *context,
956 const char *message) {
957 char *buffer;
958 size_t length;
960 if (!context) return IE_INVALID_CONTEXT;
962 /* FIXME: Check for integer overflow */
963 length = 1 + ((message) ? strlen(message) : 0);
964 buffer = realloc(context->long_message, length);
965 if (!buffer) return IE_NOMEM;
967 if (message)
968 strcpy(buffer, message);
969 else
970 *buffer = '\0';
972 context->long_message = buffer;
973 return IE_SUCCESS;
977 /* Appends message into context' long_message buffer.
978 * Application can pick the message up using isds_long_message().
979 * NULL message has void effect. */
980 _hidden isds_error isds_append_message(struct isds_ctx *context,
981 const char *message) {
982 char *buffer;
983 size_t old_length, length;
985 if (!context) return IE_INVALID_CONTEXT;
986 if (!message) return IE_SUCCESS;
987 if (!context->long_message)
988 return isds_log_message(context, message);
990 old_length = strlen(context->long_message);
991 /* FIXME: Check for integer overflow */
992 length = 1 + old_length + strlen(message);
993 buffer = realloc(context->long_message, length);
994 if (!buffer) return IE_NOMEM;
996 strcpy(buffer + old_length, message);
998 context->long_message = buffer;
999 return IE_SUCCESS;
1003 /* Stores formatted message into context' long_message buffer.
1004 * Application can pick the message up using isds_long_message(). */
1005 _hidden isds_error isds_printf_message(struct isds_ctx *context,
1006 const char *format, ...) {
1007 va_list ap;
1008 int length;
1010 if (!context) return IE_INVALID_CONTEXT;
1011 va_start(ap, format);
1012 length = isds_vasprintf(&(context->long_message), format, ap);
1013 va_end(ap);
1015 return (length < 0) ? IE_ERROR: IE_SUCCESS;
1019 /* Set logging up.
1020 * @facilities is bit mask of isds_log_facility values,
1021 * @level is verbosity level. */
1022 void isds_set_logging(const unsigned int facilities,
1023 const isds_log_level level) {
1024 log_facilities = facilities;
1025 log_level = level;
1029 /* Register callback function libisds calls when new global log message is
1030 * produced by library. Library logs to stderr by default.
1031 * @callback is function provided by application libisds will call. See type
1032 * definition for @callback argument explanation. Pass NULL to revert logging to
1033 * default behaviour.
1034 * @data is application specific data @callback gets as last argument */
1035 void isds_set_log_callback(isds_log_callback callback, void *data) {
1036 log_callback = callback;
1037 log_callback_data = data;
1041 /* Log @message in class @facility with log @level into global log. @message
1042 * is printf(3) formatting string, variadic arguments may be necessary.
1043 * For debugging purposes. */
1044 _hidden isds_error isds_log(const isds_log_facility facility,
1045 const isds_log_level level, const char *message, ...) {
1046 va_list ap;
1047 char *buffer = NULL;
1048 int length;
1050 if (level > log_level) return IE_SUCCESS;
1051 if (!(log_facilities & facility)) return IE_SUCCESS;
1052 if (!message) return IE_INVAL;
1054 if (log_callback) {
1055 /* Pass message to application supplied callback function */
1056 va_start(ap, message);
1057 length = isds_vasprintf(&buffer, message, ap);
1058 va_end(ap);
1060 if (length == -1) {
1061 return IE_ERROR;
1063 if (length > 0) {
1064 log_callback(facility, level, buffer, length, log_callback_data);
1066 free(buffer);
1067 } else {
1068 /* Default: Log it to stderr */
1069 va_start(ap, message);
1070 vfprintf(stderr, message, ap);
1071 va_end(ap);
1072 /* Line buffered printf is default.
1073 * fflush(stderr);*/
1076 return IE_SUCCESS;
1080 /* Set timeout in milliseconds for each network job like connecting to server
1081 * or sending message. Use 0 to disable timeout limits. */
1082 isds_error isds_set_timeout(struct isds_ctx *context,
1083 const unsigned int timeout) {
1084 if (!context) return IE_INVALID_CONTEXT;
1085 zfree(context->long_message);
1087 #if HAVE_LIBCURL
1088 context->timeout = timeout;
1090 if (context->curl) {
1091 CURLcode curl_err;
1093 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
1094 if (!curl_err)
1095 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1096 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
1097 context->timeout);
1098 #else
1099 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
1100 context->timeout / 1000);
1101 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1102 if (curl_err) return IE_ERROR;
1105 return IE_SUCCESS;
1106 #else /* not HAVE_LIBCURL */
1107 return IE_NOTSUP;
1108 #endif
1112 /* Register callback function libisds calls periodically during HTTP data
1113 * transfer.
1114 * @context is session context
1115 * @callback is function provided by application libisds will call. See type
1116 * definition for @callback argument explanation.
1117 * @data is application specific data @callback gets as last argument */
1118 isds_error isds_set_progress_callback(struct isds_ctx *context,
1119 isds_progress_callback callback, void *data) {
1120 if (!context) return IE_INVALID_CONTEXT;
1121 zfree(context->long_message);
1123 #if HAVE_LIBCURL
1124 context->progress_callback = callback;
1125 context->progress_callback_data = data;
1127 return IE_SUCCESS;
1128 #else /* not HAVE_LIBCURL */
1129 return IE_NOTSUP;
1130 #endif
1134 /* Change context settings.
1135 * @context is context which setting will be applied to
1136 * @option is name of option. It determines the type of last argument. See
1137 * isds_option definition for more info.
1138 * @... is value of new setting. Type is determined by @option
1139 * */
1140 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1141 ...) {
1142 isds_error err = IE_SUCCESS;
1143 va_list ap;
1144 #if HAVE_LIBCURL
1145 char *pointer, *string;
1146 #endif
1148 if (!context) return IE_INVALID_CONTEXT;
1149 zfree(context->long_message);
1151 va_start(ap, option);
1153 #define REPLACE_VA_BOOLEAN(destination) { \
1154 if (!(destination)) { \
1155 (destination) = malloc(sizeof(*(destination))); \
1156 if (!(destination)) { \
1157 err = IE_NOMEM; goto leave; \
1160 *(destination) = (_Bool) !!va_arg(ap, int); \
1163 #define REPLACE_VA_STRING(destination) { \
1164 string = va_arg(ap, char *); \
1165 if (string) { \
1166 pointer = realloc((destination), 1 + strlen(string)); \
1167 if (!pointer) { err = IE_NOMEM; goto leave; } \
1168 strcpy(pointer, string); \
1169 (destination) = pointer; \
1170 } else { \
1171 free(destination); \
1172 (destination) = NULL; \
1176 switch (option) {
1177 case IOPT_TLS_VERIFY_SERVER:
1178 #if HAVE_LIBCURL
1179 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1180 #else
1181 err = IE_NOTSUP; goto leave;
1182 #endif
1183 break;
1184 case IOPT_TLS_CA_FILE:
1185 #if HAVE_LIBCURL
1186 REPLACE_VA_STRING(context->tls_ca_file);
1187 #else
1188 err = IE_NOTSUP; goto leave;
1189 #endif
1190 break;
1191 case IOPT_TLS_CA_DIRECTORY:
1192 #if HAVE_LIBCURL
1193 REPLACE_VA_STRING(context->tls_ca_dir);
1194 #else
1195 err = IE_NOTSUP; goto leave;
1196 #endif
1197 break;
1198 case IOPT_TLS_CRL_FILE:
1199 #if HAVE_LIBCURL
1200 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1201 REPLACE_VA_STRING(context->tls_crl_file);
1202 #else
1203 isds_log_message(context,
1204 _("Curl library does not support CRL definition"));
1205 err = IE_NOTSUP;
1206 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1207 #else
1208 err = IE_NOTSUP; goto leave;
1209 #endif /* not HAVE_LIBCURL */
1210 break;
1211 case IOPT_NORMALIZE_MIME_TYPE:
1212 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1213 break;
1215 default:
1216 err = IE_ENUM; goto leave;
1219 #undef REPLACE_VA_STRING
1220 #undef REPLACE_VA_BOOLEAN
1222 leave:
1223 va_end(ap);
1224 return err;
1228 #if HAVE_LIBCURL
1229 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1230 * Destination for NULL argument will not be touched.
1231 * Destination pointers must be freed before calling this function.
1232 * If @username is @context->saved_username, the saved_username will not be
1233 * replaced. The saved_username is clobbered only if context has set otp
1234 * member.
1235 * Return IE_SUCCESS on success. */
1236 static isds_error _isds_store_credentials(struct isds_ctx *context,
1237 const char *username, const char *password,
1238 const struct isds_pki_credentials *pki_credentials) {
1239 if (NULL == context) return IE_INVALID_CONTEXT;
1241 /* FIXME: mlock password
1242 * (I have a library) */
1244 if (username) {
1245 context->username = strdup(username);
1246 if (context->otp && context->saved_username != username)
1247 context->saved_username = strdup(username);
1249 if (password) {
1250 if (NULL == context->otp_credentials)
1251 context->password = strdup(password);
1252 else
1253 context->password = _isds_astrcat(password,
1254 context->otp_credentials->otp_code);
1256 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1258 if ((NULL != username && NULL == context->username) ||
1259 (NULL != password && NULL == context->password) ||
1260 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1261 (context->otp && NULL != context->username &&
1262 NULL == context->saved_username)) {
1263 return IE_NOMEM;
1266 return IE_SUCCESS;
1268 #endif
1271 /* Connect and log into ISDS server.
1272 * All required arguments will be copied, you do not have to keep them after
1273 * that.
1274 * ISDS supports six different authentication methods. Exact method is
1275 * selected on @username, @password, @pki_credentials, and @otp arguments:
1276 * - If @pki_credentials == NULL, @username and @password must be supplied
1277 * and then
1278 * - If @otp == NULL, simple authentication by username and password will
1279 * be proceeded.
1280 * - If @otp != NULL, authentication by username and password and OTP
1281 * will be used.
1282 * - If @pki_credentials != NULL, then
1283 * - If @username == NULL, only certificate will be used
1284 * - If @username != NULL, then
1285 * - If @password == NULL, then certificate will be used and
1286 * @username shifts meaning to box ID. This is used for hosted
1287 * services.
1288 * - Otherwise all three arguments will be used.
1289 * Please note, that different cases require different certificate type
1290 * (system qualified one or commercial non qualified one). This library
1291 * does not check such political issues. Please see ISDS Specification
1292 * for more details.
1293 * @url is base address of ISDS web service. Pass extern isds_locator
1294 * variable to use production ISDS instance without client certificate
1295 * authentication (or extern isds_cert_locator with client certificate
1296 * authentication or extern isds_otp_locators with OTP authentication).
1297 * Passing NULL has the same effect, autoselection between isds_locator,
1298 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1299 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1300 * isds_otp_testing_locator) variable to select testing instance.
1301 * @username is user name of ISDS user or box ID
1302 * @password is user's secret password
1303 * @pki_credentials defines public key cryptographic material to use in client
1304 * authentication.
1305 * @otp selects one-time password authentication method to use, defines OTP
1306 * code (if known) and returns fine grade resolution of OTP procedure.
1307 * @return:
1308 * IE_SUCCESS if authentication succeeds
1309 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1310 * requested, fine grade reason will be set into @otp->resolution. Error
1311 * message from server can be obtained by isds_long_message() call.
1312 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1313 * server has sent OTP code through side channel. Application is expected to
1314 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1315 * this call to complete second phase of TOTP authentication;
1316 * or other appropriate error. */
1317 isds_error isds_login(struct isds_ctx *context, const char *url,
1318 const char *username, const char *password,
1319 const struct isds_pki_credentials *pki_credentials,
1320 struct isds_otp *otp) {
1321 #if HAVE_LIBCURL
1322 isds_error err = IE_NOT_LOGGED_IN;
1323 isds_error soap_err;
1324 xmlNsPtr isds_ns = NULL;
1325 xmlNodePtr request = NULL;
1326 #endif /* HAVE_LIBCURL */
1328 if (!context) return IE_INVALID_CONTEXT;
1329 zfree(context->long_message);
1331 #if HAVE_LIBCURL
1332 /* Close connection if already logged in */
1333 if (context->curl) {
1334 _isds_close_connection(context);
1337 /* Store configuration */
1338 context->type = CTX_TYPE_ISDS;
1339 zfree(context->url);
1341 /* Mangle base URI according to requested authentication method */
1342 if (NULL == pki_credentials) {
1343 isds_log(ILF_SEC, ILL_INFO,
1344 _("Selected authentication method: no certificate, "
1345 "username and password\n"));
1346 if (!username || !password) {
1347 isds_log_message(context,
1348 _("Both username and password must be supplied"));
1349 return IE_INVAL;
1351 context->otp_credentials = otp;
1352 context->otp = (NULL != context->otp_credentials);
1354 if (!context->otp) {
1355 /* Default locator is official system (without certificate or
1356 * OTP) */
1357 context->url = strdup((NULL != url) ? url : isds_locator);
1358 } else {
1359 const char *authenticator_uri = NULL;
1360 if (!url) url = isds_otp_locator;
1361 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1362 switch (context->otp_credentials->method) {
1363 case OTP_HMAC:
1364 isds_log(ILF_SEC, ILL_INFO,
1365 _("Selected authentication method: "
1366 "HMAC-based one-time password\n"));
1367 authenticator_uri =
1368 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1369 break;
1370 case OTP_TIME:
1371 isds_log(ILF_SEC, ILL_INFO,
1372 _("Selected authentication method: "
1373 "Time-based one-time password\n"));
1374 if (context->otp_credentials->otp_code == NULL) {
1375 isds_log(ILF_SEC, ILL_INFO,
1376 _("OTP code has not been provided by "
1377 "application, requesting server for "
1378 "new one.\n"));
1379 authenticator_uri =
1380 "%1$sas/processLogin?type=totp&sendSms=true&"
1381 "uri=%1$sapps/";
1382 } else {
1383 isds_log(ILF_SEC, ILL_INFO,
1384 _("OTP code has been provided by "
1385 "application, not requesting server "
1386 "for new one.\n"));
1387 authenticator_uri =
1388 "%1$sas/processLogin?type=totp&"
1389 "uri=%1$sapps/";
1391 break;
1392 default:
1393 isds_log_message(context,
1394 _("Unknown one-time password authentication "
1395 "method requested by application"));
1396 return IE_ENUM;
1398 if (-1 == isds_asprintf(&context->url, authenticator_uri, url))
1399 return IE_NOMEM;
1401 } else {
1402 /* Default locator is official system (with client certificate) */
1403 context->otp = 0;
1404 context->otp_credentials = NULL;
1405 if (!url) url = isds_cert_locator;
1407 if (!username) {
1408 isds_log(ILF_SEC, ILL_INFO,
1409 _("Selected authentication method: system certificate, "
1410 "no username and no password\n"));
1411 password = NULL;
1412 context->url = _isds_astrcat(url, "cert/");
1413 } else {
1414 if (!password) {
1415 isds_log(ILF_SEC, ILL_INFO,
1416 _("Selected authentication method: system certificate, "
1417 "box ID and no password\n"));
1418 context->url = _isds_astrcat(url, "hspis/");
1419 } else {
1420 isds_log(ILF_SEC, ILL_INFO,
1421 _("Selected authentication method: commercial "
1422 "certificate, username and password\n"));
1423 context->url = _isds_astrcat(url, "certds/");
1427 if (!(context->url))
1428 return IE_NOMEM;
1430 /* Prepare CURL handle */
1431 context->curl = curl_easy_init();
1432 if (!(context->curl))
1433 return IE_ERROR;
1435 /* Build log-in request */
1436 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1437 if (!request) {
1438 isds_log_message(context, _("Could not build ISDS log-in request"));
1439 return IE_ERROR;
1441 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1442 if(!isds_ns) {
1443 isds_log_message(context, _("Could not create ISDS name space"));
1444 xmlFreeNode(request);
1445 return IE_ERROR;
1447 xmlSetNs(request, isds_ns);
1449 /* Store credentials */
1450 _isds_discard_credentials(context, 1);
1451 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1452 _isds_discard_credentials(context, 1);
1453 xmlFreeNode(request);
1454 return IE_NOMEM;
1457 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1458 username, url);
1460 /* XXX: ISDS documentation does not specify response body for
1461 * DummyOperation request. However real server sends back
1462 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1463 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1464 * SOAP body content, e.g. the dmStatus element. */
1466 /* Send log-in request */
1467 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1469 if (context->otp) {
1470 /* Revert context URL from OTP authentication service URL to OTP web
1471 * service base URL for subsequent calls. Potenial isds_login() retry
1472 * will re-set context URL again. */
1473 zfree(context->url);
1474 context->url = _isds_astrcat(url, "apps/");
1475 if (context->url == NULL) {
1476 soap_err = IE_NOMEM;
1478 /* Detach pointer to OTP credentials from context */
1479 context->otp_credentials = NULL;
1482 /* Remove credentials */
1483 _isds_discard_credentials(context, 0);
1485 /* Destroy log-in request */
1486 xmlFreeNode(request);
1488 if (soap_err) {
1489 _isds_close_connection(context);
1490 return soap_err;
1493 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1494 * authentication succeeded if soap_err == IE_SUCCESS */
1495 err = IE_SUCCESS;
1497 if (!err)
1498 isds_log(ILF_ISDS, ILL_DEBUG,
1499 _("User %s has been logged into server %s successfully\n"),
1500 username, url);
1501 return err;
1502 #else /* not HAVE_LIBCURL */
1503 return IE_NOTSUP;
1504 #endif
1508 /* Log out from ISDS server discards credentials and connection configuration. */
1509 isds_error isds_logout(struct isds_ctx *context) {
1510 if (!context) return IE_INVALID_CONTEXT;
1511 zfree(context->long_message);
1513 #if HAVE_LIBCURL
1514 if (context->curl) {
1515 if (context->otp) {
1516 isds_error err = _isds_invalidate_otp_cookie(context);
1517 if (err) return err;
1520 /* Close connection */
1521 _isds_close_connection(context);
1523 /* Discard credentials for sure. They should not survive isds_login(),
1524 * even successful .*/
1525 _isds_discard_credentials(context, 1);
1527 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1528 } else {
1529 _isds_discard_credentials(context, 1);
1531 zfree(context->url);
1532 return IE_SUCCESS;
1533 #else /* not HAVE_LIBCURL */
1534 return IE_NOTSUP;
1535 #endif
1539 /* Verify connection to ISDS is alive and server is responding.
1540 * Send dummy request to ISDS and expect dummy response. */
1541 isds_error isds_ping(struct isds_ctx *context) {
1542 #if HAVE_LIBCURL
1543 isds_error soap_err;
1544 xmlNsPtr isds_ns = NULL;
1545 xmlNodePtr request = NULL;
1546 #endif /* HAVE_LIBCURL */
1548 if (!context) return IE_INVALID_CONTEXT;
1549 zfree(context->long_message);
1551 #if HAVE_LIBCURL
1552 /* Check if connection is established */
1553 if (!context->curl) return IE_CONNECTION_CLOSED;
1556 /* Build dummy request */
1557 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1558 if (!request) {
1559 isds_log_message(context, _("Could build ISDS dummy request"));
1560 return IE_ERROR;
1562 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1563 if(!isds_ns) {
1564 isds_log_message(context, _("Could not create ISDS name space"));
1565 xmlFreeNode(request);
1566 return IE_ERROR;
1568 xmlSetNs(request, isds_ns);
1570 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1572 /* XXX: ISDS documentation does not specify response body for
1573 * DummyOperation request. However real server sends back
1574 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1575 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1576 * SOAP body content, e.g. the dmStatus element. */
1578 /* Send dummy request */
1579 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1581 /* Destroy log-in request */
1582 xmlFreeNode(request);
1584 if (soap_err) {
1585 isds_log(ILF_ISDS, ILL_DEBUG,
1586 _("ISDS server could not be contacted\n"));
1587 return soap_err;
1590 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1591 * authentication succeeded if soap_err == IE_SUCCESS */
1594 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1596 return IE_SUCCESS;
1597 #else /* not HAVE_LIBCURL */
1598 return IE_NOTSUP;
1599 #endif
1603 /* Send bogus request to ISDS.
1604 * Just for test purposes */
1605 isds_error isds_bogus_request(struct isds_ctx *context) {
1606 #if HAVE_LIBCURL
1607 isds_error err;
1608 xmlNsPtr isds_ns = NULL;
1609 xmlNodePtr request = NULL;
1610 xmlDocPtr response = NULL;
1611 xmlChar *code = NULL, *message = NULL;
1612 #endif
1614 if (!context) return IE_INVALID_CONTEXT;
1615 zfree(context->long_message);
1617 #if HAVE_LIBCURL
1618 /* Check if connection is established */
1619 if (!context->curl) {
1620 /* Testing printf message */
1621 isds_printf_message(context, "%s", _("I said connection closed"));
1622 return IE_CONNECTION_CLOSED;
1626 /* Build dummy request */
1627 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1628 if (!request) {
1629 isds_log_message(context, _("Could build ISDS bogus request"));
1630 return IE_ERROR;
1632 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1633 if(!isds_ns) {
1634 isds_log_message(context, _("Could not create ISDS name space"));
1635 xmlFreeNode(request);
1636 return IE_ERROR;
1638 xmlSetNs(request, isds_ns);
1640 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1642 /* Sent bogus request */
1643 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1645 /* Destroy request */
1646 xmlFreeNode(request);
1648 if (err) {
1649 isds_log(ILF_ISDS, ILL_DEBUG,
1650 _("Processing ISDS response on bogus request failed\n"));
1651 xmlFreeDoc(response);
1652 return err;
1655 /* Check for response status */
1656 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1657 &code, &message, NULL);
1658 if (err) {
1659 isds_log(ILF_ISDS, ILL_DEBUG,
1660 _("ISDS response on bogus request is missing status\n"));
1661 free(code);
1662 free(message);
1663 xmlFreeDoc(response);
1664 return err;
1666 if (xmlStrcmp(code, BAD_CAST "0000")) {
1667 char *code_locale = _isds_utf82locale((char*)code);
1668 char *message_locale = _isds_utf82locale((char*)message);
1669 isds_log(ILF_ISDS, ILL_DEBUG,
1670 _("Server refused bogus request (code=%s, message=%s)\n"),
1671 code_locale, message_locale);
1672 /* XXX: Literal error messages from ISDS are Czech messages
1673 * (English sometimes) in UTF-8. It's hard to catch them for
1674 * translation. Successfully gettextized would return in locale
1675 * encoding, unsuccessfully translated would pass in UTF-8. */
1676 isds_log_message(context, message_locale);
1677 free(code_locale);
1678 free(message_locale);
1679 free(code);
1680 free(message);
1681 xmlFreeDoc(response);
1682 return IE_ISDS;
1686 free(code);
1687 free(message);
1688 xmlFreeDoc(response);
1690 isds_log(ILF_ISDS, ILL_DEBUG,
1691 _("Bogus message accepted by server. This should not happen.\n"));
1693 return IE_SUCCESS;
1694 #else /* not HAVE_LIBCURL */
1695 return IE_NOTSUP;
1696 #endif
1700 #if HAVE_LIBCURL
1701 /* Serialize XML subtree to buffer preserving XML indentation.
1702 * @context is session context
1703 * @subtree is XML element to be serialized (with children)
1704 * @buffer is automatically reallocated buffer where serialize to
1705 * @length is size of serialized stream in bytes
1706 * @return standard error code, free @buffer in case of error */
1707 static isds_error serialize_subtree(struct isds_ctx *context,
1708 xmlNodePtr subtree, void **buffer, size_t *length) {
1709 isds_error err = IE_SUCCESS;
1710 xmlBufferPtr xml_buffer = NULL;
1711 xmlSaveCtxtPtr save_ctx = NULL;
1712 xmlDocPtr subtree_doc = NULL;
1713 xmlNodePtr subtree_copy;
1714 xmlNsPtr isds_ns;
1715 void *new_buffer;
1717 if (!context) return IE_INVALID_CONTEXT;
1718 if (!buffer) return IE_INVAL;
1719 zfree(*buffer);
1720 if (!subtree || !length) return IE_INVAL;
1722 /* Make temporary XML document with @subtree root element */
1723 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1724 * It can result in not well-formed on invalid XML tree (e.g. name space
1725 * prefix definition can miss. */
1726 /*FIXME */
1728 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1729 if (!subtree_doc) {
1730 isds_log_message(context, _("Could not build temporary document"));
1731 err = IE_ERROR;
1732 goto leave;
1735 /* XXX: Copy subtree and attach the copy to document.
1736 * One node can not bee attached into more document at the same time.
1737 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1738 * automatically.
1739 * XXX: Check xmlSaveTree() too. */
1740 subtree_copy = xmlCopyNodeList(subtree);
1741 if (!subtree_copy) {
1742 isds_log_message(context, _("Could not copy subtree"));
1743 err = IE_ERROR;
1744 goto leave;
1746 xmlDocSetRootElement(subtree_doc, subtree_copy);
1748 /* Only this way we get namespace definition as @xmlns:isds,
1749 * otherwise we get namespace prefix without definition */
1750 /* FIXME: Don't overwrite original default namespace */
1751 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1752 if(!isds_ns) {
1753 isds_log_message(context, _("Could not create ISDS name space"));
1754 err = IE_ERROR;
1755 goto leave;
1757 xmlSetNs(subtree_copy, isds_ns);
1760 /* Serialize the document into buffer */
1761 xml_buffer = xmlBufferCreate();
1762 if (!xml_buffer) {
1763 isds_log_message(context, _("Could not create xmlBuffer"));
1764 err = IE_ERROR;
1765 goto leave;
1767 /* Last argument 0 means to not format the XML tree */
1768 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1769 if (!save_ctx) {
1770 isds_log_message(context, _("Could not create XML serializer"));
1771 err = IE_ERROR;
1772 goto leave;
1774 /* XXX: According LibXML documentation, this function does not return
1775 * meaningful value yet */
1776 xmlSaveDoc(save_ctx, subtree_doc);
1777 if (-1 == xmlSaveFlush(save_ctx)) {
1778 isds_log_message(context,
1779 _("Could not serialize XML subtree"));
1780 err = IE_ERROR;
1781 goto leave;
1783 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1784 * even after xmlSaveFlush(). Thus close it here */
1785 xmlSaveClose(save_ctx); save_ctx = NULL;
1788 /* Store and detach buffer from xml_buffer */
1789 *buffer = xml_buffer->content;
1790 *length = xml_buffer->use;
1791 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1793 /* Shrink buffer */
1794 new_buffer = realloc(*buffer, *length);
1795 if (new_buffer) *buffer = new_buffer;
1797 leave:
1798 if (err) {
1799 zfree(*buffer);
1800 *length = 0;
1803 xmlSaveClose(save_ctx);
1804 xmlBufferFree(xml_buffer);
1805 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1806 return err;
1808 #endif /* HAVE_LIBCURL */
1811 #if 0
1812 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1813 * @context is session context
1814 * @document is original document where @nodeset points to
1815 * @nodeset is XPath node set to dump (recursively)
1816 * @buffer is automatically reallocated buffer where serialize to
1817 * @length is size of serialized stream in bytes
1818 * @return standard error code, free @buffer in case of error */
1819 static isds_error dump_nodeset(struct isds_ctx *context,
1820 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1821 void **buffer, size_t *length) {
1822 isds_error err = IE_SUCCESS;
1823 xmlBufferPtr xml_buffer = NULL;
1824 void *new_buffer;
1826 if (!context) return IE_INVALID_CONTEXT;
1827 if (!buffer) return IE_INVAL;
1828 zfree(*buffer);
1829 if (!document || !nodeset || !length) return IE_INVAL;
1830 *length = 0;
1832 /* Empty node set results into NULL buffer */
1833 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1834 goto leave;
1837 /* Resulting the document into buffer */
1838 xml_buffer = xmlBufferCreate();
1839 if (!xml_buffer) {
1840 isds_log_message(context, _("Could not create xmlBuffer"));
1841 err = IE_ERROR;
1842 goto leave;
1845 /* Iterate over all nodes */
1846 for (int i = 0; i < nodeset->nodeNr; i++) {
1847 /* Serialize node.
1848 * XXX: xmlNodeDump() appends to xml_buffer. */
1849 if (-1 ==
1850 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1851 isds_log_message(context, _("Could not dump XML node"));
1852 err = IE_ERROR;
1853 goto leave;
1857 /* Store and detach buffer from xml_buffer */
1858 *buffer = xml_buffer->content;
1859 *length = xml_buffer->use;
1860 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1862 /* Shrink buffer */
1863 new_buffer = realloc(*buffer, *length);
1864 if (new_buffer) *buffer = new_buffer;
1867 leave:
1868 if (err) {
1869 zfree(*buffer);
1870 *length = 0;
1873 xmlBufferFree(xml_buffer);
1874 return err;
1876 #endif
1878 #if 0
1879 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1880 * @context is session context
1881 * @document is original document where @nodeset points to
1882 * @nodeset is XPath node set to dump (recursively)
1883 * @buffer is automatically reallocated buffer where serialize to
1884 * @length is size of serialized stream in bytes
1885 * @return standard error code, free @buffer in case of error */
1886 static isds_error dump_nodeset(struct isds_ctx *context,
1887 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1888 void **buffer, size_t *length) {
1889 isds_error err = IE_SUCCESS;
1890 xmlBufferPtr xml_buffer = NULL;
1891 xmlSaveCtxtPtr save_ctx = NULL;
1892 void *new_buffer;
1894 if (!context) return IE_INVALID_CONTEXT;
1895 if (!buffer) return IE_INVAL;
1896 zfree(*buffer);
1897 if (!document || !nodeset || !length) return IE_INVAL;
1898 *length = 0;
1900 /* Empty node set results into NULL buffer */
1901 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1902 goto leave;
1905 /* Resulting the document into buffer */
1906 xml_buffer = xmlBufferCreate();
1907 if (!xml_buffer) {
1908 isds_log_message(context, _("Could not create xmlBuffer"));
1909 err = IE_ERROR;
1910 goto leave;
1912 if (xmlSubstituteEntitiesDefault(1)) {
1913 isds_log_message(context, _("Could not disable attribute escaping"));
1914 err = IE_ERROR;
1915 goto leave;
1917 /* Last argument means:
1918 * 0 to not format the XML tree
1919 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1920 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1921 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1922 if (!save_ctx) {
1923 isds_log_message(context, _("Could not create XML serializer"));
1924 err = IE_ERROR;
1925 goto leave;
1927 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1928 isds_log_message(context, _("Could not disable attribute escaping"));
1929 err = IE_ERROR;
1930 goto leave;
1934 /* Iterate over all nodes */
1935 for (int i = 0; i < nodeset->nodeNr; i++) {
1936 /* Serialize node.
1937 * XXX: xmlNodeDump() appends to xml_buffer. */
1938 /*if (-1 ==
1939 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1941 /* XXX: According LibXML documentation, this function does not return
1942 * meaningful value yet */
1943 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1944 if (-1 == xmlSaveFlush(save_ctx)) {
1945 isds_log_message(context,
1946 _("Could not serialize XML subtree"));
1947 err = IE_ERROR;
1948 goto leave;
1952 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1953 * even after xmlSaveFlush(). Thus close it here */
1954 xmlSaveClose(save_ctx); save_ctx = NULL;
1956 /* Store and detach buffer from xml_buffer */
1957 *buffer = xml_buffer->content;
1958 *length = xml_buffer->use;
1959 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1961 /* Shrink buffer */
1962 new_buffer = realloc(*buffer, *length);
1963 if (new_buffer) *buffer = new_buffer;
1965 leave:
1966 if (err) {
1967 zfree(*buffer);
1968 *length = 0;
1971 xmlSaveClose(save_ctx);
1972 xmlBufferFree(xml_buffer);
1973 return err;
1975 #endif
1978 #if HAVE_LIBCURL
1979 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1980 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1981 if (!string || !type) return IE_INVAL;
1983 if (!xmlStrcmp(string, BAD_CAST "FO"))
1984 *type = DBTYPE_FO;
1985 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1986 *type = DBTYPE_PFO;
1987 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1988 *type = DBTYPE_PFO_ADVOK;
1989 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1990 *type = DBTYPE_PFO_DANPOR;
1991 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1992 *type = DBTYPE_PFO_INSSPR;
1993 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1994 *type = DBTYPE_PO;
1995 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1996 *type = DBTYPE_PO_ZAK;
1997 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1998 *type = DBTYPE_PO_REQ;
1999 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
2000 *type = DBTYPE_OVM;
2001 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
2002 *type = DBTYPE_OVM_NOTAR;
2003 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
2004 *type = DBTYPE_OVM_EXEKUT;
2005 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
2006 *type = DBTYPE_OVM_REQ;
2007 else
2008 return IE_ENUM;
2009 return IE_SUCCESS;
2013 /* Convert ISDS dbType enum @type to UTF-8 string.
2014 * @Return pointer to static string, or NULL if unknown enum value */
2015 static const xmlChar *isds_DbType2string(const isds_DbType type) {
2016 switch(type) {
2017 /* DBTYPE_SYSTEM and DBTYPE_OVM_MAIN are invalid values from point
2018 * of view of generic public SOAP interface. */
2019 case DBTYPE_FO: return(BAD_CAST "FO"); break;
2020 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
2021 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
2022 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
2023 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
2024 case DBTYPE_PO: return(BAD_CAST "PO"); break;
2025 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
2026 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
2027 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
2028 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
2029 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
2030 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
2031 default: return NULL; break;
2036 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2037 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
2038 if (!string || !type) return IE_INVAL;
2040 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2041 *type = USERTYPE_PRIMARY;
2042 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2043 *type = USERTYPE_ENTRUSTED;
2044 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2045 *type = USERTYPE_ADMINISTRATOR;
2046 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2047 *type = USERTYPE_OFFICIAL;
2048 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2049 *type = USERTYPE_OFFICIAL_CERT;
2050 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2051 *type = USERTYPE_LIQUIDATOR;
2052 else
2053 return IE_ENUM;
2054 return IE_SUCCESS;
2058 /* Convert ISDS userType enum @type to UTF-8 string.
2059 * @Return pointer to static string, or NULL if unknown enum value */
2060 static const xmlChar *isds_UserType2string(const isds_UserType type) {
2061 switch(type) {
2062 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
2063 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
2064 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
2065 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
2066 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
2067 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
2068 default: return NULL; break;
2073 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2074 static isds_error string2isds_sender_type(const xmlChar *string,
2075 isds_sender_type *type) {
2076 if (!string || !type) return IE_INVAL;
2078 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2079 *type = SENDERTYPE_PRIMARY;
2080 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2081 *type = SENDERTYPE_ENTRUSTED;
2082 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2083 *type = SENDERTYPE_ADMINISTRATOR;
2084 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2085 *type = SENDERTYPE_OFFICIAL;
2086 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2087 *type = SENDERTYPE_VIRTUAL;
2088 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2089 *type = SENDERTYPE_OFFICIAL_CERT;
2090 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2091 *type = SENDERTYPE_LIQUIDATOR;
2092 else
2093 return IE_ENUM;
2094 return IE_SUCCESS;
2098 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2099 static isds_error string2isds_payment_type(const xmlChar *string,
2100 isds_payment_type *type) {
2101 if (!string || !type) return IE_INVAL;
2103 if (!xmlStrcmp(string, BAD_CAST "K"))
2104 *type = PAYMENT_SENDER;
2105 else if (!xmlStrcmp(string, BAD_CAST "O"))
2106 *type = PAYMENT_RESPONSE;
2107 else if (!xmlStrcmp(string, BAD_CAST "G"))
2108 *type = PAYMENT_SPONSOR;
2109 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2110 *type = PAYMENT_SPONSOR_LIMITED;
2111 else if (!xmlStrcmp(string, BAD_CAST "D"))
2112 *type = PAYMENT_SPONSOR_EXTERNAL;
2113 else if (!xmlStrcmp(string, BAD_CAST "E"))
2114 *type = PAYMENT_STAMP;
2115 else
2116 return IE_ENUM;
2117 return IE_SUCCESS;
2121 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2122 * ciEventType is integer but we convert it from string representation
2123 * directly. */
2124 static isds_error string2isds_credit_event_type(const xmlChar *string,
2125 isds_credit_event_type *type) {
2126 if (!string || !type) return IE_INVAL;
2128 if (!xmlStrcmp(string, BAD_CAST "1"))
2129 *type = ISDS_CREDIT_CHARGED;
2130 else if (!xmlStrcmp(string, BAD_CAST "2"))
2131 *type = ISDS_CREDIT_DISCHARGED;
2132 else if (!xmlStrcmp(string, BAD_CAST "3"))
2133 *type = ISDS_CREDIT_MESSAGE_SENT;
2134 else if (!xmlStrcmp(string, BAD_CAST "4"))
2135 *type = ISDS_CREDIT_STORAGE_SET;
2136 else if (!xmlStrcmp(string, BAD_CAST "5"))
2137 *type = ISDS_CREDIT_EXPIRED;
2138 else
2139 return IE_ENUM;
2140 return IE_SUCCESS;
2144 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2145 * @Return pointer to static string, or NULL if unknown enum value */
2146 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2147 switch(type) {
2148 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2149 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2150 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2151 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2152 default: return NULL; break;
2157 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2158 * ISDSSearch2/searchType value.
2159 * @Return pointer to static string, or NULL if unknown enum value */
2160 static const xmlChar *isds_fulltext_target2string(
2161 const isds_fulltext_target type) {
2162 switch(type) {
2163 case FULLTEXT_ALL: return(BAD_CAST "GENERAL"); break;
2164 case FULLTEXT_ADDRESS: return(BAD_CAST "ADDRESS"); break;
2165 case FULLTEXT_IC: return(BAD_CAST "ICO"); break;
2166 case FULLTEXT_BOX_ID: return(BAD_CAST "DBID"); break;
2167 default: return NULL; break;
2170 #endif /* HAVE_LIBCURL */
2173 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2174 * @Return IE_ENUM if @string is not valid enum member */
2175 static isds_error string2isds_FileMetaType(const xmlChar *string,
2176 isds_FileMetaType *type) {
2177 if (!string || !type) return IE_INVAL;
2179 if (!xmlStrcmp(string, BAD_CAST "main"))
2180 *type = FILEMETATYPE_MAIN;
2181 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2182 *type = FILEMETATYPE_ENCLOSURE;
2183 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2184 *type = FILEMETATYPE_SIGNATURE;
2185 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2186 *type = FILEMETATYPE_META;
2187 else
2188 return IE_ENUM;
2189 return IE_SUCCESS;
2193 /* Convert UTF-8 @string to ISDS hash @algorithm.
2194 * @Return IE_ENUM if @string is not valid enum member */
2195 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2196 isds_hash_algorithm *algorithm) {
2197 if (!string || !algorithm) return IE_INVAL;
2199 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2200 *algorithm = HASH_ALGORITHM_MD5;
2201 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2202 *algorithm = HASH_ALGORITHM_SHA_1;
2203 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2204 *algorithm = HASH_ALGORITHM_SHA_224;
2205 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2206 *algorithm = HASH_ALGORITHM_SHA_256;
2207 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2208 *algorithm = HASH_ALGORITHM_SHA_384;
2209 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2210 *algorithm = HASH_ALGORITHM_SHA_512;
2211 else
2212 return IE_ENUM;
2213 return IE_SUCCESS;
2217 #if HAVE_LIBCURL
2218 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2219 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2220 if (!time || !string) return IE_INVAL;
2222 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2223 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2224 return IE_ERROR;
2226 return IE_SUCCESS;
2230 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2231 * respects the @time microseconds too. */
2232 static isds_error timeval2timestring(const struct timeval *time,
2233 xmlChar **string) {
2234 struct tm broken;
2235 time_t seconds_as_time_t;
2237 if (!time || !string) return IE_INVAL;
2239 /* MinGW32 GCC 4.8+ uses 64-bit time_t but time->tv_sec is defined as
2240 * 32-bit long in Microsoft API. Convert value to the type expected by
2241 * gmtime_r(). */
2242 seconds_as_time_t = time->tv_sec;
2243 if (!gmtime_r(&seconds_as_time_t, &broken)) return IE_DATE;
2244 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2246 /* TODO: small negative year should be formatted as "-0012". This is not
2247 * true for glibc "%04d". We should implement it.
2248 * time->tv_usec type is su_seconds_t which is required to be signed
2249 * integer to accomodate values from range [-1, 1000000].
2250 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2251 /* XXX: Do not format time->tv_usec as intmax_t because %jd is not
2252 * supported and PRIdMAX is broken on MingGW. We can use int32_t because
2253 * of the range check above. */
2254 if (-1 == isds_asprintf((char **) string,
2255 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRId32,
2256 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2257 broken.tm_hour, broken.tm_min, broken.tm_sec,
2258 (int32_t)time->tv_usec))
2259 return IE_ERROR;
2261 return IE_SUCCESS;
2263 #endif /* HAVE_LIBCURL */
2266 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2267 * It respects microseconds too. Microseconds are rounded half up.
2268 * In case of error, @time will be freed. */
2269 static isds_error timestring2timeval(const xmlChar *string,
2270 struct timeval **time) {
2271 struct tm broken;
2272 char *offset, *delim, *endptr;
2273 const int subsecond_resolution = 6;
2274 char subseconds[subsecond_resolution + 1];
2275 _Bool round_up = 0;
2276 int offset_hours, offset_minutes;
2277 int i;
2278 long int long_number;
2279 #ifdef _WIN32
2280 int tmp;
2281 #endif
2283 if (!time) return IE_INVAL;
2284 if (!string) {
2285 zfree(*time);
2286 return IE_INVAL;
2289 memset(&broken, 0, sizeof(broken));
2291 if (!*time) {
2292 *time = calloc(1, sizeof(**time));
2293 if (!*time) return IE_NOMEM;
2294 } else {
2295 memset(*time, 0, sizeof(**time));
2299 /* xsd:date is ISO 8601 string, thus ASCII */
2300 /*TODO: negative year */
2302 #ifdef _WIN32
2303 i = 0;
2304 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2305 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2306 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2307 &i)) < 6) {
2308 zfree(*time);
2309 return IE_DATE;
2312 broken.tm_year -= 1900;
2313 broken.tm_mon--;
2314 broken.tm_isdst = -1;
2315 offset = (char*)string + i;
2316 #else
2317 /* Parse date and time without subseconds and offset */
2318 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2319 if (!offset) {
2320 zfree(*time);
2321 return IE_DATE;
2323 #endif
2325 /* Get subseconds */
2326 if (*offset == '.' ) {
2327 offset++;
2329 /* Copy first 6 digits, pad it with zeros.
2330 * Current server implementation uses only millisecond resolution. */
2331 /* TODO: isdigit() is locale sensitive */
2332 for (i = 0;
2333 i < subsecond_resolution && isdigit(*offset);
2334 i++, offset++) {
2335 subseconds[i] = *offset;
2337 if (subsecond_resolution == i && isdigit(*offset)) {
2338 /* Check 7th digit for rounding */
2339 if (*offset >= '5') round_up = 1;
2340 offset++;
2342 for (; i < subsecond_resolution; i++) {
2343 subseconds[i] = '0';
2345 subseconds[subsecond_resolution] = '\0';
2347 /* Convert it into integer */
2348 long_number = strtol(subseconds, &endptr, 10);
2349 if (*endptr != '\0' || long_number == LONG_MIN ||
2350 long_number == LONG_MAX) {
2351 zfree(*time);
2352 return IE_DATE;
2354 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2355 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2356 * microseconds" and "the type shall be a signed integer capable of
2357 * storing values at least in the range [-1, 1000000]. */
2358 if (long_number < -1 || long_number >= 1000000) {
2359 zfree(*time);
2360 return IE_DATE;
2362 (*time)->tv_usec = long_number;
2364 /* Round the subseconds */
2365 if (round_up) {
2366 if (999999 == (*time)->tv_usec) {
2367 (*time)->tv_usec = 0;
2368 broken.tm_sec++;
2369 } else {
2370 (*time)->tv_usec++;
2374 /* move to the zone offset delimiter or signal NULL*/
2375 delim = strchr(offset, '-');
2376 if (!delim)
2377 delim = strchr(offset, '+');
2378 if (!delim)
2379 delim = strchr(offset, 'Z');
2380 offset = delim;
2383 /* Get zone offset */
2384 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2385 * "" equals to "Z" and it means UTC zone. */
2386 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2387 * colon separator */
2388 if (offset && (*offset == '-' || *offset == '+')) {
2389 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2390 zfree(*time);
2391 return IE_DATE;
2393 if (*offset == '+') {
2394 broken.tm_hour -= offset_hours;
2395 broken.tm_min -= offset_minutes;
2396 } else {
2397 broken.tm_hour += offset_hours;
2398 broken.tm_min += offset_minutes;
2402 /* Convert to time_t */
2403 (*time)->tv_sec = _isds_timegm(&broken);
2404 if ((*time)->tv_sec == (time_t) -1) {
2405 zfree(*time);
2406 return IE_DATE;
2409 return IE_SUCCESS;
2413 /* Convert unsigned int into isds_message_status.
2414 * @context is session context
2415 * @number is pointer to number value. NULL will be treated as invalid value.
2416 * @status is automatically reallocated status
2417 * @return IE_SUCCESS, or error code and free status */
2418 static isds_error uint2isds_message_status(struct isds_ctx *context,
2419 const unsigned long int *number, isds_message_status **status) {
2420 if (!context) return IE_INVALID_CONTEXT;
2421 if (!status) return IE_INVAL;
2423 free(*status); *status = NULL;
2424 if (!number) return IE_INVAL;
2426 if (*number < 1 || *number > 10) {
2427 isds_printf_message(context, _("Invalid message status value: %lu"),
2428 *number);
2429 return IE_ENUM;
2432 *status = malloc(sizeof(**status));
2433 if (!*status) return IE_NOMEM;
2435 **status = 1 << *number;
2436 return IE_SUCCESS;
2440 /* Convert event description string into isds_event members type and
2441 * description
2442 * @string is raw event description starting with event prefix
2443 * @event is structure where to store type and stripped description to
2444 * @return standard error code, unknown prefix is not classified as an error.
2445 * */
2446 static isds_error eventstring2event(const xmlChar *string,
2447 struct isds_event* event) {
2448 const xmlChar *known_prefixes[] = {
2449 BAD_CAST "EV0:",
2450 BAD_CAST "EV1:",
2451 BAD_CAST "EV2:",
2452 BAD_CAST "EV3:",
2453 BAD_CAST "EV4:",
2454 BAD_CAST "EV5:",
2455 BAD_CAST "EV11:",
2456 BAD_CAST "EV12:",
2457 BAD_CAST "EV13:"
2459 const isds_event_type types[] = {
2460 EVENT_ENTERED_SYSTEM,
2461 EVENT_ACCEPTED_BY_RECIPIENT,
2462 EVENT_ACCEPTED_BY_FICTION,
2463 EVENT_UNDELIVERABLE,
2464 EVENT_COMMERCIAL_ACCEPTED,
2465 EVENT_DELIVERED,
2466 EVENT_PRIMARY_LOGIN,
2467 EVENT_ENTRUSTED_LOGIN,
2468 EVENT_SYSCERT_LOGIN
2470 unsigned int index;
2471 size_t length;
2473 if (!string || !event) return IE_INVAL;
2475 if (!event->type) {
2476 event->type = malloc(sizeof(*event->type));
2477 if (!(event->type)) return IE_NOMEM;
2479 zfree(event->description);
2481 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2482 index++) {
2483 length = xmlUTF8Strlen(known_prefixes[index]);
2485 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2486 /* Prefix is known */
2487 *event->type = types[index];
2489 /* Strip prefix from description and spaces */
2490 /* TODO: Recognize all white spaces from UCS blank class and
2491 * operate on UTF-8 chars. */
2492 for (; string[length] != '\0' && string[length] == ' '; length++);
2493 event->description = strdup((char *) (string + length));
2494 if (!(event->description)) return IE_NOMEM;
2496 return IE_SUCCESS;
2500 /* Unknown event prefix.
2501 * XSD allows any string */
2502 char *string_locale = _isds_utf82locale((char *) string);
2503 isds_log(ILF_ISDS, ILL_WARNING,
2504 _("Unknown delivery info event prefix: %s\n"), string_locale);
2505 free(string_locale);
2507 *event->type = EVENT_UKNOWN;
2508 event->description = strdup((char *) string);
2509 if (!(event->description)) return IE_NOMEM;
2511 return IE_SUCCESS;
2515 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2516 * and leave label */
2517 #define EXTRACT_STRING(element, string) { \
2518 xmlXPathFreeObject(result); \
2519 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2520 if (NULL == (result)) { \
2521 err = IE_ERROR; \
2522 goto leave; \
2524 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2525 if (result->nodesetval->nodeNr > 1) { \
2526 isds_printf_message(context, _("Multiple %s element"), element); \
2527 err = IE_ERROR; \
2528 goto leave; \
2530 (string) = (char *) \
2531 xmlXPathCastNodeSetToString(result->nodesetval); \
2532 if (NULL == (string)) { \
2533 err = IE_ERROR; \
2534 goto leave; \
2539 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2541 char *string = NULL; \
2542 EXTRACT_STRING(element, string); \
2544 if (string) { \
2545 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2546 if (!(booleanPtr)) { \
2547 free(string); \
2548 err = IE_NOMEM; \
2549 goto leave; \
2552 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2553 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2554 *(booleanPtr) = 1; \
2555 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2556 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2557 *(booleanPtr) = 0; \
2558 else { \
2559 char *string_locale = _isds_utf82locale((char*)string); \
2560 isds_printf_message(context, \
2561 _("%s value is not valid boolean: %s"), \
2562 element, string_locale); \
2563 free(string_locale); \
2564 free(string); \
2565 err = IE_ERROR; \
2566 goto leave; \
2569 free(string); \
2573 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2575 char *string = NULL; \
2576 EXTRACT_STRING(element, string); \
2578 if (NULL == string) { \
2579 isds_printf_message(context, _("%s element is empty"), element); \
2580 err = IE_ERROR; \
2581 goto leave; \
2583 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2584 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2585 (boolean) = 1; \
2586 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2587 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2588 (boolean) = 0; \
2589 else { \
2590 char *string_locale = _isds_utf82locale((char*)string); \
2591 isds_printf_message(context, \
2592 _("%s value is not valid boolean: %s"), \
2593 element, string_locale); \
2594 free(string_locale); \
2595 free(string); \
2596 err = IE_ERROR; \
2597 goto leave; \
2600 free(string); \
2603 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2605 char *string = NULL; \
2606 EXTRACT_STRING(element, string); \
2607 if (string) { \
2608 long int number; \
2609 char *endptr; \
2611 number = strtol((char*)string, &endptr, 10); \
2613 if (*endptr != '\0') { \
2614 char *string_locale = _isds_utf82locale((char *)string); \
2615 isds_printf_message(context, \
2616 _("%s is not valid integer: %s"), \
2617 element, string_locale); \
2618 free(string_locale); \
2619 free(string); \
2620 err = IE_ISDS; \
2621 goto leave; \
2624 if (number == LONG_MIN || number == LONG_MAX) { \
2625 char *string_locale = _isds_utf82locale((char *)string); \
2626 isds_printf_message(context, \
2627 _("%s value out of range of long int: %s"), \
2628 element, string_locale); \
2629 free(string_locale); \
2630 free(string); \
2631 err = IE_ERROR; \
2632 goto leave; \
2635 free(string); string = NULL; \
2637 if (!(preallocated)) { \
2638 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2639 if (!(longintPtr)) { \
2640 err = IE_NOMEM; \
2641 goto leave; \
2644 *(longintPtr) = number; \
2648 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2650 char *string = NULL; \
2651 EXTRACT_STRING(element, string); \
2652 if (string) { \
2653 long int number; \
2654 char *endptr; \
2656 number = strtol((char*)string, &endptr, 10); \
2658 if (*endptr != '\0') { \
2659 char *string_locale = _isds_utf82locale((char *)string); \
2660 isds_printf_message(context, \
2661 _("%s is not valid integer: %s"), \
2662 element, string_locale); \
2663 free(string_locale); \
2664 free(string); \
2665 err = IE_ISDS; \
2666 goto leave; \
2669 if (number == LONG_MIN || number == LONG_MAX) { \
2670 char *string_locale = _isds_utf82locale((char *)string); \
2671 isds_printf_message(context, \
2672 _("%s value out of range of long int: %s"), \
2673 element, string_locale); \
2674 free(string_locale); \
2675 free(string); \
2676 err = IE_ERROR; \
2677 goto leave; \
2680 free(string); string = NULL; \
2681 if (number < 0) { \
2682 isds_printf_message(context, \
2683 _("%s value is negative: %ld"), element, number); \
2684 err = IE_ERROR; \
2685 goto leave; \
2688 if (!(preallocated)) { \
2689 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2690 if (!(ulongintPtr)) { \
2691 err = IE_NOMEM; \
2692 goto leave; \
2695 *(ulongintPtr) = number; \
2699 #define EXTRACT_DATE(element, tmPtr) { \
2700 char *string = NULL; \
2701 EXTRACT_STRING(element, string); \
2702 if (NULL != string) { \
2703 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2704 if (NULL == (tmPtr)) { \
2705 free(string); \
2706 err = IE_NOMEM; \
2707 goto leave; \
2709 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2710 if (err) { \
2711 if (err == IE_NOTSUP) { \
2712 err = IE_ISDS; \
2713 char *string_locale = _isds_utf82locale(string); \
2714 char *element_locale = _isds_utf82locale(element); \
2715 isds_printf_message(context, _("Invalid %s value: %s"), \
2716 element_locale, string_locale); \
2717 free(string_locale); \
2718 free(element_locale); \
2720 free(string); \
2721 goto leave; \
2723 free(string); \
2727 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2728 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2729 NULL); \
2730 if ((required) && (!string)) { \
2731 char *attribute_locale = _isds_utf82locale(attribute); \
2732 char *element_locale = \
2733 _isds_utf82locale((char *)xpath_ctx->node->name); \
2734 isds_printf_message(context, \
2735 _("Could not extract required %s attribute value from " \
2736 "%s element"), attribute_locale, element_locale); \
2737 free(element_locale); \
2738 free(attribute_locale); \
2739 err = IE_ERROR; \
2740 goto leave; \
2745 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2747 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2748 (xmlChar *) (string)); \
2749 if (!node) { \
2750 isds_printf_message(context, \
2751 _("Could not add %s child to %s element"), \
2752 element, (parent)->name); \
2753 err = IE_ERROR; \
2754 goto leave; \
2758 #define INSERT_STRING(parent, element, string) \
2759 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2761 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2763 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2764 else { INSERT_STRING(parent, element, "false"); } \
2767 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2769 if (booleanPtr) { \
2770 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2771 } else { \
2772 INSERT_STRING(parent, element, NULL); \
2776 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2777 if ((longintPtr)) { \
2778 /* FIXME: locale sensitive */ \
2779 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2780 err = IE_NOMEM; \
2781 goto leave; \
2783 INSERT_STRING(parent, element, buffer) \
2784 free(buffer); (buffer) = NULL; \
2785 } else { INSERT_STRING(parent, element, NULL) } \
2788 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2789 if ((ulongintPtr)) { \
2790 /* FIXME: locale sensitive */ \
2791 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2792 err = IE_NOMEM; \
2793 goto leave; \
2795 INSERT_STRING(parent, element, buffer) \
2796 free(buffer); (buffer) = NULL; \
2797 } else { INSERT_STRING(parent, element, NULL) } \
2800 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2802 /* FIXME: locale sensitive */ \
2803 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2804 err = IE_NOMEM; \
2805 goto leave; \
2807 INSERT_STRING(parent, element, buffer) \
2808 free(buffer); (buffer) = NULL; \
2811 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2812 * new attribute. */
2813 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2815 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2816 (xmlChar *) (string)); \
2817 if (!attribute_node) { \
2818 isds_printf_message(context, _("Could not add %s " \
2819 "attribute to %s element"), \
2820 (attribute), (parent)->name); \
2821 err = IE_ERROR; \
2822 goto leave; \
2826 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2827 if (string) { \
2828 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2829 if (length > (maximum)) { \
2830 isds_printf_message(context, \
2831 ngettext("%s has more than %d characters", \
2832 "%s has more than %d characters", (maximum)), \
2833 (name), (maximum)); \
2834 err = IE_2BIG; \
2835 goto leave; \
2837 if (length < (minimum)) { \
2838 isds_printf_message(context, \
2839 ngettext("%s has less than %d characters", \
2840 "%s has less than %d characters", (minimum)), \
2841 (name), (minimum)); \
2842 err = IE_2SMALL; \
2843 goto leave; \
2848 #define INSERT_ELEMENT(child, parent, element) \
2850 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2851 if (!(child)) { \
2852 isds_printf_message(context, \
2853 _("Could not add %s child to %s element"), \
2854 (element), (parent)->name); \
2855 err = IE_ERROR; \
2856 goto leave; \
2861 /* Find child element by name in given XPath context and switch context onto
2862 * it. The child must be uniq and must exist. Otherwise fails.
2863 * @context is ISDS context
2864 * @child is child element name
2865 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2866 * into it child. In error case, the @xpath_ctx keeps original value. */
2867 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2868 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2869 isds_error err = IE_SUCCESS;
2870 xmlXPathObjectPtr result = NULL;
2872 if (!context) return IE_INVALID_CONTEXT;
2873 if (!child || !xpath_ctx) return IE_INVAL;
2875 /* Find child */
2876 result = xmlXPathEvalExpression(child, xpath_ctx);
2877 if (!result) {
2878 err = IE_XML;
2879 goto leave;
2882 /* No match */
2883 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2884 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2885 char *child_locale = _isds_utf82locale((char*) child);
2886 isds_printf_message(context,
2887 _("%s element does not contain %s child"),
2888 parent_locale, child_locale);
2889 free(child_locale);
2890 free(parent_locale);
2891 err = IE_NOEXIST;
2892 goto leave;
2895 /* More matches */
2896 if (result->nodesetval->nodeNr > 1) {
2897 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2898 char *child_locale = _isds_utf82locale((char*) child);
2899 isds_printf_message(context,
2900 _("%s element contains multiple %s children"),
2901 parent_locale, child_locale);
2902 free(child_locale);
2903 free(parent_locale);
2904 err = IE_NOTUNIQ;
2905 goto leave;
2908 /* Switch context */
2909 xpath_ctx->node = result->nodesetval->nodeTab[0];
2911 leave:
2912 xmlXPathFreeObject(result);
2913 return err;
2918 #if HAVE_LIBCURL
2919 /* Find and convert XSD:gPersonName group in current node into structure
2920 * @context is ISDS context
2921 * @personName is automatically reallocated person name structure. If no member
2922 * value is found, will be freed.
2923 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2924 * elements
2925 * In case of error @personName will be freed. */
2926 static isds_error extract_gPersonName(struct isds_ctx *context,
2927 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2928 isds_error err = IE_SUCCESS;
2929 xmlXPathObjectPtr result = NULL;
2931 if (!context) return IE_INVALID_CONTEXT;
2932 if (!personName) return IE_INVAL;
2933 isds_PersonName_free(personName);
2934 if (!xpath_ctx) return IE_INVAL;
2937 *personName = calloc(1, sizeof(**personName));
2938 if (!*personName) {
2939 err = IE_NOMEM;
2940 goto leave;
2943 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2944 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2945 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2946 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2948 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2949 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2950 isds_PersonName_free(personName);
2952 leave:
2953 if (err) isds_PersonName_free(personName);
2954 xmlXPathFreeObject(result);
2955 return err;
2959 /* Find and convert XSD:gAddress group in current node into structure
2960 * @context is ISDS context
2961 * @address is automatically reallocated address structure. If no member
2962 * value is found, will be freed.
2963 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2964 * elements
2965 * In case of error @address will be freed. */
2966 static isds_error extract_gAddress(struct isds_ctx *context,
2967 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2968 isds_error err = IE_SUCCESS;
2969 xmlXPathObjectPtr result = NULL;
2971 if (!context) return IE_INVALID_CONTEXT;
2972 if (!address) return IE_INVAL;
2973 isds_Address_free(address);
2974 if (!xpath_ctx) return IE_INVAL;
2977 *address = calloc(1, sizeof(**address));
2978 if (!*address) {
2979 err = IE_NOMEM;
2980 goto leave;
2983 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2984 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2985 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2986 EXTRACT_STRING("isds:adNumberInMunicipality",
2987 (*address)->adNumberInMunicipality);
2988 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2989 EXTRACT_STRING("isds:adState", (*address)->adState);
2991 if (!(*address)->adCity && !(*address)->adStreet &&
2992 !(*address)->adNumberInStreet &&
2993 !(*address)->adNumberInMunicipality &&
2994 !(*address)->adZipCode && !(*address)->adState)
2995 isds_Address_free(address);
2997 leave:
2998 if (err) isds_Address_free(address);
2999 xmlXPathFreeObject(result);
3000 return err;
3004 /* Find and convert isds:biDate element in current node into structure
3005 * @context is ISDS context
3006 * @biDate is automatically reallocated birth date structure. If no member
3007 * value is found, will be freed.
3008 * @xpath_ctx is XPath context with current node as parent for isds:biDate
3009 * element
3010 * In case of error @biDate will be freed. */
3011 static isds_error extract_BiDate(struct isds_ctx *context,
3012 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
3013 isds_error err = IE_SUCCESS;
3014 xmlXPathObjectPtr result = NULL;
3015 char *string = NULL;
3017 if (!context) return IE_INVALID_CONTEXT;
3018 if (!biDate) return IE_INVAL;
3019 zfree(*biDate);
3020 if (!xpath_ctx) return IE_INVAL;
3022 EXTRACT_STRING("isds:biDate", string);
3023 if (string) {
3024 *biDate = calloc(1, sizeof(**biDate));
3025 if (!*biDate) {
3026 err = IE_NOMEM;
3027 goto leave;
3029 err = _isds_datestring2tm((xmlChar *)string, *biDate);
3030 if (err) {
3031 if (err == IE_NOTSUP) {
3032 err = IE_ISDS;
3033 char *string_locale = _isds_utf82locale(string);
3034 isds_printf_message(context,
3035 _("Invalid isds:biDate value: %s"), string_locale);
3036 free(string_locale);
3038 goto leave;
3042 leave:
3043 if (err) zfree(*biDate);
3044 free(string);
3045 xmlXPathFreeObject(result);
3046 return err;
3050 /* Convert isds:dBOwnerInfo XML tree into structure
3051 * @context is ISDS context
3052 * @db_owner_info is automatically reallocated box owner info structure
3053 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
3054 * In case of error @db_owner_info will be freed. */
3055 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
3056 struct isds_DbOwnerInfo **db_owner_info,
3057 xmlXPathContextPtr xpath_ctx) {
3058 isds_error err = IE_SUCCESS;
3059 xmlXPathObjectPtr result = NULL;
3060 char *string = NULL;
3062 if (!context) return IE_INVALID_CONTEXT;
3063 if (!db_owner_info) return IE_INVAL;
3064 isds_DbOwnerInfo_free(db_owner_info);
3065 if (!xpath_ctx) return IE_INVAL;
3068 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3069 if (!*db_owner_info) {
3070 err = IE_NOMEM;
3071 goto leave;
3074 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
3076 EXTRACT_STRING("isds:dbType", string);
3077 if (string) {
3078 (*db_owner_info)->dbType =
3079 calloc(1, sizeof(*((*db_owner_info)->dbType)));
3080 if (!(*db_owner_info)->dbType) {
3081 err = IE_NOMEM;
3082 goto leave;
3084 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
3085 if (err) {
3086 zfree((*db_owner_info)->dbType);
3087 if (err == IE_ENUM) {
3088 err = IE_ISDS;
3089 char *string_locale = _isds_utf82locale(string);
3090 isds_printf_message(context, _("Unknown isds:dbType: %s"),
3091 string_locale);
3092 free(string_locale);
3094 goto leave;
3096 zfree(string);
3099 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
3101 err = extract_gPersonName(context, &(*db_owner_info)->personName,
3102 xpath_ctx);
3103 if (err) goto leave;
3105 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
3107 (*db_owner_info)->birthInfo =
3108 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
3109 if (!(*db_owner_info)->birthInfo) {
3110 err = IE_NOMEM;
3111 goto leave;
3113 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
3114 xpath_ctx);
3115 if (err) goto leave;
3116 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3117 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3118 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3119 if (!(*db_owner_info)->birthInfo->biDate &&
3120 !(*db_owner_info)->birthInfo->biCity &&
3121 !(*db_owner_info)->birthInfo->biCounty &&
3122 !(*db_owner_info)->birthInfo->biState)
3123 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3125 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3126 if (err) goto leave;
3128 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3129 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3130 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3131 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3132 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3134 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3136 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3137 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3138 (*db_owner_info)->dbOpenAddressing);
3140 leave:
3141 if (err) isds_DbOwnerInfo_free(db_owner_info);
3142 free(string);
3143 xmlXPathFreeObject(result);
3144 return err;
3148 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3149 * @context is session context
3150 * @owner is libisds structure with box description
3151 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3152 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3153 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
3155 isds_error err = IE_SUCCESS;
3156 xmlNodePtr node;
3157 xmlChar *string = NULL;
3158 const xmlChar *type_string = NULL;
3160 if (!context) return IE_INVALID_CONTEXT;
3161 if (!owner || !db_owner_info) return IE_INVAL;
3164 /* Build XSD:tDbOwnerInfo */
3165 /* XXX: All the elements except email and telNumber are mandatory. */
3166 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3167 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3169 /* dbType */
3170 if (owner->dbType) {
3171 type_string = isds_DbType2string(*(owner->dbType));
3172 if (!type_string) {
3173 isds_printf_message(context, _("Invalid dbType value: %d"),
3174 *(owner->dbType));
3175 err = IE_ENUM;
3176 goto leave;
3179 INSERT_STRING(db_owner_info, "dbType", type_string);
3181 INSERT_STRING(db_owner_info, "ic", owner->ic);
3183 INSERT_STRING(db_owner_info, "pnFirstName",
3184 (NULL == owner->personName) ? NULL: owner->personName->pnFirstName);
3185 INSERT_STRING(db_owner_info, "pnMiddleName",
3186 (NULL == owner->personName) ? NULL: owner->personName->pnMiddleName);
3187 INSERT_STRING(db_owner_info, "pnLastName",
3188 (NULL == owner->personName) ? NULL: owner->personName->pnLastName);
3189 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3190 (NULL == owner->personName) ? NULL:
3191 owner->personName->pnLastNameAtBirth);
3193 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3195 if (NULL != owner->birthInfo && NULL != owner->birthInfo->biDate) {
3196 err = tm2datestring(owner->birthInfo->biDate, &string);
3197 if (err) goto leave;
3199 INSERT_STRING(db_owner_info, "biDate", string);
3200 zfree(string);
3202 INSERT_STRING(db_owner_info, "biCity",
3203 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biCity);
3204 INSERT_STRING(db_owner_info, "biCounty",
3205 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biCounty);
3206 INSERT_STRING(db_owner_info, "biState",
3207 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biState);
3209 INSERT_STRING(db_owner_info, "adCity",
3210 (NULL == owner->address) ? NULL: owner->address->adCity);
3211 INSERT_STRING(db_owner_info, "adStreet",
3212 (NULL == owner->address) ? NULL: owner->address->adStreet);
3213 INSERT_STRING(db_owner_info, "adNumberInStreet",
3214 (NULL == owner->address) ? NULL: owner->address->adNumberInStreet);
3215 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3216 (NULL == owner->address) ? NULL: owner->address->adNumberInMunicipality);
3217 INSERT_STRING(db_owner_info, "adZipCode",
3218 (NULL == owner->address) ? NULL: owner->address->adZipCode);
3219 INSERT_STRING(db_owner_info, "adState",
3220 (NULL == owner->address) ? NULL: owner->address->adState);
3222 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3223 INSERT_STRING(db_owner_info, "email", owner->email);
3224 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3226 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3227 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3229 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3230 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3232 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3234 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3235 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3236 owner->dbOpenAddressing);
3238 leave:
3239 free(string);
3240 return err;
3244 /* Convert XSD:tDbUserInfo XML tree into structure
3245 * @context is ISDS context
3246 * @db_user_info is automatically reallocated user info structure
3247 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3248 * In case of error @db_user_info will be freed. */
3249 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3250 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3251 isds_error err = IE_SUCCESS;
3252 xmlXPathObjectPtr result = NULL;
3253 char *string = NULL;
3255 if (!context) return IE_INVALID_CONTEXT;
3256 if (!db_user_info) return IE_INVAL;
3257 isds_DbUserInfo_free(db_user_info);
3258 if (!xpath_ctx) return IE_INVAL;
3261 *db_user_info = calloc(1, sizeof(**db_user_info));
3262 if (!*db_user_info) {
3263 err = IE_NOMEM;
3264 goto leave;
3267 EXTRACT_STRING_ATTRIBUTE("AIFOTicket", (*db_user_info)->aifo_ticket, 0);
3269 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3271 EXTRACT_STRING("isds:userType", string);
3272 if (string) {
3273 (*db_user_info)->userType =
3274 calloc(1, sizeof(*((*db_user_info)->userType)));
3275 if (!(*db_user_info)->userType) {
3276 err = IE_NOMEM;
3277 goto leave;
3279 err = string2isds_UserType((xmlChar *)string,
3280 (*db_user_info)->userType);
3281 if (err) {
3282 zfree((*db_user_info)->userType);
3283 if (err == IE_ENUM) {
3284 err = IE_ISDS;
3285 char *string_locale = _isds_utf82locale(string);
3286 isds_printf_message(context,
3287 _("Unknown isds:userType value: %s"), string_locale);
3288 free(string_locale);
3290 goto leave;
3292 zfree(string);
3295 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3297 (*db_user_info)->personName =
3298 calloc(1, sizeof(*((*db_user_info)->personName)));
3299 if (!(*db_user_info)->personName) {
3300 err = IE_NOMEM;
3301 goto leave;
3304 err = extract_gPersonName(context, &(*db_user_info)->personName,
3305 xpath_ctx);
3306 if (err) goto leave;
3308 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3309 if (err) goto leave;
3311 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3312 if (err) goto leave;
3314 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3315 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3317 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3318 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3319 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3321 /* ???: Default value is "CZ" according specification. Should we provide
3322 * it? */
3323 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3325 leave:
3326 if (err) isds_DbUserInfo_free(db_user_info);
3327 free(string);
3328 xmlXPathFreeObject(result);
3329 return err;
3333 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3334 * @context is session context
3335 * @user is libisds structure with user description
3336 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
3337 * @db_user_info is XML element of XSD:tDbUserInfo */
3338 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3339 const struct isds_DbUserInfo *user, _Bool honor_aifo_ticket,
3340 xmlNodePtr db_user_info) {
3342 isds_error err = IE_SUCCESS;
3343 xmlNodePtr node;
3344 xmlAttrPtr attribute_node;
3345 xmlChar *string = NULL;
3347 if (!context) return IE_INVALID_CONTEXT;
3348 if (!user || !db_user_info) return IE_INVAL;
3350 /* Build XSD:tDbUserInfo */
3352 /* XXX: Insert AIFOTicket attribute only when allowed. XML schema does not
3353 * allow it everywhere. */
3354 if (honor_aifo_ticket && user->aifo_ticket) {
3355 INSERT_STRING_ATTRIBUTE(db_user_info, "AIFOTicket", user->aifo_ticket);
3358 if (user->personName) {
3359 INSERT_STRING(db_user_info, "pnFirstName",
3360 user->personName->pnFirstName);
3361 INSERT_STRING(db_user_info, "pnMiddleName",
3362 user->personName->pnMiddleName);
3363 INSERT_STRING(db_user_info, "pnLastName",
3364 user->personName->pnLastName);
3365 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3366 user->personName->pnLastNameAtBirth);
3368 if (user->address) {
3369 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3370 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3371 INSERT_STRING(db_user_info, "adNumberInStreet",
3372 user->address->adNumberInStreet);
3373 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3374 user->address->adNumberInMunicipality);
3375 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3376 INSERT_STRING(db_user_info, "adState", user->address->adState);
3378 if (user->biDate) {
3379 if (!tm2datestring(user->biDate, &string))
3380 INSERT_STRING(db_user_info, "biDate", string);
3381 zfree(string);
3383 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3384 INSERT_STRING(db_user_info, "userID", user->userID);
3386 /* userType */
3387 if (user->userType) {
3388 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3389 if (!type_string) {
3390 isds_printf_message(context, _("Invalid userType value: %d"),
3391 *(user->userType));
3392 err = IE_ENUM;
3393 goto leave;
3395 INSERT_STRING(db_user_info, "userType", type_string);
3398 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3399 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3400 INSERT_STRING(db_user_info, "ic", user->ic);
3401 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3402 INSERT_STRING(db_user_info, "firmName", user->firmName);
3403 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3404 INSERT_STRING(db_user_info, "caCity", user->caCity);
3405 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3406 INSERT_STRING(db_user_info, "caState", user->caState);
3408 leave:
3409 free(string);
3410 return err;
3414 /* Convert XSD:tPDZRec XML tree into structure
3415 * @context is ISDS context
3416 * @permission is automatically reallocated commercial permission structure
3417 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3418 * In case of error @permission will be freed. */
3419 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3420 struct isds_commercial_permission **permission,
3421 xmlXPathContextPtr xpath_ctx) {
3422 isds_error err = IE_SUCCESS;
3423 xmlXPathObjectPtr result = NULL;
3424 char *string = NULL;
3426 if (!context) return IE_INVALID_CONTEXT;
3427 if (!permission) return IE_INVAL;
3428 isds_commercial_permission_free(permission);
3429 if (!xpath_ctx) return IE_INVAL;
3432 *permission = calloc(1, sizeof(**permission));
3433 if (!*permission) {
3434 err = IE_NOMEM;
3435 goto leave;
3438 EXTRACT_STRING("isds:PDZType", string);
3439 if (string) {
3440 err = string2isds_payment_type((xmlChar *)string,
3441 &(*permission)->type);
3442 if (err) {
3443 if (err == IE_ENUM) {
3444 err = IE_ISDS;
3445 char *string_locale = _isds_utf82locale(string);
3446 isds_printf_message(context,
3447 _("Unknown isds:PDZType value: %s"), string_locale);
3448 free(string_locale);
3450 goto leave;
3452 zfree(string);
3455 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3456 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3458 EXTRACT_STRING("isds:PDZExpire", string);
3459 if (string) {
3460 err = timestring2timeval((xmlChar *) string,
3461 &((*permission)->expiration));
3462 if (err) {
3463 char *string_locale = _isds_utf82locale(string);
3464 if (err == IE_DATE) err = IE_ISDS;
3465 isds_printf_message(context,
3466 _("Could not convert PDZExpire as ISO time: %s"),
3467 string_locale);
3468 free(string_locale);
3469 goto leave;
3471 zfree(string);
3474 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3475 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3477 leave:
3478 if (err) isds_commercial_permission_free(permission);
3479 free(string);
3480 xmlXPathFreeObject(result);
3481 return err;
3485 /* Convert XSD:tCiRecord XML tree into structure
3486 * @context is ISDS context
3487 * @event is automatically reallocated commercial credit event structure
3488 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3489 * In case of error @event will be freed. */
3490 static isds_error extract_CiRecord(struct isds_ctx *context,
3491 struct isds_credit_event **event,
3492 xmlXPathContextPtr xpath_ctx) {
3493 isds_error err = IE_SUCCESS;
3494 xmlXPathObjectPtr result = NULL;
3495 char *string = NULL;
3496 long int *number_ptr;
3498 if (!context) return IE_INVALID_CONTEXT;
3499 if (!event) return IE_INVAL;
3500 isds_credit_event_free(event);
3501 if (!xpath_ctx) return IE_INVAL;
3504 *event = calloc(1, sizeof(**event));
3505 if (!*event) {
3506 err = IE_NOMEM;
3507 goto leave;
3510 EXTRACT_STRING("isds:ciEventTime", string);
3511 if (string) {
3512 err = timestring2timeval((xmlChar *) string,
3513 &(*event)->time);
3514 if (err) {
3515 char *string_locale = _isds_utf82locale(string);
3516 if (err == IE_DATE) err = IE_ISDS;
3517 isds_printf_message(context,
3518 _("Could not convert ciEventTime as ISO time: %s"),
3519 string_locale);
3520 free(string_locale);
3521 goto leave;
3523 zfree(string);
3526 EXTRACT_STRING("isds:ciEventType", string);
3527 if (string) {
3528 err = string2isds_credit_event_type((xmlChar *)string,
3529 &(*event)->type);
3530 if (err) {
3531 if (err == IE_ENUM) {
3532 err = IE_ISDS;
3533 char *string_locale = _isds_utf82locale(string);
3534 isds_printf_message(context,
3535 _("Unknown isds:ciEventType value: %s"), string_locale);
3536 free(string_locale);
3538 goto leave;
3540 zfree(string);
3543 number_ptr = &((*event)->credit_change);
3544 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3545 number_ptr = &(*event)->new_credit;
3546 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3548 switch((*event)->type) {
3549 case ISDS_CREDIT_CHARGED:
3550 EXTRACT_STRING("isds:ciTransID",
3551 (*event)->details.charged.transaction);
3552 break;
3553 case ISDS_CREDIT_DISCHARGED:
3554 EXTRACT_STRING("isds:ciTransID",
3555 (*event)->details.discharged.transaction);
3556 break;
3557 case ISDS_CREDIT_MESSAGE_SENT:
3558 EXTRACT_STRING("isds:ciRecipientID",
3559 (*event)->details.message_sent.recipient);
3560 EXTRACT_STRING("isds:ciPDZID",
3561 (*event)->details.message_sent.message_id);
3562 break;
3563 case ISDS_CREDIT_STORAGE_SET:
3564 number_ptr = &((*event)->details.storage_set.new_capacity);
3565 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3566 EXTRACT_DATE("isds:ciNewFrom",
3567 (*event)->details.storage_set.new_valid_from);
3568 EXTRACT_DATE("isds:ciNewTo",
3569 (*event)->details.storage_set.new_valid_to);
3570 EXTRACT_LONGINT("isds:ciOldCapacity",
3571 (*event)->details.storage_set.old_capacity, 0);
3572 EXTRACT_DATE("isds:ciOldFrom",
3573 (*event)->details.storage_set.old_valid_from);
3574 EXTRACT_DATE("isds:ciOldTo",
3575 (*event)->details.storage_set.old_valid_to);
3576 EXTRACT_STRING("isds:ciDoneBy",
3577 (*event)->details.storage_set.initiator);
3578 break;
3579 case ISDS_CREDIT_EXPIRED:
3580 break;
3583 leave:
3584 if (err) isds_credit_event_free(event);
3585 free(string);
3586 xmlXPathFreeObject(result);
3587 return err;
3591 #endif /* HAVE_LIBCURL */
3594 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3595 * isds_envelope structure. The envelope is automatically allocated but not
3596 * reallocated. The date are just appended into envelope structure.
3597 * @context is ISDS context
3598 * @envelope is automatically allocated message envelope structure
3599 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3600 * In case of error @envelope will be freed. */
3601 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3602 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3603 isds_error err = IE_SUCCESS;
3604 xmlXPathObjectPtr result = NULL;
3606 if (!context) return IE_INVALID_CONTEXT;
3607 if (!envelope) return IE_INVAL;
3608 if (!xpath_ctx) return IE_INVAL;
3611 if (!*envelope) {
3612 /* Allocate envelope */
3613 *envelope = calloc(1, sizeof(**envelope));
3614 if (!*envelope) {
3615 err = IE_NOMEM;
3616 goto leave;
3618 } else {
3619 /* Else free former data */
3620 zfree((*envelope)->dmSenderOrgUnit);
3621 zfree((*envelope)->dmSenderOrgUnitNum);
3622 zfree((*envelope)->dbIDRecipient);
3623 zfree((*envelope)->dmRecipientOrgUnit);
3624 zfree((*envelope)->dmRecipientOrgUnitNum);
3625 zfree((*envelope)->dmToHands);
3626 zfree((*envelope)->dmAnnotation);
3627 zfree((*envelope)->dmRecipientRefNumber);
3628 zfree((*envelope)->dmSenderRefNumber);
3629 zfree((*envelope)->dmRecipientIdent);
3630 zfree((*envelope)->dmSenderIdent);
3631 zfree((*envelope)->dmLegalTitleLaw);
3632 zfree((*envelope)->dmLegalTitleYear);
3633 zfree((*envelope)->dmLegalTitleSect);
3634 zfree((*envelope)->dmLegalTitlePar);
3635 zfree((*envelope)->dmLegalTitlePoint);
3636 zfree((*envelope)->dmPersonalDelivery);
3637 zfree((*envelope)->dmAllowSubstDelivery);
3640 /* Extract envelope elements added by sender or ISDS
3641 * (XSD: gMessageEnvelopeSub type) */
3642 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3643 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3644 (*envelope)->dmSenderOrgUnitNum, 0);
3645 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3646 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3647 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3648 (*envelope)->dmRecipientOrgUnitNum, 0);
3649 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3650 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3651 EXTRACT_STRING("isds:dmRecipientRefNumber",
3652 (*envelope)->dmRecipientRefNumber);
3653 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3654 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3655 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3657 /* Extract envelope elements regarding law reference */
3658 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3659 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3660 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3661 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3662 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3664 /* Extract envelope other elements */
3665 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3666 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3667 (*envelope)->dmAllowSubstDelivery);
3669 leave:
3670 if (err) isds_envelope_free(envelope);
3671 xmlXPathFreeObject(result);
3672 return err;
3677 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3678 * isds_envelope structure. The envelope is automatically allocated but not
3679 * reallocated. The date are just appended into envelope structure.
3680 * @context is ISDS context
3681 * @envelope is automatically allocated message envelope structure
3682 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3683 * In case of error @envelope will be freed. */
3684 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3685 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3686 isds_error err = IE_SUCCESS;
3687 xmlXPathObjectPtr result = NULL;
3689 if (!context) return IE_INVALID_CONTEXT;
3690 if (!envelope) return IE_INVAL;
3691 if (!xpath_ctx) return IE_INVAL;
3694 if (!*envelope) {
3695 /* Allocate envelope */
3696 *envelope = calloc(1, sizeof(**envelope));
3697 if (!*envelope) {
3698 err = IE_NOMEM;
3699 goto leave;
3701 } else {
3702 /* Else free former data */
3703 zfree((*envelope)->dmID);
3704 zfree((*envelope)->dbIDSender);
3705 zfree((*envelope)->dmSender);
3706 zfree((*envelope)->dmSenderAddress);
3707 zfree((*envelope)->dmSenderType);
3708 zfree((*envelope)->dmRecipient);
3709 zfree((*envelope)->dmRecipientAddress);
3710 zfree((*envelope)->dmAmbiguousRecipient);
3713 /* Extract envelope elements added by ISDS
3714 * (XSD: gMessageEnvelope type) */
3715 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3716 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3717 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3718 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3719 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3720 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3721 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3722 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3723 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3724 (*envelope)->dmAmbiguousRecipient);
3726 /* Extract envelope elements added by sender and ISDS
3727 * (XSD: gMessageEnvelope type) */
3728 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3729 if (err) goto leave;
3731 leave:
3732 if (err) isds_envelope_free(envelope);
3733 xmlXPathFreeObject(result);
3734 return err;
3738 /* Convert other envelope elements from XML tree into isds_envelope structure:
3739 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3740 * The envelope is automatically allocated but not reallocated.
3741 * The data are just appended into envelope structure.
3742 * @context is ISDS context
3743 * @envelope is automatically allocated message envelope structure
3744 * @xpath_ctx is XPath context with current node as parent desired elements
3745 * In case of error @envelope will be freed. */
3746 static isds_error append_status_size_times(struct isds_ctx *context,
3747 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3748 isds_error err = IE_SUCCESS;
3749 xmlXPathObjectPtr result = NULL;
3750 char *string = NULL;
3751 unsigned long int *unumber = NULL;
3753 if (!context) return IE_INVALID_CONTEXT;
3754 if (!envelope) return IE_INVAL;
3755 if (!xpath_ctx) return IE_INVAL;
3758 if (!*envelope) {
3759 /* Allocate new */
3760 *envelope = calloc(1, sizeof(**envelope));
3761 if (!*envelope) {
3762 err = IE_NOMEM;
3763 goto leave;
3765 } else {
3766 /* Free old data */
3767 zfree((*envelope)->dmMessageStatus);
3768 zfree((*envelope)->dmAttachmentSize);
3769 zfree((*envelope)->dmDeliveryTime);
3770 zfree((*envelope)->dmAcceptanceTime);
3774 /* dmMessageStatus element is mandatory */
3775 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3776 if (!unumber) {
3777 isds_log_message(context,
3778 _("Missing mandatory sisds:dmMessageStatus integer"));
3779 err = IE_ISDS;
3780 goto leave;
3782 err = uint2isds_message_status(context, unumber,
3783 &((*envelope)->dmMessageStatus));
3784 if (err) {
3785 if (err == IE_ENUM) err = IE_ISDS;
3786 goto leave;
3788 free(unumber); unumber = NULL;
3790 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3793 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3794 if (string) {
3795 err = timestring2timeval((xmlChar *) string,
3796 &((*envelope)->dmDeliveryTime));
3797 if (err) {
3798 char *string_locale = _isds_utf82locale(string);
3799 if (err == IE_DATE) err = IE_ISDS;
3800 isds_printf_message(context,
3801 _("Could not convert dmDeliveryTime as ISO time: %s"),
3802 string_locale);
3803 free(string_locale);
3804 goto leave;
3806 zfree(string);
3809 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3810 if (string) {
3811 err = timestring2timeval((xmlChar *) string,
3812 &((*envelope)->dmAcceptanceTime));
3813 if (err) {
3814 char *string_locale = _isds_utf82locale(string);
3815 if (err == IE_DATE) err = IE_ISDS;
3816 isds_printf_message(context,
3817 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3818 string_locale);
3819 free(string_locale);
3820 goto leave;
3822 zfree(string);
3825 leave:
3826 if (err) isds_envelope_free(envelope);
3827 free(unumber);
3828 free(string);
3829 xmlXPathFreeObject(result);
3830 return err;
3834 /* Convert message type attribute of current element into isds_envelope
3835 * structure.
3836 * TODO: This function can be incorporated into append_status_size_times() as
3837 * they are called always together.
3838 * The envelope is automatically allocated but not reallocated.
3839 * The data are just appended into envelope structure.
3840 * @context is ISDS context
3841 * @envelope is automatically allocated message envelope structure
3842 * @xpath_ctx is XPath context with current node as parent of attribute
3843 * carrying message type
3844 * In case of error @envelope will be freed. */
3845 static isds_error append_message_type(struct isds_ctx *context,
3846 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3847 isds_error err = IE_SUCCESS;
3849 if (!context) return IE_INVALID_CONTEXT;
3850 if (!envelope) return IE_INVAL;
3851 if (!xpath_ctx) return IE_INVAL;
3854 if (!*envelope) {
3855 /* Allocate new */
3856 *envelope = calloc(1, sizeof(**envelope));
3857 if (!*envelope) {
3858 err = IE_NOMEM;
3859 goto leave;
3861 } else {
3862 /* Free old data */
3863 zfree((*envelope)->dmType);
3867 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3869 if (!(*envelope)->dmType) {
3870 /* Use default value */
3871 (*envelope)->dmType = strdup("V");
3872 if (!(*envelope)->dmType) {
3873 err = IE_NOMEM;
3874 goto leave;
3876 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3877 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3878 isds_printf_message(context,
3879 _("Message type in dmType attribute is not 1 character long: "
3880 "%s"),
3881 type_locale);
3882 free(type_locale);
3883 err = IE_ISDS;
3884 goto leave;
3887 leave:
3888 if (err) isds_envelope_free(envelope);
3889 return err;
3893 #if HAVE_LIBCURL
3894 /* Convert dmType isds_envelope member into XML attribute and append it to
3895 * current node.
3896 * @context is ISDS context
3897 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3898 * @dm_envelope is XML element the resulting attribute will be appended to.
3899 * @return error code, in case of error context' message is filled. */
3900 static isds_error insert_message_type(struct isds_ctx *context,
3901 const char *type, xmlNodePtr dm_envelope) {
3902 isds_error err = IE_SUCCESS;
3903 xmlAttrPtr attribute_node;
3905 if (!context) return IE_INVALID_CONTEXT;
3906 if (!dm_envelope) return IE_INVAL;
3908 /* Insert optional message type */
3909 if (type) {
3910 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3911 char *type_locale = _isds_utf82locale(type);
3912 isds_printf_message(context,
3913 _("Message type in envelope is not 1 character long: %s"),
3914 type_locale);
3915 free(type_locale);
3916 err = IE_INVAL;
3917 goto leave;
3919 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3922 leave:
3923 return err;
3925 #endif /* HAVE_LIBCURL */
3928 /* Extract message document into reallocated document structure
3929 * @context is ISDS context
3930 * @document is automatically reallocated message documents structure
3931 * @xpath_ctx is XPath context with current node as isds:dmFile
3932 * In case of error @document will be freed. */
3933 static isds_error extract_document(struct isds_ctx *context,
3934 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3935 isds_error err = IE_SUCCESS;
3936 xmlXPathObjectPtr result = NULL;
3937 xmlNodePtr file_node;
3938 char *string = NULL;
3940 if (!context) return IE_INVALID_CONTEXT;
3941 if (!document) return IE_INVAL;
3942 isds_document_free(document);
3943 if (!xpath_ctx) return IE_INVAL;
3944 file_node = xpath_ctx->node;
3946 *document = calloc(1, sizeof(**document));
3947 if (!*document) {
3948 err = IE_NOMEM;
3949 goto leave;
3952 /* Extract document meta data */
3953 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3954 if (context->normalize_mime_type) {
3955 const char *normalized_type =
3956 isds_normalize_mime_type((*document)->dmMimeType);
3957 if (NULL != normalized_type &&
3958 normalized_type != (*document)->dmMimeType) {
3959 char *new_type = strdup(normalized_type);
3960 if (NULL == new_type) {
3961 isds_printf_message(context,
3962 _("Not enough memory to normalize document MIME type"));
3963 err = IE_NOMEM;
3964 goto leave;
3966 free((*document)->dmMimeType);
3967 (*document)->dmMimeType = new_type;
3971 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3972 err = string2isds_FileMetaType((xmlChar*)string,
3973 &((*document)->dmFileMetaType));
3974 if (err) {
3975 char *meta_type_locale = _isds_utf82locale(string);
3976 isds_printf_message(context,
3977 _("Document has invalid dmFileMetaType attribute value: %s"),
3978 meta_type_locale);
3979 free(meta_type_locale);
3980 err = IE_ISDS;
3981 goto leave;
3983 zfree(string);
3985 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3986 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3987 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3988 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3991 /* Extract document data.
3992 * Base64 encoded blob or XML subtree must be presented. */
3994 /* Check for dmEncodedContent */
3995 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3996 xpath_ctx);
3997 if (!result) {
3998 err = IE_XML;
3999 goto leave;
4002 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4003 /* Here we have Base64 blob */
4004 (*document)->is_xml = 0;
4006 if (result->nodesetval->nodeNr > 1) {
4007 isds_printf_message(context,
4008 _("Document has more dmEncodedContent elements"));
4009 err = IE_ISDS;
4010 goto leave;
4013 xmlXPathFreeObject(result); result = NULL;
4014 EXTRACT_STRING("isds:dmEncodedContent", string);
4016 /* Decode non-empty document */
4017 if (string && string[0] != '\0') {
4018 (*document)->data_length =
4019 _isds_b64decode(string, &((*document)->data));
4020 if ((*document)->data_length == (size_t) -1) {
4021 isds_printf_message(context,
4022 _("Error while Base64-decoding document content"));
4023 err = IE_ERROR;
4024 goto leave;
4027 } else {
4028 /* No Base64 blob, try XML document */
4029 xmlXPathFreeObject(result); result = NULL;
4030 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
4031 xpath_ctx);
4032 if (!result) {
4033 err = IE_XML;
4034 goto leave;
4037 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4038 /* Here we have XML document */
4039 (*document)->is_xml = 1;
4041 if (result->nodesetval->nodeNr > 1) {
4042 isds_printf_message(context,
4043 _("Document has more dmXMLContent elements"));
4044 err = IE_ISDS;
4045 goto leave;
4048 /* XXX: We cannot serialize the content simply because:
4049 * - XML document may point out of its scope (e.g. to message
4050 * envelope)
4051 * - isds:dmXMLContent can contain more elements, no element,
4052 * a text node only
4053 * - it's not the XML way
4054 * Thus we provide the only right solution: XML DOM. Let's
4055 * application to cope with this hot potato :) */
4056 (*document)->xml_node_list =
4057 result->nodesetval->nodeTab[0]->children;
4058 } else {
4059 /* No base64 blob, nor XML document */
4060 isds_printf_message(context,
4061 _("Document has no dmEncodedContent, nor dmXMLContent "
4062 "element"));
4063 err = IE_ISDS;
4064 goto leave;
4069 leave:
4070 if (err) isds_document_free(document);
4071 free(string);
4072 xmlXPathFreeObject(result);
4073 xpath_ctx->node = file_node;
4074 return err;
4079 /* Extract message documents into reallocated list of documents
4080 * @context is ISDS context
4081 * @documents is automatically reallocated message documents list structure
4082 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4083 * In case of error @documents will be freed. */
4084 static isds_error extract_documents(struct isds_ctx *context,
4085 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
4086 isds_error err = IE_SUCCESS;
4087 xmlXPathObjectPtr result = NULL;
4088 xmlNodePtr files_node;
4089 struct isds_list *document, *prev_document = NULL;
4091 if (!context) return IE_INVALID_CONTEXT;
4092 if (!documents) return IE_INVAL;
4093 isds_list_free(documents);
4094 if (!xpath_ctx) return IE_INVAL;
4095 files_node = xpath_ctx->node;
4097 /* Find documents */
4098 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
4099 if (!result) {
4100 err = IE_XML;
4101 goto leave;
4104 /* No match */
4105 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4106 isds_printf_message(context,
4107 _("Message does not contain any document"));
4108 err = IE_ISDS;
4109 goto leave;
4113 /* Iterate over documents */
4114 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4116 /* Allocate and append list item */
4117 document = calloc(1, sizeof(*document));
4118 if (!document) {
4119 err = IE_NOMEM;
4120 goto leave;
4122 document->destructor = (void (*)(void **))isds_document_free;
4123 if (i == 0) *documents = document;
4124 else prev_document->next = document;
4125 prev_document = document;
4127 /* Extract document */
4128 xpath_ctx->node = result->nodesetval->nodeTab[i];
4129 err = extract_document(context,
4130 (struct isds_document **) &(document->data), xpath_ctx);
4131 if (err) goto leave;
4135 leave:
4136 if (err) isds_list_free(documents);
4137 xmlXPathFreeObject(result);
4138 xpath_ctx->node = files_node;
4139 return err;
4143 #if HAVE_LIBCURL
4144 /* Convert isds:dmRecord XML tree into structure
4145 * @context is ISDS context
4146 * @envelope is automatically reallocated message envelope structure
4147 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4148 * In case of error @envelope will be freed. */
4149 static isds_error extract_DmRecord(struct isds_ctx *context,
4150 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4151 isds_error err = IE_SUCCESS;
4152 xmlXPathObjectPtr result = NULL;
4154 if (!context) return IE_INVALID_CONTEXT;
4155 if (!envelope) return IE_INVAL;
4156 isds_envelope_free(envelope);
4157 if (!xpath_ctx) return IE_INVAL;
4160 *envelope = calloc(1, sizeof(**envelope));
4161 if (!*envelope) {
4162 err = IE_NOMEM;
4163 goto leave;
4167 /* Extract tRecord data */
4168 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4170 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4171 * dmAcceptanceTime. */
4172 err = append_status_size_times(context, envelope, xpath_ctx);
4173 if (err) goto leave;
4175 /* Extract envelope elements added by sender and ISDS
4176 * (XSD: gMessageEnvelope type) */
4177 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4178 if (err) goto leave;
4180 /* Get message type */
4181 err = append_message_type(context, envelope, xpath_ctx);
4182 if (err) goto leave;
4185 leave:
4186 if (err) isds_envelope_free(envelope);
4187 xmlXPathFreeObject(result);
4188 return err;
4192 /* Convert XSD:tStateChangesRecord type XML tree into structure
4193 * @context is ISDS context
4194 * @changed_status is automatically reallocated message state change structure
4195 * @xpath_ctx is XPath context with current node as element of
4196 * XSD:tStateChangesRecord type
4197 * In case of error @changed_status will be freed. */
4198 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4199 struct isds_message_status_change **changed_status,
4200 xmlXPathContextPtr xpath_ctx) {
4201 isds_error err = IE_SUCCESS;
4202 xmlXPathObjectPtr result = NULL;
4203 unsigned long int *unumber = NULL;
4204 char *string = NULL;
4206 if (!context) return IE_INVALID_CONTEXT;
4207 if (!changed_status) return IE_INVAL;
4208 isds_message_status_change_free(changed_status);
4209 if (!xpath_ctx) return IE_INVAL;
4212 *changed_status = calloc(1, sizeof(**changed_status));
4213 if (!*changed_status) {
4214 err = IE_NOMEM;
4215 goto leave;
4219 /* Extract tGetStateChangesInput data */
4220 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4222 /* dmEventTime is mandatory */
4223 EXTRACT_STRING("isds:dmEventTime", string);
4224 if (string) {
4225 err = timestring2timeval((xmlChar *) string,
4226 &((*changed_status)->time));
4227 if (err) {
4228 char *string_locale = _isds_utf82locale(string);
4229 if (err == IE_DATE) err = IE_ISDS;
4230 isds_printf_message(context,
4231 _("Could not convert dmEventTime as ISO time: %s"),
4232 string_locale);
4233 free(string_locale);
4234 goto leave;
4236 zfree(string);
4239 /* dmMessageStatus element is mandatory */
4240 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4241 if (!unumber) {
4242 isds_log_message(context,
4243 _("Missing mandatory isds:dmMessageStatus integer"));
4244 err = IE_ISDS;
4245 goto leave;
4247 err = uint2isds_message_status(context, unumber,
4248 &((*changed_status)->dmMessageStatus));
4249 if (err) {
4250 if (err == IE_ENUM) err = IE_ISDS;
4251 goto leave;
4253 zfree(unumber);
4256 leave:
4257 free(unumber);
4258 free(string);
4259 if (err) isds_message_status_change_free(changed_status);
4260 xmlXPathFreeObject(result);
4261 return err;
4263 #endif /* HAVE_LIBCURL */
4266 /* Find and convert isds:dmHash XML tree into structure
4267 * @context is ISDS context
4268 * @envelope is automatically reallocated message hash structure
4269 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4270 * In case of error @hash will be freed. */
4271 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4272 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4273 isds_error err = IE_SUCCESS;
4274 xmlNodePtr old_ctx_node;
4275 xmlXPathObjectPtr result = NULL;
4276 char *string = NULL;
4278 if (!context) return IE_INVALID_CONTEXT;
4279 if (!hash) return IE_INVAL;
4280 isds_hash_free(hash);
4281 if (!xpath_ctx) return IE_INVAL;
4283 old_ctx_node = xpath_ctx->node;
4285 *hash = calloc(1, sizeof(**hash));
4286 if (!*hash) {
4287 err = IE_NOMEM;
4288 goto leave;
4291 /* Locate dmHash */
4292 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4293 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4294 err = IE_ISDS;
4295 goto leave;
4297 if (err) {
4298 err = IE_ERROR;
4299 goto leave;
4302 /* Get hash algorithm */
4303 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4304 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4305 if (err) {
4306 if (err == IE_ENUM) {
4307 char *string_locale = _isds_utf82locale(string);
4308 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4309 string_locale);
4310 free(string_locale);
4312 goto leave;
4314 zfree(string);
4316 /* Get hash value */
4317 EXTRACT_STRING(".", string);
4318 if (!string) {
4319 isds_printf_message(context,
4320 _("sisds:dmHash element is missing hash value"));
4321 err = IE_ISDS;
4322 goto leave;
4324 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4325 if ((*hash)->length == (size_t) -1) {
4326 isds_printf_message(context,
4327 _("Error while Base64-decoding hash value"));
4328 err = IE_ERROR;
4329 goto leave;
4332 leave:
4333 if (err) isds_hash_free(hash);
4334 free(string);
4335 xmlXPathFreeObject(result);
4336 xpath_ctx->node = old_ctx_node;
4337 return err;
4341 /* Find and append isds:dmQTimestamp XML tree into envelope.
4342 * Because one service is allowed to miss time-stamp content, and we think
4343 * other could too (flaw in specification), this function is deliberated and
4344 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4345 * @context is ISDS context
4346 * @envelope is automatically allocated envelope structure
4347 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4348 * child
4349 * In case of error @envelope will be freed. */
4350 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4351 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4352 isds_error err = IE_SUCCESS;
4353 xmlXPathObjectPtr result = NULL;
4354 char *string = NULL;
4356 if (!context) return IE_INVALID_CONTEXT;
4357 if (!envelope) return IE_INVAL;
4358 if (!xpath_ctx) {
4359 isds_envelope_free(envelope);
4360 return IE_INVAL;
4363 if (!*envelope) {
4364 *envelope = calloc(1, sizeof(**envelope));
4365 if (!*envelope) {
4366 err = IE_NOMEM;
4367 goto leave;
4369 } else {
4370 zfree((*envelope)->timestamp);
4371 (*envelope)->timestamp_length = 0;
4374 /* Get dmQTimestamp */
4375 EXTRACT_STRING("sisds:dmQTimestamp", string);
4376 if (!string) {
4377 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4378 goto leave;
4380 (*envelope)->timestamp_length =
4381 _isds_b64decode(string, &((*envelope)->timestamp));
4382 if ((*envelope)->timestamp_length == (size_t) -1) {
4383 isds_printf_message(context,
4384 _("Error while Base64-decoding time stamp value"));
4385 err = IE_ERROR;
4386 goto leave;
4389 leave:
4390 if (err) isds_envelope_free(envelope);
4391 free(string);
4392 xmlXPathFreeObject(result);
4393 return err;
4397 /* Convert XSD tReturnedMessage XML tree into message structure.
4398 * It does not store serialized XML tree into message->raw.
4399 * It does store (pointer to) parsed XML tree into message->xml if needed.
4400 * @context is ISDS context
4401 * @include_documents Use true if documents must be extracted
4402 * (tReturnedMessage XSD type), use false if documents shall be omitted
4403 * (tReturnedMessageEnvelope).
4404 * @message is automatically reallocated message structure
4405 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4406 * type
4407 * In case of error @message will be freed. */
4408 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4409 const _Bool include_documents, struct isds_message **message,
4410 xmlXPathContextPtr xpath_ctx) {
4411 isds_error err = IE_SUCCESS;
4412 xmlNodePtr message_node;
4414 if (!context) return IE_INVALID_CONTEXT;
4415 if (!message) return IE_INVAL;
4416 isds_message_free(message);
4417 if (!xpath_ctx) return IE_INVAL;
4420 *message = calloc(1, sizeof(**message));
4421 if (!*message) {
4422 err = IE_NOMEM;
4423 goto leave;
4426 /* Save message XPATH context node */
4427 message_node = xpath_ctx->node;
4430 /* Extract dmDM */
4431 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4432 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4433 if (err) { err = IE_ERROR; goto leave; }
4434 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4435 if (err) goto leave;
4437 if (include_documents) {
4438 struct isds_list *item;
4440 /* Extract dmFiles */
4441 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4442 xpath_ctx);
4443 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4444 err = IE_ISDS; goto leave;
4446 if (err) { err = IE_ERROR; goto leave; }
4447 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4448 if (err) goto leave;
4450 /* Store xmlDoc of this message if needed */
4451 /* Only if we got a XML document in all the documents. */
4452 for (item = (*message)->documents; item; item = item->next) {
4453 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4454 (*message)->xml = xpath_ctx->doc;
4455 break;
4461 /* Restore context to message */
4462 xpath_ctx->node = message_node;
4464 /* Extract dmHash */
4465 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4466 xpath_ctx);
4467 if (err) goto leave;
4469 /* Extract dmQTimestamp, */
4470 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4471 xpath_ctx);
4472 if (err) goto leave;
4474 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4475 * dmAcceptanceTime. */
4476 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4477 if (err) goto leave;
4479 /* Get message type */
4480 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4481 if (err) goto leave;
4483 leave:
4484 if (err) isds_message_free(message);
4485 return err;
4489 /* Extract message event into reallocated isds_event structure
4490 * @context is ISDS context
4491 * @event is automatically reallocated message event structure
4492 * @xpath_ctx is XPath context with current node as isds:dmEvent
4493 * In case of error @event will be freed. */
4494 static isds_error extract_event(struct isds_ctx *context,
4495 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4496 isds_error err = IE_SUCCESS;
4497 xmlXPathObjectPtr result = NULL;
4498 xmlNodePtr event_node;
4499 char *string = NULL;
4501 if (!context) return IE_INVALID_CONTEXT;
4502 if (!event) return IE_INVAL;
4503 isds_event_free(event);
4504 if (!xpath_ctx) return IE_INVAL;
4505 event_node = xpath_ctx->node;
4507 *event = calloc(1, sizeof(**event));
4508 if (!*event) {
4509 err = IE_NOMEM;
4510 goto leave;
4513 /* Extract event data.
4514 * All elements are optional according XSD. That's funny. */
4515 EXTRACT_STRING("sisds:dmEventTime", string);
4516 if (string) {
4517 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4518 if (err) {
4519 char *string_locale = _isds_utf82locale(string);
4520 if (err == IE_DATE) err = IE_ISDS;
4521 isds_printf_message(context,
4522 _("Could not convert dmEventTime as ISO time: %s"),
4523 string_locale);
4524 free(string_locale);
4525 goto leave;
4527 zfree(string);
4530 /* dmEventDescr element has prefix and the rest */
4531 EXTRACT_STRING("sisds:dmEventDescr", string);
4532 if (string) {
4533 err = eventstring2event((xmlChar *) string, *event);
4534 if (err) goto leave;
4535 zfree(string);
4538 leave:
4539 if (err) isds_event_free(event);
4540 free(string);
4541 xmlXPathFreeObject(result);
4542 xpath_ctx->node = event_node;
4543 return err;
4547 /* Convert element of XSD tEventsArray type from XML tree into
4548 * isds_list of isds_event's structure. The list is automatically reallocated.
4549 * @context is ISDS context
4550 * @events is automatically reallocated list of event structures
4551 * @xpath_ctx is XPath context with current node as tEventsArray
4552 * In case of error @events will be freed. */
4553 static isds_error extract_events(struct isds_ctx *context,
4554 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4555 isds_error err = IE_SUCCESS;
4556 xmlXPathObjectPtr result = NULL;
4557 xmlNodePtr events_node;
4558 struct isds_list *event, *prev_event = NULL;
4560 if (!context) return IE_INVALID_CONTEXT;
4561 if (!events) return IE_INVAL;
4562 if (!xpath_ctx) return IE_INVAL;
4563 events_node = xpath_ctx->node;
4565 /* Free old list */
4566 isds_list_free(events);
4568 /* Find events */
4569 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4570 if (!result) {
4571 err = IE_XML;
4572 goto leave;
4575 /* No match */
4576 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4577 isds_printf_message(context,
4578 _("Delivery info does not contain any event"));
4579 err = IE_ISDS;
4580 goto leave;
4584 /* Iterate over events */
4585 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4587 /* Allocate and append list item */
4588 event = calloc(1, sizeof(*event));
4589 if (!event) {
4590 err = IE_NOMEM;
4591 goto leave;
4593 event->destructor = (void (*)(void **))isds_event_free;
4594 if (i == 0) *events = event;
4595 else prev_event->next = event;
4596 prev_event = event;
4598 /* Extract event */
4599 xpath_ctx->node = result->nodesetval->nodeTab[i];
4600 err = extract_event(context,
4601 (struct isds_event **) &(event->data), xpath_ctx);
4602 if (err) goto leave;
4606 leave:
4607 if (err) isds_list_free(events);
4608 xmlXPathFreeObject(result);
4609 xpath_ctx->node = events_node;
4610 return err;
4614 #if HAVE_LIBCURL
4615 /* Insert Base64 encoded data as element with text child.
4616 * @context is session context
4617 * @parent is XML node to append @element with @data as child
4618 * @ns is XML namespace of @element, use NULL to inherit from @parent
4619 * @element is UTF-8 encoded name of new element
4620 * @data is bit stream to encode into @element
4621 * @length is size of @data in bytes
4622 * @return standard error code and fill long error message if needed */
4623 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4624 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4625 const void *data, size_t length) {
4626 isds_error err = IE_SUCCESS;
4627 xmlNodePtr node;
4629 if (!context) return IE_INVALID_CONTEXT;
4630 if (!data && length > 0) return IE_INVAL;
4631 if (!parent || !element) return IE_INVAL;
4633 xmlChar *base64data = NULL;
4634 base64data = (xmlChar *) _isds_b64encode(data, length);
4635 if (!base64data) {
4636 isds_printf_message(context,
4637 ngettext("Not enough memory to encode %zd byte into Base64",
4638 "Not enough memory to encode %zd bytes into Base64",
4639 length),
4640 length);
4641 err = IE_NOMEM;
4642 goto leave;
4644 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4646 leave:
4647 free(base64data);
4648 return err;
4652 /* Convert isds_document structure into XML tree and append to dmFiles node.
4653 * @context is session context
4654 * @document is ISDS document
4655 * @dm_files is XML element the resulting tree will be appended to as a child.
4656 * @return error code, in case of error context' message is filled. */
4657 static isds_error insert_document(struct isds_ctx *context,
4658 struct isds_document *document, xmlNodePtr dm_files) {
4659 isds_error err = IE_SUCCESS;
4660 xmlNodePtr new_file = NULL, file = NULL, node;
4661 xmlAttrPtr attribute_node;
4663 if (!context) return IE_INVALID_CONTEXT;
4664 if (!document || !dm_files) return IE_INVAL;
4666 /* Allocate new dmFile */
4667 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4668 if (!new_file) {
4669 isds_printf_message(context, _("Could not allocate main dmFile"));
4670 err = IE_ERROR;
4671 goto leave;
4673 /* Append the new dmFile.
4674 * XXX: Main document must go first */
4675 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4676 file = xmlAddPrevSibling(dm_files->children, new_file);
4677 else
4678 file = xmlAddChild(dm_files, new_file);
4680 if (!file) {
4681 xmlFreeNode(new_file); new_file = NULL;
4682 isds_printf_message(context, _("Could not add dmFile child to "
4683 "%s element"), dm_files->name);
4684 err = IE_ERROR;
4685 goto leave;
4688 /* @dmMimeType is required */
4689 if (!document->dmMimeType) {
4690 isds_log_message(context,
4691 _("Document is missing mandatory MIME type definition"));
4692 err = IE_INVAL;
4693 goto leave;
4695 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4697 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4698 if (!string) {
4699 isds_printf_message(context,
4700 _("Document has unknown dmFileMetaType: %ld"),
4701 document->dmFileMetaType);
4702 err = IE_ENUM;
4703 goto leave;
4705 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4707 if (document->dmFileGuid) {
4708 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4710 if (document->dmUpFileGuid) {
4711 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4714 /* @dmFileDescr is required */
4715 if (!document->dmFileDescr) {
4716 isds_log_message(context,
4717 _("Document is missing mandatory description (title)"));
4718 err = IE_INVAL;
4719 goto leave;
4721 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4723 if (document->dmFormat) {
4724 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4728 /* Insert content (body) of the document. */
4729 if (document->is_xml) {
4730 /* XML document requested */
4732 /* Allocate new dmXMLContent */
4733 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4734 if (!xmlcontent) {
4735 isds_printf_message(context,
4736 _("Could not allocate dmXMLContent element"));
4737 err = IE_ERROR;
4738 goto leave;
4740 /* Append it */
4741 node = xmlAddChild(file, xmlcontent);
4742 if (!node) {
4743 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4744 isds_printf_message(context,
4745 _("Could not add dmXMLContent child to %s element"),
4746 file->name);
4747 err = IE_ERROR;
4748 goto leave;
4751 /* Copy non-empty node list */
4752 if (document->xml_node_list) {
4753 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4754 document->xml_node_list);
4755 if (!content) {
4756 isds_printf_message(context,
4757 _("Not enough memory to copy XML document"));
4758 err = IE_NOMEM;
4759 goto leave;
4762 if (!xmlAddChildList(node, content)) {
4763 xmlFreeNodeList(content);
4764 isds_printf_message(context,
4765 _("Error while adding XML document into dmXMLContent"));
4766 err = IE_XML;
4767 goto leave;
4769 /* XXX: We cannot free the content here because it's part of node's
4770 * document since now. It will be freed with it automatically. */
4772 } else {
4773 /* Binary document requested */
4774 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4775 document->data, document->data_length);
4776 if (err) goto leave;
4779 leave:
4780 return err;
4784 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4785 * The copy must be preallocated, the date are just appended into structure.
4786 * @context is ISDS context
4787 * @copy is message copy structure
4788 * @xpath_ctx is XPath context with current node as tMStatus */
4789 static isds_error append_TMStatus(struct isds_ctx *context,
4790 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4791 isds_error err = IE_SUCCESS;
4792 xmlXPathObjectPtr result = NULL;
4793 char *code = NULL, *message = NULL;
4795 if (!context) return IE_INVALID_CONTEXT;
4796 if (!copy || !xpath_ctx) return IE_INVAL;
4798 /* Free old values */
4799 zfree(copy->dmStatus);
4800 zfree(copy->dmID);
4802 /* Get error specific to this copy */
4803 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4804 if (!code) {
4805 isds_log_message(context,
4806 _("Missing isds:dmStatusCode under "
4807 "XSD:tMStatus type element"));
4808 err = IE_ISDS;
4809 goto leave;
4812 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4813 /* This copy failed */
4814 copy->error = IE_ISDS;
4815 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4816 if (message) {
4817 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4818 if (!copy->dmStatus) {
4819 copy->dmStatus = code;
4820 code = NULL;
4822 } else {
4823 copy->dmStatus = code;
4824 code = NULL;
4826 } else {
4827 /* This copy succeeded. In this case only, message ID is valid */
4828 copy->error = IE_SUCCESS;
4830 EXTRACT_STRING("isds:dmID", copy->dmID);
4831 if (!copy->dmID) {
4832 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4833 "but did not returned assigned message ID\n"));
4834 err = IE_ISDS;
4838 leave:
4839 free(code);
4840 free(message);
4841 xmlXPathFreeObject(result);
4842 return err;
4846 /* Insert struct isds_approval data (box approval) into XML tree
4847 * @context is session context
4848 * @approval is libisds structure with approval description. NULL is
4849 * acceptable.
4850 * @parent is XML element to append @approval to */
4851 static isds_error insert_GExtApproval(struct isds_ctx *context,
4852 const struct isds_approval *approval, xmlNodePtr parent) {
4854 isds_error err = IE_SUCCESS;
4855 xmlNodePtr node;
4857 if (!context) return IE_INVALID_CONTEXT;
4858 if (!parent) return IE_INVAL;
4860 if (!approval) return IE_SUCCESS;
4862 /* Build XSD:gExtApproval */
4863 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4864 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4866 leave:
4867 return err;
4871 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4872 * code
4873 * @context is session context
4874 * @service_name is name of SERVICE_DB_ACCESS
4875 * @response is reallocated server SOAP body response as XML document
4876 * @raw_response is reallocated bit stream with response body. Use
4877 * NULL if you don't care
4878 * @raw_response_length is size of @raw_response in bytes
4879 * @code is reallocated ISDS status code
4880 * @status_message is reallocated ISDS status message
4881 * @return error coded from lower layer, context message will be set up
4882 * appropriately. */
4883 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4884 const xmlChar *service_name,
4885 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4886 xmlChar **code, xmlChar **status_message) {
4888 isds_error err = IE_SUCCESS;
4889 char *service_name_locale = NULL;
4890 xmlNodePtr request = NULL, node;
4891 xmlNsPtr isds_ns = NULL;
4893 if (!context) return IE_INVALID_CONTEXT;
4894 if (!service_name) return IE_INVAL;
4895 if (!response || !code || !status_message) return IE_INVAL;
4896 if (!raw_response_length && raw_response) return IE_INVAL;
4898 /* Free output argument */
4899 xmlFreeDoc(*response); *response = NULL;
4900 if (raw_response) zfree(*raw_response);
4901 zfree(*code);
4902 zfree(*status_message);
4905 /* Check if connection is established
4906 * TODO: This check should be done downstairs. */
4907 if (!context->curl) return IE_CONNECTION_CLOSED;
4909 service_name_locale = _isds_utf82locale((char*)service_name);
4910 if (!service_name_locale) {
4911 err = IE_NOMEM;
4912 goto leave;
4915 /* Build request */
4916 request = xmlNewNode(NULL, service_name);
4917 if (!request) {
4918 isds_printf_message(context,
4919 _("Could not build %s request"), service_name_locale);
4920 err = IE_ERROR;
4921 goto leave;
4923 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4924 if(!isds_ns) {
4925 isds_log_message(context, _("Could not create ISDS name space"));
4926 err = IE_ERROR;
4927 goto leave;
4929 xmlSetNs(request, isds_ns);
4932 /* Add XSD:tDummyInput child */
4933 INSERT_STRING(request, "dbDummy", NULL);
4936 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4937 service_name_locale);
4939 /* Send request */
4940 err = _isds(context, SERVICE_DB_ACCESS, request, response,
4941 raw_response, raw_response_length);
4942 xmlFreeNode(request); request = NULL;
4944 if (err) {
4945 isds_log(ILF_ISDS, ILL_DEBUG,
4946 _("Processing ISDS response on %s request failed\n"),
4947 service_name_locale);
4948 goto leave;
4951 /* Check for response status */
4952 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4953 code, status_message, NULL);
4954 if (err) {
4955 isds_log(ILF_ISDS, ILL_DEBUG,
4956 _("ISDS response on %s request is missing status\n"),
4957 service_name_locale);
4958 goto leave;
4961 /* Request processed, but nothing found */
4962 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4963 char *code_locale = _isds_utf82locale((char*) *code);
4964 char *status_message_locale =
4965 _isds_utf82locale((char*) *status_message);
4966 isds_log(ILF_ISDS, ILL_DEBUG,
4967 _("Server refused %s request (code=%s, message=%s)\n"),
4968 service_name_locale, code_locale, status_message_locale);
4969 isds_log_message(context, status_message_locale);
4970 free(code_locale);
4971 free(status_message_locale);
4972 err = IE_ISDS;
4973 goto leave;
4976 leave:
4977 free(service_name_locale);
4978 xmlFreeNode(request);
4979 return err;
4981 #endif
4984 /* Get data about logged in user and his box.
4985 * @context is session context
4986 * @db_owner_info is reallocated box owner description. It will be freed on
4987 * error.
4988 * @return error code from lower layer, context message will be set up
4989 * appropriately. */
4990 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4991 struct isds_DbOwnerInfo **db_owner_info) {
4992 isds_error err = IE_SUCCESS;
4993 #if HAVE_LIBCURL
4994 xmlDocPtr response = NULL;
4995 xmlChar *code = NULL, *message = NULL;
4996 xmlXPathContextPtr xpath_ctx = NULL;
4997 xmlXPathObjectPtr result = NULL;
4998 char *string = NULL;
4999 #endif
5001 if (!context) return IE_INVALID_CONTEXT;
5002 zfree(context->long_message);
5003 if (!db_owner_info) return IE_INVAL;
5004 isds_DbOwnerInfo_free(db_owner_info);
5006 #if HAVE_LIBCURL
5007 /* Check if connection is established */
5008 if (!context->curl) return IE_CONNECTION_CLOSED;
5011 /* Do request and check for success */
5012 err = build_send_check_dbdummy_request(context,
5013 BAD_CAST "GetOwnerInfoFromLogin",
5014 &response, NULL, NULL, &code, &message);
5015 if (err) goto leave;
5018 /* Extract data */
5019 /* Prepare structure */
5020 *db_owner_info = calloc(1, sizeof(**db_owner_info));
5021 if (!*db_owner_info) {
5022 err = IE_NOMEM;
5023 goto leave;
5025 xpath_ctx = xmlXPathNewContext(response);
5026 if (!xpath_ctx) {
5027 err = IE_ERROR;
5028 goto leave;
5030 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5031 err = IE_ERROR;
5032 goto leave;
5035 /* Set context node */
5036 result = xmlXPathEvalExpression(BAD_CAST
5037 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
5038 if (!result) {
5039 err = IE_ERROR;
5040 goto leave;
5042 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5043 isds_log_message(context, _("Missing dbOwnerInfo element"));
5044 err = IE_ISDS;
5045 goto leave;
5047 if (result->nodesetval->nodeNr > 1) {
5048 isds_log_message(context, _("Multiple dbOwnerInfo element"));
5049 err = IE_ISDS;
5050 goto leave;
5052 xpath_ctx->node = result->nodesetval->nodeTab[0];
5053 xmlXPathFreeObject(result); result = NULL;
5055 /* Extract it */
5056 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
5059 leave:
5060 if (err) {
5061 isds_DbOwnerInfo_free(db_owner_info);
5064 free(string);
5065 xmlXPathFreeObject(result);
5066 xmlXPathFreeContext(xpath_ctx);
5068 free(code);
5069 free(message);
5070 xmlFreeDoc(response);
5072 if (!err)
5073 isds_log(ILF_ISDS, ILL_DEBUG,
5074 _("GetOwnerInfoFromLogin request processed by server "
5075 "successfully.\n"));
5076 #else /* not HAVE_LIBCURL */
5077 err = IE_NOTSUP;
5078 #endif
5080 return err;
5084 /* Get data about logged in user. */
5085 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
5086 struct isds_DbUserInfo **db_user_info) {
5087 isds_error err = IE_SUCCESS;
5088 #if HAVE_LIBCURL
5089 xmlDocPtr response = NULL;
5090 xmlChar *code = NULL, *message = NULL;
5091 xmlXPathContextPtr xpath_ctx = NULL;
5092 xmlXPathObjectPtr result = NULL;
5093 #endif
5095 if (!context) return IE_INVALID_CONTEXT;
5096 zfree(context->long_message);
5097 if (!db_user_info) return IE_INVAL;
5098 isds_DbUserInfo_free(db_user_info);
5100 #if HAVE_LIBCURL
5101 /* Check if connection is established */
5102 if (!context->curl) return IE_CONNECTION_CLOSED;
5105 /* Do request and check for success */
5106 err = build_send_check_dbdummy_request(context,
5107 BAD_CAST "GetUserInfoFromLogin",
5108 &response, NULL, NULL, &code, &message);
5109 if (err) goto leave;
5112 /* Extract data */
5113 /* Prepare structure */
5114 *db_user_info = calloc(1, sizeof(**db_user_info));
5115 if (!*db_user_info) {
5116 err = IE_NOMEM;
5117 goto leave;
5119 xpath_ctx = xmlXPathNewContext(response);
5120 if (!xpath_ctx) {
5121 err = IE_ERROR;
5122 goto leave;
5124 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5125 err = IE_ERROR;
5126 goto leave;
5129 /* Set context node */
5130 result = xmlXPathEvalExpression(BAD_CAST
5131 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
5132 if (!result) {
5133 err = IE_ERROR;
5134 goto leave;
5136 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5137 isds_log_message(context, _("Missing dbUserInfo element"));
5138 err = IE_ISDS;
5139 goto leave;
5141 if (result->nodesetval->nodeNr > 1) {
5142 isds_log_message(context, _("Multiple dbUserInfo element"));
5143 err = IE_ISDS;
5144 goto leave;
5146 xpath_ctx->node = result->nodesetval->nodeTab[0];
5147 xmlXPathFreeObject(result); result = NULL;
5149 /* Extract it */
5150 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5152 leave:
5153 if (err) {
5154 isds_DbUserInfo_free(db_user_info);
5157 xmlXPathFreeObject(result);
5158 xmlXPathFreeContext(xpath_ctx);
5160 free(code);
5161 free(message);
5162 xmlFreeDoc(response);
5164 if (!err)
5165 isds_log(ILF_ISDS, ILL_DEBUG,
5166 _("GetUserInfoFromLogin request processed by server "
5167 "successfully.\n"));
5168 #else /* not HAVE_LIBCURL */
5169 err = IE_NOTSUP;
5170 #endif
5172 return err;
5176 /* Get expiration time of current password
5177 * @context is session context
5178 * @expiration is automatically reallocated time when password expires. If
5179 * password expiration is disabled, NULL will be returned. In case of error
5180 * it will be nulled too. */
5181 isds_error isds_get_password_expiration(struct isds_ctx *context,
5182 struct timeval **expiration) {
5183 isds_error err = IE_SUCCESS;
5184 #if HAVE_LIBCURL
5185 xmlDocPtr response = NULL;
5186 xmlChar *code = NULL, *message = NULL;
5187 xmlXPathContextPtr xpath_ctx = NULL;
5188 xmlXPathObjectPtr result = NULL;
5189 char *string = NULL;
5190 #endif
5192 if (!context) return IE_INVALID_CONTEXT;
5193 zfree(context->long_message);
5194 if (!expiration) return IE_INVAL;
5195 zfree(*expiration);
5197 #if HAVE_LIBCURL
5198 /* Check if connection is established */
5199 if (!context->curl) return IE_CONNECTION_CLOSED;
5202 /* Do request and check for success */
5203 err = build_send_check_dbdummy_request(context,
5204 BAD_CAST "GetPasswordInfo",
5205 &response, NULL, NULL, &code, &message);
5206 if (err) goto leave;
5209 /* Extract data */
5210 xpath_ctx = xmlXPathNewContext(response);
5211 if (!xpath_ctx) {
5212 err = IE_ERROR;
5213 goto leave;
5215 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5216 err = IE_ERROR;
5217 goto leave;
5220 /* Set context node */
5221 result = xmlXPathEvalExpression(BAD_CAST
5222 "/isds:GetPasswordInfoResponse", xpath_ctx);
5223 if (!result) {
5224 err = IE_ERROR;
5225 goto leave;
5227 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5228 isds_log_message(context,
5229 _("Missing GetPasswordInfoResponse element"));
5230 err = IE_ISDS;
5231 goto leave;
5233 if (result->nodesetval->nodeNr > 1) {
5234 isds_log_message(context,
5235 _("Multiple GetPasswordInfoResponse element"));
5236 err = IE_ISDS;
5237 goto leave;
5239 xpath_ctx->node = result->nodesetval->nodeTab[0];
5240 xmlXPathFreeObject(result); result = NULL;
5242 /* Extract expiration date */
5243 EXTRACT_STRING("isds:pswExpDate", string);
5244 if (string) {
5245 /* And convert it if any returned. Otherwise expiration is disabled. */
5246 err = timestring2timeval((xmlChar *) string, expiration);
5247 if (err) {
5248 char *string_locale = _isds_utf82locale(string);
5249 if (err == IE_DATE) err = IE_ISDS;
5250 isds_printf_message(context,
5251 _("Could not convert pswExpDate as ISO time: %s"),
5252 string_locale);
5253 free(string_locale);
5254 goto leave;
5258 leave:
5259 if (err) {
5260 if (*expiration) {
5261 zfree(*expiration);
5265 free(string);
5266 xmlXPathFreeObject(result);
5267 xmlXPathFreeContext(xpath_ctx);
5269 free(code);
5270 free(message);
5271 xmlFreeDoc(response);
5273 if (!err)
5274 isds_log(ILF_ISDS, ILL_DEBUG,
5275 _("GetPasswordInfo request processed by server "
5276 "successfully.\n"));
5277 #else /* not HAVE_LIBCURL */
5278 err = IE_NOTSUP;
5279 #endif
5281 return err;
5285 #if HAVE_LIBCURL
5286 /* Request delivering new TOTP code from ISDS through side channel before
5287 * changing password.
5288 * @context is session context
5289 * @password is current password.
5290 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5291 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5292 * function for more details.
5293 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5294 * NULL, if you don't care.
5295 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5296 * error code. */
5297 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5298 const char *password, struct isds_otp *otp, char **refnumber) {
5299 isds_error err = IE_SUCCESS;
5300 char *saved_url = NULL; /* No copy */
5301 #if HAVE_CURL_REAUTHORIZATION_BUG
5302 CURL *saved_curl = NULL; /* No copy */
5303 #endif
5304 xmlNsPtr isds_ns = NULL;
5305 xmlNodePtr request = NULL;
5306 xmlDocPtr response = NULL;
5307 xmlChar *code = NULL, *message = NULL;
5308 const xmlChar *codes[] = {
5309 BAD_CAST "2300",
5310 BAD_CAST "2301",
5311 BAD_CAST "2302"
5313 const char *meanings[] = {
5314 N_("Unexpected error"),
5315 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5316 N_("One-time code could not been sent. Try later again.")
5318 const isds_otp_resolution resolutions[] = {
5319 OTP_RESOLUTION_UNKNOWN,
5320 OTP_RESOLUTION_TO_FAST,
5321 OTP_RESOLUTION_TOTP_NOT_SENT
5324 if (NULL == context) return IE_INVALID_CONTEXT;
5325 zfree(context->long_message);
5326 if (NULL == password) {
5327 isds_log_message(context,
5328 _("Second argument (password) of isds_change_password() "
5329 "is NULL"));
5330 return IE_INVAL;
5333 /* Check if connection is established
5334 * TODO: This check should be done downstairs. */
5335 if (!context->curl) return IE_CONNECTION_CLOSED;
5337 if (!context->otp) {
5338 isds_log_message(context, _("This function requires OTP-authenticated "
5339 "context"));
5340 return IE_INVALID_CONTEXT;
5342 if (NULL == otp) {
5343 isds_log_message(context, _("If one-time password authentication "
5344 "method is in use, requesting new OTP code requires "
5345 "one-time credentials argument either"));
5346 return IE_INVAL;
5348 if (otp->method != OTP_TIME) {
5349 isds_log_message(context, _("Requesting new time-based OTP code from "
5350 "server requires one-time password authentication "
5351 "method"));
5352 return IE_INVAL;
5354 if (otp->otp_code != NULL) {
5355 isds_log_message(context, _("Requesting new time-based OTP code from "
5356 "server requires undefined OTP code member in "
5357 "one-time credentials argument"));
5358 return IE_INVAL;
5362 /* Build request */
5363 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5364 if (!request) {
5365 isds_log_message(context, _("Could not build SendSMSCode request"));
5366 return IE_ERROR;
5368 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5369 if(!isds_ns) {
5370 isds_log_message(context, _("Could not create ISDS name space"));
5371 xmlFreeNode(request);
5372 return IE_ERROR;
5374 xmlSetNs(request, isds_ns);
5376 /* Change URL temporarily for sending this request only */
5378 char *new_url = NULL;
5379 if ((err = _isds_build_url_from_context(context,
5380 "%1$.*2$sasws/changePassword", &new_url))) {
5381 goto leave;
5383 saved_url = context->url;
5384 context->url = new_url;
5387 /* Store credentials for sending this request only */
5388 context->otp_credentials = otp;
5389 _isds_discard_credentials(context, 0);
5390 if ((err = _isds_store_credentials(context, context->saved_username,
5391 password, NULL))) {
5392 _isds_discard_credentials(context, 0);
5393 goto leave;
5395 #if HAVE_CURL_REAUTHORIZATION_BUG
5396 saved_curl = context->curl;
5397 context->curl = curl_easy_init();
5398 if (NULL == context->curl) {
5399 err = IE_ERROR;
5400 goto leave;
5402 if (context->timeout) {
5403 err = isds_set_timeout(context, context->timeout);
5404 if (err) goto leave;
5406 #endif
5408 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5410 /* Sent request */
5411 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5413 /* Remove temporal credentials */
5414 _isds_discard_credentials(context, 0);
5415 /* Detach pointer to OTP credentials from context */
5416 context->otp_credentials = NULL;
5417 /* Keep context->otp true to keep signaling this is OTP session */
5419 /* Destroy request */
5420 xmlFreeNode(request); request = NULL;
5422 if (err) {
5423 isds_log(ILF_ISDS, ILL_DEBUG,
5424 _("Processing ISDS response on SendSMSCode request failed\n"));
5425 goto leave;
5428 /* Check for response status */
5429 err = isds_response_status(context, SERVICE_ASWS, response,
5430 &code, &message, (xmlChar **)refnumber);
5431 if (err) {
5432 isds_log(ILF_ISDS, ILL_DEBUG,
5433 _("ISDS response on SendSMSCode request is missing "
5434 "status\n"));
5435 goto leave;
5438 /* Check for error */
5439 if (xmlStrcmp(code, BAD_CAST "0000")) {
5440 char *code_locale = _isds_utf82locale((char*)code);
5441 char *message_locale = _isds_utf82locale((char*)message);
5442 size_t i;
5443 isds_log(ILF_ISDS, ILL_DEBUG,
5444 _("Server refused to send new code on SendSMSCode "
5445 "request (code=%s, message=%s)\n"),
5446 code_locale, message_locale);
5448 /* Check for known error codes */
5449 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5450 if (!xmlStrcmp(code, codes[i])) break;
5452 if (i < sizeof(codes)/sizeof(*codes)) {
5453 isds_log_message(context, _(meanings[i]));
5454 /* Mimic otp->resolution according to the code, specification does
5455 * prescribe OTP header to be available. */
5456 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5457 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5458 otp->resolution = resolutions[i];
5459 } else
5460 isds_log_message(context, message_locale);
5462 free(code_locale);
5463 free(message_locale);
5465 err = IE_ISDS;
5466 goto leave;
5469 /* Otherwise new code sent successfully */
5470 /* Mimic otp->resolution according to the code, specification does
5471 * prescribe OTP header to be available. */
5472 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5473 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5475 leave:
5476 if (NULL != saved_url) {
5477 /* Revert URL to original one */
5478 zfree(context->url);
5479 context->url = saved_url;
5481 #if HAVE_CURL_REAUTHORIZATION_BUG
5482 if (NULL != saved_curl) {
5483 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5484 context->curl = saved_curl;
5486 #endif
5488 free(code);
5489 free(message);
5490 xmlFreeDoc(response);
5491 xmlFreeNode(request);
5493 if (!err)
5494 isds_log(ILF_ISDS, ILL_DEBUG,
5495 _("New OTP code has been sent successfully on SendSMSCode "
5496 "request.\n"));
5497 return err;
5501 /* Convert response status code to isds_error code and set long message
5502 * @context is context to save long message to
5503 * @map is mapping from codes to errors and messages. Pass NULL for generic
5504 * handling.
5505 * @code is status code to translate
5506 * @message is non-localized status message to put into long message in case
5507 * of uknown error. It can be NULL if server did not provide any.
5508 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5509 * invalid invocation. */
5510 static isds_error statuscode2isds_error(struct isds_ctx *context,
5511 const struct code_map_isds_error *map,
5512 const xmlChar *code, const xmlChar *message) {
5513 if (NULL == code) {
5514 isds_log_message(context,
5515 _("NULL status code passed to statuscode2isds_error()"));
5516 return IE_INVAL;
5519 if (NULL != map) {
5520 /* Check for known error codes */
5521 for (int i=0; map->codes[i] != NULL; i++) {
5522 if (!xmlStrcmp(code, map->codes[i])) {
5523 isds_log_message(context, _(map->meanings[i]));
5524 return map->errors[i];
5529 /* Other error */
5530 if (xmlStrcmp(code, BAD_CAST "0000")) {
5531 char *message_locale = _isds_utf82locale((char*)message);
5532 if (NULL == message_locale)
5533 isds_log_message(context, _("ISDS server returned unknown error"));
5534 else
5535 isds_log_message(context, message_locale);
5536 free(message_locale);
5537 return IE_ISDS;
5540 return IE_SUCCESS;
5542 #endif
5545 /* Change user password in ISDS.
5546 * User must supply old password, new password will takes effect after some
5547 * time, current session can continue. Password must fulfill some constraints.
5548 * @context is session context
5549 * @old_password is current password.
5550 * @new_password is requested new password
5551 * @otp auxiliary data required if one-time password authentication is in use,
5552 * defines OTP code (if known) and returns fine grade resolution of OTP
5553 * procedure. Pass NULL, if one-time password authentication is not needed.
5554 * Please note the @otp argument must match OTP method used at log-in time. See
5555 * isds_login() function for more details.
5556 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5557 * NULL, if you don't care.
5558 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5559 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5560 * awaiting OTP code that has been delivered by side channel to the user. */
5561 isds_error isds_change_password(struct isds_ctx *context,
5562 const char *old_password, const char *new_password,
5563 struct isds_otp *otp, char **refnumber) {
5564 isds_error err = IE_SUCCESS;
5565 #if HAVE_LIBCURL
5566 char *saved_url = NULL; /* No copy */
5567 #if HAVE_CURL_REAUTHORIZATION_BUG
5568 CURL *saved_curl = NULL; /* No copy */
5569 #endif
5570 xmlNsPtr isds_ns = NULL;
5571 xmlNodePtr request = NULL, node;
5572 xmlDocPtr response = NULL;
5573 xmlChar *code = NULL, *message = NULL;
5574 const xmlChar *codes[] = {
5575 BAD_CAST "1066",
5576 BAD_CAST "1067",
5577 BAD_CAST "1079",
5578 BAD_CAST "1080",
5579 BAD_CAST "1081",
5580 BAD_CAST "1082",
5581 BAD_CAST "1083",
5582 BAD_CAST "1090",
5583 BAD_CAST "1091",
5584 BAD_CAST "2300",
5585 BAD_CAST "9204"
5587 const char *meanings[] = {
5588 N_("Password length must be between 8 and 32 characters"),
5589 N_("Password cannot be reused"), /* Server does not distinguish 1067
5590 and 1091 on ChangePasswordOTP */
5591 N_("Password contains forbidden character"),
5592 N_("Password must contain at least one upper-case letter, "
5593 "one lower-case, and one digit"),
5594 N_("Password cannot contain sequence of three identical characters"),
5595 N_("Password cannot contain user identifier"),
5596 N_("Password is too simmple"),
5597 N_("Old password is not valid"),
5598 N_("Password cannot be reused"),
5599 N_("Unexpected error"),
5600 N_("LDAP update error")
5602 #endif
5604 if (!context) return IE_INVALID_CONTEXT;
5605 zfree(context->long_message);
5606 if (NULL != refnumber)
5607 zfree(*refnumber);
5608 if (NULL == old_password) {
5609 isds_log_message(context,
5610 _("Second argument (old password) of isds_change_password() "
5611 "is NULL"));
5612 return IE_INVAL;
5614 if (NULL == otp && NULL == new_password) {
5615 isds_log_message(context,
5616 _("Third argument (new password) of isds_change_password() "
5617 "is NULL"));
5618 return IE_INVAL;
5621 #if HAVE_LIBCURL
5622 /* Check if connection is established
5623 * TODO: This check should be done downstairs. */
5624 if (!context->curl) return IE_CONNECTION_CLOSED;
5626 if (context->otp && NULL == otp) {
5627 isds_log_message(context, _("If one-time password authentication "
5628 "method is in use, changing password requires one-time "
5629 "credentials either"));
5630 return IE_INVAL;
5633 /* Build ChangeISDSPassword request */
5634 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5635 BAD_CAST "ChangePasswordOTP");
5636 if (!request) {
5637 isds_log_message(context, (NULL == otp) ?
5638 _("Could not build ChangeISDSPassword request") :
5639 _("Could not build ChangePasswordOTP request"));
5640 return IE_ERROR;
5642 isds_ns = xmlNewNs(request,
5643 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5644 NULL);
5645 if(!isds_ns) {
5646 isds_log_message(context, _("Could not create ISDS name space"));
5647 xmlFreeNode(request);
5648 return IE_ERROR;
5650 xmlSetNs(request, isds_ns);
5652 INSERT_STRING(request, "dbOldPassword", old_password);
5653 INSERT_STRING(request, "dbNewPassword", new_password);
5655 if (NULL != otp) {
5656 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5657 switch (otp->method) {
5658 case OTP_HMAC:
5659 isds_log(ILF_SEC, ILL_INFO,
5660 _("Selected authentication method: "
5661 "HMAC-based one-time password\n"));
5662 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5663 break;
5664 case OTP_TIME:
5665 isds_log(ILF_SEC, ILL_INFO,
5666 _("Selected authentication method: "
5667 "Time-based one-time password\n"));
5668 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5669 if (otp->otp_code == NULL) {
5670 isds_log(ILF_SEC, ILL_INFO,
5671 _("OTP code has not been provided by "
5672 "application, requesting server for "
5673 "new one.\n"));
5674 err = _isds_request_totp_code(context, old_password, otp,
5675 refnumber);
5676 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5677 goto leave;
5679 } else {
5680 isds_log(ILF_SEC, ILL_INFO,
5681 _("OTP code has been provided by "
5682 "application, not requesting server "
5683 "for new one.\n"));
5685 break;
5686 default:
5687 isds_log_message(context,
5688 _("Unknown one-time password authentication "
5689 "method requested by application"));
5690 err = IE_ENUM;
5691 goto leave;
5694 /* Change URL temporarily for sending this request only */
5696 char *new_url = NULL;
5697 if ((err = _isds_build_url_from_context(context,
5698 "%1$.*2$sasws/changePassword", &new_url))) {
5699 goto leave;
5701 saved_url = context->url;
5702 context->url = new_url;
5705 /* Store credentials for sending this request only */
5706 context->otp_credentials = otp;
5707 _isds_discard_credentials(context, 0);
5708 if ((err = _isds_store_credentials(context, context->saved_username,
5709 old_password, NULL))) {
5710 _isds_discard_credentials(context, 0);
5711 goto leave;
5713 #if HAVE_CURL_REAUTHORIZATION_BUG
5714 saved_curl = context->curl;
5715 context->curl = curl_easy_init();
5716 if (NULL == context->curl) {
5717 err = IE_ERROR;
5718 goto leave;
5720 if (context->timeout) {
5721 err = isds_set_timeout(context, context->timeout);
5722 if (err) goto leave;
5724 #endif
5727 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5728 _("Sending ChangeISDSPassword request to ISDS\n") :
5729 _("Sending ChangePasswordOTP request to ISDS\n"));
5731 /* Sent request */
5732 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5733 request, &response, NULL, NULL);
5735 if (otp) {
5736 /* Remove temporal credentials */
5737 _isds_discard_credentials(context, 0);
5738 /* Detach pointer to OTP credentials from context */
5739 context->otp_credentials = NULL;
5740 /* Keep context->otp true to keep signaling this is OTP session */
5743 /* Destroy request */
5744 xmlFreeNode(request); request = NULL;
5746 if (err) {
5747 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5748 _("Processing ISDS response on ChangeISDSPassword "
5749 "request failed\n") :
5750 _("Processing ISDS response on ChangePasswordOTP "
5751 "request failed\n"));
5752 goto leave;
5755 /* Check for response status */
5756 err = isds_response_status(context,
5757 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5758 &code, &message, (xmlChar **)refnumber);
5759 if (err) {
5760 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5761 _("ISDS response on ChangeISDSPassword request is missing "
5762 "status\n") :
5763 _("ISDS response on ChangePasswordOTP request is missing "
5764 "status\n"));
5765 goto leave;
5768 /* Check for known error codes */
5769 for (size_t i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5770 if (!xmlStrcmp(code, codes[i])) {
5771 char *code_locale = _isds_utf82locale((char*)code);
5772 char *message_locale = _isds_utf82locale((char*)message);
5773 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5774 _("Server refused to change password on ChangeISDSPassword "
5775 "request (code=%s, message=%s)\n") :
5776 _("Server refused to change password on ChangePasswordOTP "
5777 "request (code=%s, message=%s)\n"),
5778 code_locale, message_locale);
5779 free(code_locale);
5780 free(message_locale);
5781 isds_log_message(context, _(meanings[i]));
5782 err = IE_INVAL;
5783 goto leave;
5787 /* Other error */
5788 if (xmlStrcmp(code, BAD_CAST "0000")) {
5789 char *code_locale = _isds_utf82locale((char*)code);
5790 char *message_locale = _isds_utf82locale((char*)message);
5791 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5792 _("Server refused to change password on ChangeISDSPassword "
5793 "request (code=%s, message=%s)\n") :
5794 _("Server refused to change password on ChangePasswordOTP "
5795 "request (code=%s, message=%s)\n"),
5796 code_locale, message_locale);
5797 isds_log_message(context, message_locale);
5798 free(code_locale);
5799 free(message_locale);
5800 err = IE_ISDS;
5801 goto leave;
5804 /* Otherwise password changed successfully */
5806 leave:
5807 if (NULL != saved_url) {
5808 /* Revert URL to original one */
5809 zfree(context->url);
5810 context->url = saved_url;
5812 #if HAVE_CURL_REAUTHORIZATION_BUG
5813 if (NULL != saved_curl) {
5814 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5815 context->curl = saved_curl;
5817 #endif
5819 free(code);
5820 free(message);
5821 xmlFreeDoc(response);
5822 xmlFreeNode(request);
5824 if (!err)
5825 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5826 _("Password changed successfully on ChangeISDSPassword "
5827 "request.\n") :
5828 _("Password changed successfully on ChangePasswordOTP "
5829 "request.\n"));
5830 #else /* not HAVE_LIBCURL */
5831 err = IE_NOTSUP;
5832 #endif
5834 return err;
5838 #if HAVE_LIBCURL
5839 /* Generic middle part with request sending and response check.
5840 * It sends prepared request and checks for error code.
5841 * @context is ISDS session context.
5842 * @service is ISDS service handler
5843 * @service_name is name in scope of given @service
5844 * @request is XML tree with request. Will be freed to save memory.
5845 * @response is XML document outputting ISDS response.
5846 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5847 * @map is mapping from status code to library error. Pass NULL if no special
5848 * handling is requested.
5849 * NULL, if you don't care. */
5850 static isds_error send_destroy_request_check_response(
5851 struct isds_ctx *context,
5852 const isds_service service, const xmlChar *service_name,
5853 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5854 const struct code_map_isds_error *map) {
5855 isds_error err = IE_SUCCESS;
5856 char *service_name_locale = NULL;
5857 xmlChar *code = NULL, *message = NULL;
5860 if (!context) return IE_INVALID_CONTEXT;
5861 if (!service_name || *service_name == '\0' || !request || !*request ||
5862 !response)
5863 return IE_INVAL;
5865 /* Check if connection is established
5866 * TODO: This check should be done downstairs. */
5867 if (!context->curl) return IE_CONNECTION_CLOSED;
5869 service_name_locale = _isds_utf82locale((char*) service_name);
5870 if (!service_name_locale) {
5871 err = IE_NOMEM;
5872 goto leave;
5875 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5876 service_name_locale);
5878 /* Send request */
5879 err = _isds(context, service, *request, response, NULL, NULL);
5880 xmlFreeNode(*request); *request = NULL;
5882 if (err) {
5883 isds_log(ILF_ISDS, ILL_DEBUG,
5884 _("Processing ISDS response on %s request failed\n"),
5885 service_name_locale);
5886 goto leave;
5889 /* Check for response status */
5890 err = isds_response_status(context, service, *response,
5891 &code, &message, refnumber);
5892 if (err) {
5893 isds_log(ILF_ISDS, ILL_DEBUG,
5894 _("ISDS response on %s request is missing status\n"),
5895 service_name_locale);
5896 goto leave;
5899 err = statuscode2isds_error(context, map, code, message);
5901 /* Request processed, but server failed */
5902 if (xmlStrcmp(code, BAD_CAST "0000")) {
5903 char *code_locale = _isds_utf82locale((char*) code);
5904 char *message_locale = _isds_utf82locale((char*) message);
5905 isds_log(ILF_ISDS, ILL_DEBUG,
5906 _("Server refused %s request (code=%s, message=%s)\n"),
5907 service_name_locale, code_locale, message_locale);
5908 free(code_locale);
5909 free(message_locale);
5910 goto leave;
5914 leave:
5915 free(code);
5916 free(message);
5917 if (err && *response) {
5918 xmlFreeDoc(*response);
5919 *response = NULL;
5921 if (*request) {
5922 xmlFreeNode(*request);
5923 *request = NULL;
5925 free(service_name_locale);
5927 return err;
5931 /* Generic bottom half with request sending.
5932 * It sends prepared request, checks for error code, destroys response and
5933 * request and log success or failure.
5934 * @context is ISDS session context.
5935 * @service is ISDS service handler
5936 * @service_name is name in scope of given @service
5937 * @request is XML tree with request. Will be freed to save memory.
5938 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5939 * NULL, if you don't care. */
5940 static isds_error send_request_check_drop_response(
5941 struct isds_ctx *context,
5942 const isds_service service, const xmlChar *service_name,
5943 xmlNodePtr *request, xmlChar **refnumber) {
5944 isds_error err = IE_SUCCESS;
5945 xmlDocPtr response = NULL;
5948 if (!context) return IE_INVALID_CONTEXT;
5949 if (!service_name || *service_name == '\0' || !request || !*request)
5950 return IE_INVAL;
5952 /* Send request and check response*/
5953 err = send_destroy_request_check_response(context,
5954 service, service_name, request, &response, refnumber, NULL);
5956 xmlFreeDoc(response);
5958 if (*request) {
5959 xmlFreeNode(*request);
5960 *request = NULL;
5963 if (!err) {
5964 char *service_name_locale = _isds_utf82locale((char *) service_name);
5965 isds_log(ILF_ISDS, ILL_DEBUG,
5966 _("%s request processed by server successfully.\n"),
5967 service_name_locale);
5968 free(service_name_locale);
5971 return err;
5975 /* Insert isds_credentials_delivery structure into XML request if not NULL
5976 * @context is session context
5977 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5978 * credentials delivery. The email field is passed.
5979 * @parent is XML element where to insert */
5980 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5981 const struct isds_credentials_delivery *credentials_delivery,
5982 xmlNodePtr parent) {
5983 isds_error err = IE_SUCCESS;
5984 xmlNodePtr node;
5986 if (!context) return IE_INVALID_CONTEXT;
5987 if (!parent) return IE_INVAL;
5989 if (credentials_delivery) {
5990 /* Following elements are valid only for services:
5991 * NewAccessData, AddDataBoxUser, CreateDataBox */
5992 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5993 INSERT_STRING(parent, "email", credentials_delivery->email);
5996 leave:
5997 return err;
6001 /* Extract credentials delivery from ISDS response.
6002 * @context is session context
6003 * @credentials_delivery is pointer to valid structure to fill in returned
6004 * user's password (and new log-in name). If NULL, do not extract the data.
6005 * @response is pointer to XML document with ISDS response
6006 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
6007 * @return IE_SUCCESS even if new user name has not been found because it's not
6008 * clear whether it's returned always. */
6009 static isds_error extract_credentials_delivery(struct isds_ctx *context,
6010 struct isds_credentials_delivery *credentials_delivery,
6011 xmlDocPtr response, const char *request_name) {
6012 isds_error err = IE_SUCCESS;
6013 xmlXPathContextPtr xpath_ctx = NULL;
6014 xmlXPathObjectPtr result = NULL;
6015 char *xpath_query = NULL;
6017 if (!context) return IE_INVALID_CONTEXT;
6018 if (credentials_delivery) {
6019 zfree(credentials_delivery->token);
6020 zfree(credentials_delivery->new_user_name);
6022 if (!response || !request_name || !*request_name) return IE_INVAL;
6025 /* Extract optional token */
6026 if (credentials_delivery) {
6027 xpath_ctx = xmlXPathNewContext(response);
6028 if (!xpath_ctx) {
6029 err = IE_ERROR;
6030 goto leave;
6032 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6033 err = IE_ERROR;
6034 goto leave;
6037 /* Verify root element */
6038 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
6039 request_name)) {
6040 err = IE_NOMEM;
6041 goto leave;
6043 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
6044 if (!result) {
6045 err = IE_ERROR;
6046 goto leave;
6048 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6049 char *request_name_locale = _isds_utf82locale(request_name);
6050 isds_log(ILF_ISDS, ILL_WARNING,
6051 _("Wrong element in ISDS response for %s request "
6052 "while extracting credentials delivery details\n"),
6053 request_name_locale);
6054 free(request_name_locale);
6055 err = IE_ERROR;
6056 goto leave;
6058 xpath_ctx->node = result->nodesetval->nodeTab[0];
6061 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6062 * optional. */
6063 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
6065 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
6066 if (!credentials_delivery->token) {
6067 char *request_name_locale = _isds_utf82locale(request_name);
6068 isds_log(ILF_ISDS, ILL_ERR,
6069 _("ISDS did not return token on %s request "
6070 "even if requested\n"), request_name_locale);
6071 free(request_name_locale);
6072 err = IE_ERROR;
6076 leave:
6077 free(xpath_query);
6078 xmlXPathFreeObject(result);
6079 xmlXPathFreeContext(xpath_ctx);
6081 return err;
6085 /* Build XSD:tCreateDBInput request type for box creating.
6086 * @context is session context
6087 * @request outputs built XML tree
6088 * @service_name is request name of SERVICE_DB_MANIPULATION service
6089 * @box is box description to create including single primary user (in case of
6090 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6091 * ignored.
6092 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6093 * box, or contact address of PFO box owner)
6094 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6095 * @upper_box_id is optional ID of supper box if currently created box is
6096 * subordinated.
6097 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6098 * don't care.
6099 * @credentials_delivery is valid pointer if ISDS should return token that box
6100 * owner can use to obtain his new credentials in on-line way. Then valid email
6101 * member value should be supplied.
6102 * @approval is optional external approval of box manipulation */
6103 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
6104 xmlNodePtr *request, const xmlChar *service_name,
6105 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6106 const xmlChar *former_names, const xmlChar *upper_box_id,
6107 const xmlChar *ceo_label,
6108 const struct isds_credentials_delivery *credentials_delivery,
6109 const struct isds_approval *approval) {
6110 isds_error err = IE_SUCCESS;
6111 xmlNsPtr isds_ns = NULL;
6112 xmlNodePtr node, dbPrimaryUsers;
6113 xmlChar *string = NULL;
6114 const struct isds_list *item;
6117 if (!context) return IE_INVALID_CONTEXT;
6118 if (!request || !service_name || service_name[0] == '\0' || !box)
6119 return IE_INVAL;
6122 /* Build CreateDataBox-similar request */
6123 *request = xmlNewNode(NULL, service_name);
6124 if (!*request) {
6125 char *service_name_locale = _isds_utf82locale((char*) service_name);
6126 isds_printf_message(context, _("Could build %s request"),
6127 service_name_locale);
6128 free(service_name_locale);
6129 return IE_ERROR;
6131 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
6132 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
6133 if (!isds_ns) {
6134 isds_log_message(context, _("Could not create ISDS1 name space"));
6135 xmlFreeNode(*request);
6136 return IE_ERROR;
6138 } else {
6139 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6140 if (!isds_ns) {
6141 isds_log_message(context, _("Could not create ISDS name space"));
6142 xmlFreeNode(*request);
6143 return IE_ERROR;
6146 xmlSetNs(*request, isds_ns);
6148 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6149 err = insert_DbOwnerInfo(context, box, node);
6150 if (err) goto leave;
6152 /* Insert users */
6153 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6154 * verbose documentation allows none dbUserInfo */
6155 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6156 for (item = users; item; item = item->next) {
6157 if (item->data) {
6158 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6159 err = insert_DbUserInfo(context,
6160 (struct isds_DbUserInfo *) item->data, 1, node);
6161 if (err) goto leave;
6165 INSERT_STRING(*request, "dbFormerNames", former_names);
6166 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6167 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6169 err = insert_credentials_delivery(context, credentials_delivery, *request);
6170 if (err) goto leave;
6172 err = insert_GExtApproval(context, approval, *request);
6173 if (err) goto leave;
6175 leave:
6176 if (err) {
6177 xmlFreeNode(*request);
6178 *request = NULL;
6180 free(string);
6181 return err;
6183 #endif /* HAVE_LIBCURL */
6186 /* Create new box.
6187 * @context is session context
6188 * @box is box description to create including single primary user (in case of
6189 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6190 * ignored. It outputs box ID assigned by ISDS in dbID element.
6191 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6192 * box, or contact address of PFO box owner)
6193 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6194 * @upper_box_id is optional ID of supper box if currently created box is
6195 * subordinated.
6196 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6197 * @credentials_delivery is NULL if new password should be delivered off-line
6198 * to box owner. It is valid pointer if owner should obtain new password on-line
6199 * on dedicated web server. Then input @credentials_delivery.email value is
6200 * his e-mail address he must provide to dedicated web server together
6201 * with output reallocated @credentials_delivery.token member. Output
6202 * member @credentials_delivery.new_user_name is unused up on this call.
6203 * @approval is optional external approval of box manipulation
6204 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6205 * NULL, if you don't care.*/
6206 isds_error isds_add_box(struct isds_ctx *context,
6207 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6208 const char *former_names, const char *upper_box_id,
6209 const char *ceo_label,
6210 struct isds_credentials_delivery *credentials_delivery,
6211 const struct isds_approval *approval, char **refnumber) {
6212 isds_error err = IE_SUCCESS;
6213 #if HAVE_LIBCURL
6214 xmlNodePtr request = NULL;
6215 xmlDocPtr response = NULL;
6216 xmlXPathContextPtr xpath_ctx = NULL;
6217 xmlXPathObjectPtr result = NULL;
6218 #endif
6221 if (!context) return IE_INVALID_CONTEXT;
6222 zfree(context->long_message);
6223 if (credentials_delivery) {
6224 zfree(credentials_delivery->token);
6225 zfree(credentials_delivery->new_user_name);
6227 if (!box) return IE_INVAL;
6229 #if HAVE_LIBCURL
6230 /* Scratch box ID */
6231 zfree(box->dbID);
6233 /* Build CreateDataBox request */
6234 err = build_CreateDBInput_request(context,
6235 &request, BAD_CAST "CreateDataBox",
6236 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6237 (xmlChar *) ceo_label, credentials_delivery, approval);
6238 if (err) goto leave;
6240 /* Send it to server and process response */
6241 err = send_destroy_request_check_response(context,
6242 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6243 &response, (xmlChar **) refnumber, NULL);
6245 /* Extract box ID */
6246 xpath_ctx = xmlXPathNewContext(response);
6247 if (!xpath_ctx) {
6248 err = IE_ERROR;
6249 goto leave;
6251 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6252 err = IE_ERROR;
6253 goto leave;
6255 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6257 /* Extract optional token */
6258 err = extract_credentials_delivery(context, credentials_delivery, response,
6259 "CreateDataBox");
6261 leave:
6262 xmlXPathFreeObject(result);
6263 xmlXPathFreeContext(xpath_ctx);
6264 xmlFreeDoc(response);
6265 xmlFreeNode(request);
6267 if (!err) {
6268 isds_log(ILF_ISDS, ILL_DEBUG,
6269 _("CreateDataBox request processed by server successfully.\n"));
6271 #else /* not HAVE_LIBCURL */
6272 err = IE_NOTSUP;
6273 #endif
6275 return err;
6279 /* Notify ISDS about new PFO entity.
6280 * This function has no real effect.
6281 * @context is session context
6282 * @box is PFO description including single primary user. aifoIsds,
6283 * address->adCode, address->adDistrict members are ignored.
6284 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6285 * @former_names is optional undocumented string. Pass NULL if you don't care.
6286 * @upper_box_id is optional ID of supper box if currently created box is
6287 * subordinated.
6288 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6289 * @approval is optional external approval of box manipulation
6290 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6291 * NULL, if you don't care.*/
6292 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6293 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6294 const char *former_names, const char *upper_box_id,
6295 const char *ceo_label, const struct isds_approval *approval,
6296 char **refnumber) {
6297 isds_error err = IE_SUCCESS;
6298 #if HAVE_LIBCURL
6299 xmlNodePtr request = NULL;
6300 #endif
6302 if (!context) return IE_INVALID_CONTEXT;
6303 zfree(context->long_message);
6304 if (!box) return IE_INVAL;
6306 #if HAVE_LIBCURL
6307 /* Build CreateDataBoxPFOInfo request */
6308 err = build_CreateDBInput_request(context,
6309 &request, BAD_CAST "CreateDataBoxPFOInfo",
6310 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6311 (xmlChar *) ceo_label, NULL, approval);
6312 if (err) goto leave;
6314 /* Send it to server and process response */
6315 err = send_request_check_drop_response(context,
6316 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6317 (xmlChar **) refnumber);
6318 /* XXX: XML Schema names output dbID element but textual documentation
6319 * states no box identifier is returned. */
6320 leave:
6321 xmlFreeNode(request);
6322 #else /* not HAVE_LIBCURL */
6323 err = IE_NOTSUP;
6324 #endif
6325 return err;
6329 /* Common implementation for removing given box.
6330 * @context is session context
6331 * @service_name is UTF-8 encoded name fo ISDS service
6332 * @box is box description to delete. aifoIsds, address->adCode,
6333 * address->adDistrict members are ignored.
6334 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6335 * carry sane value. If NULL, do not inject this information into request.
6336 * @approval is optional external approval of box manipulation
6337 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6338 * NULL, if you don't care.*/
6339 static isds_error _isds_delete_box_common(struct isds_ctx *context,
6340 const xmlChar *service_name,
6341 const struct isds_DbOwnerInfo *box, const struct tm *since,
6342 const struct isds_approval *approval, char **refnumber) {
6343 isds_error err = IE_SUCCESS;
6344 #if HAVE_LIBCURL
6345 xmlNsPtr isds_ns = NULL;
6346 xmlNodePtr request = NULL;
6347 xmlNodePtr node;
6348 xmlChar *string = NULL;
6349 #endif
6352 if (!context) return IE_INVALID_CONTEXT;
6353 zfree(context->long_message);
6354 if (!service_name || !*service_name || !box) return IE_INVAL;
6357 #if HAVE_LIBCURL
6358 /* Build DeleteDataBox(Promptly) request */
6359 request = xmlNewNode(NULL, service_name);
6360 if (!request) {
6361 char *service_name_locale = _isds_utf82locale((char*)service_name);
6362 isds_printf_message(context,
6363 _("Could build %s request"), service_name_locale);
6364 free(service_name_locale);
6365 return IE_ERROR;
6367 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6368 if(!isds_ns) {
6369 isds_log_message(context, _("Could not create ISDS name space"));
6370 xmlFreeNode(request);
6371 return IE_ERROR;
6373 xmlSetNs(request, isds_ns);
6375 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6376 err = insert_DbOwnerInfo(context, box, node);
6377 if (err) goto leave;
6379 if (since) {
6380 err = tm2datestring(since, &string);
6381 if (err) {
6382 isds_log_message(context,
6383 _("Could not convert `since' argument to ISO date string"));
6384 goto leave;
6386 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6387 zfree(string);
6390 err = insert_GExtApproval(context, approval, request);
6391 if (err) goto leave;
6394 /* Send it to server and process response */
6395 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6396 service_name, &request, (xmlChar **) refnumber);
6398 leave:
6399 xmlFreeNode(request);
6400 free(string);
6401 #else /* not HAVE_LIBCURL */
6402 err = IE_NOTSUP;
6403 #endif
6404 return err;
6408 /* Remove given box permanently.
6409 * @context is session context
6410 * @box is box description to delete. aifoIsds, address->adCode,
6411 * address->adDistrict members are ignored.
6412 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6413 * carry sane value.
6414 * @approval is optional external approval of box manipulation
6415 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6416 * NULL, if you don't care.*/
6417 isds_error isds_delete_box(struct isds_ctx *context,
6418 const struct isds_DbOwnerInfo *box, const struct tm *since,
6419 const struct isds_approval *approval, char **refnumber) {
6420 if (!context) return IE_INVALID_CONTEXT;
6421 zfree(context->long_message);
6422 if (!box || !since) return IE_INVAL;
6424 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6425 box, since, approval, refnumber);
6429 /* Undocumented function.
6430 * @context is session context
6431 * @box is box description to delete. aifoIsds, address->adCode,
6432 * address->adDistrict members are ignored.
6433 * @approval is optional external approval of box manipulation
6434 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6435 * NULL, if you don't care.*/
6436 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6437 const struct isds_DbOwnerInfo *box,
6438 const struct isds_approval *approval, char **refnumber) {
6439 if (!context) return IE_INVALID_CONTEXT;
6440 zfree(context->long_message);
6441 if (!box) return IE_INVAL;
6443 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6444 box, NULL, approval, refnumber);
6448 /* Update data about given box.
6449 * @context is session context
6450 * @old_box current box description. aifoIsds, address->adCode,
6451 * address->adDistrict members are ignored.
6452 * @new_box are updated data about @old_box. aifoIsds, address->adCode,
6453 * address->adDistrict members are ignored.
6454 * @approval is optional external approval of box manipulation
6455 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6456 * NULL, if you don't care.*/
6457 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6458 const struct isds_DbOwnerInfo *old_box,
6459 const struct isds_DbOwnerInfo *new_box,
6460 const struct isds_approval *approval, char **refnumber) {
6461 isds_error err = IE_SUCCESS;
6462 #if HAVE_LIBCURL
6463 xmlNsPtr isds_ns = NULL;
6464 xmlNodePtr request = NULL;
6465 xmlNodePtr node;
6466 #endif
6469 if (!context) return IE_INVALID_CONTEXT;
6470 zfree(context->long_message);
6471 if (!old_box || !new_box) return IE_INVAL;
6474 #if HAVE_LIBCURL
6475 /* Build UpdateDataBoxDescr request */
6476 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6477 if (!request) {
6478 isds_log_message(context,
6479 _("Could build UpdateDataBoxDescr request"));
6480 return IE_ERROR;
6482 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6483 if(!isds_ns) {
6484 isds_log_message(context, _("Could not create ISDS name space"));
6485 xmlFreeNode(request);
6486 return IE_ERROR;
6488 xmlSetNs(request, isds_ns);
6490 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6491 err = insert_DbOwnerInfo(context, old_box, node);
6492 if (err) goto leave;
6494 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6495 err = insert_DbOwnerInfo(context, new_box, node);
6496 if (err) goto leave;
6498 err = insert_GExtApproval(context, approval, request);
6499 if (err) goto leave;
6502 /* Send it to server and process response */
6503 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6504 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6506 leave:
6507 xmlFreeNode(request);
6508 #else /* not HAVE_LIBCURL */
6509 err = IE_NOTSUP;
6510 #endif
6512 return err;
6516 #if HAVE_LIBCURL
6517 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6518 * code
6519 * @context is session context
6520 * @service is SOAP service
6521 * @service_name is name of request in @service
6522 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6523 * @box_id is box ID of interest
6524 * @approval is optional external approval of box manipulation
6525 * @response is server SOAP body response as XML document
6526 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6527 * NULL, if you don't care.
6528 * @return error coded from lower layer, context message will be set up
6529 * appropriately. */
6530 static isds_error build_send_dbid_request_check_response(
6531 struct isds_ctx *context, const isds_service service,
6532 const xmlChar *service_name, const xmlChar *box_id_element,
6533 const xmlChar *box_id, const struct isds_approval *approval,
6534 xmlDocPtr *response, xmlChar **refnumber) {
6536 isds_error err = IE_SUCCESS;
6537 char *service_name_locale = NULL, *box_id_locale = NULL;
6538 xmlNodePtr request = NULL, node;
6539 xmlNsPtr isds_ns = NULL;
6541 if (!context) return IE_INVALID_CONTEXT;
6542 if (!service_name || !box_id) return IE_INVAL;
6543 if (!response) return IE_INVAL;
6545 /* Free output argument */
6546 xmlFreeDoc(*response); *response = NULL;
6548 /* Prepare strings */
6549 service_name_locale = _isds_utf82locale((char*)service_name);
6550 if (!service_name_locale) {
6551 err = IE_NOMEM;
6552 goto leave;
6554 box_id_locale = _isds_utf82locale((char*)box_id);
6555 if (!box_id_locale) {
6556 err = IE_NOMEM;
6557 goto leave;
6560 /* Build request */
6561 request = xmlNewNode(NULL, service_name);
6562 if (!request) {
6563 isds_printf_message(context,
6564 _("Could not build %s request for %s box"), service_name_locale,
6565 box_id_locale);
6566 err = IE_ERROR;
6567 goto leave;
6569 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6570 if(!isds_ns) {
6571 isds_log_message(context, _("Could not create ISDS name space"));
6572 err = IE_ERROR;
6573 goto leave;
6575 xmlSetNs(request, isds_ns);
6577 /* Add XSD:tIdDbInput children */
6578 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6579 INSERT_STRING(request, box_id_element, box_id);
6580 err = insert_GExtApproval(context, approval, request);
6581 if (err) goto leave;
6583 /* Send request and check response*/
6584 err = send_destroy_request_check_response(context,
6585 service, service_name, &request, response, refnumber, NULL);
6587 leave:
6588 free(service_name_locale);
6589 free(box_id_locale);
6590 xmlFreeNode(request);
6591 return err;
6593 #endif /* HAVE_LIBCURL */
6596 /* Get data about all users assigned to given box.
6597 * @context is session context
6598 * @box_id is box ID
6599 * @users is automatically reallocated list of struct isds_DbUserInfo */
6600 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6601 struct isds_list **users) {
6602 isds_error err = IE_SUCCESS;
6603 #if HAVE_LIBCURL
6604 xmlDocPtr response = NULL;
6605 xmlXPathContextPtr xpath_ctx = NULL;
6606 xmlXPathObjectPtr result = NULL;
6607 int i;
6608 struct isds_list *item, *prev_item = NULL;
6609 #endif
6611 if (!context) return IE_INVALID_CONTEXT;
6612 zfree(context->long_message);
6613 if (!users || !box_id) return IE_INVAL;
6614 isds_list_free(users);
6617 #if HAVE_LIBCURL
6618 /* Do request and check for success */
6619 err = build_send_dbid_request_check_response(context,
6620 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6621 BAD_CAST box_id, NULL, &response, NULL);
6622 if (err) goto leave;
6625 /* Extract data */
6626 /* Prepare structure */
6627 xpath_ctx = xmlXPathNewContext(response);
6628 if (!xpath_ctx) {
6629 err = IE_ERROR;
6630 goto leave;
6632 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6633 err = IE_ERROR;
6634 goto leave;
6637 /* Set context node */
6638 result = xmlXPathEvalExpression(BAD_CAST
6639 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6640 xpath_ctx);
6641 if (!result) {
6642 err = IE_ERROR;
6643 goto leave;
6645 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6646 /* Iterate over all users */
6647 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6649 /* Prepare structure */
6650 item = calloc(1, sizeof(*item));
6651 if (!item) {
6652 err = IE_NOMEM;
6653 goto leave;
6655 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6656 if (i == 0) *users = item;
6657 else prev_item->next = item;
6658 prev_item = item;
6660 /* Extract it */
6661 xpath_ctx->node = result->nodesetval->nodeTab[i];
6662 err = extract_DbUserInfo(context,
6663 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6664 if (err) goto leave;
6668 leave:
6669 if (err) {
6670 isds_list_free(users);
6673 xmlXPathFreeObject(result);
6674 xmlXPathFreeContext(xpath_ctx);
6675 xmlFreeDoc(response);
6677 if (!err)
6678 isds_log(ILF_ISDS, ILL_DEBUG,
6679 _("GetDataBoxUsers request processed by server "
6680 "successfully.\n"));
6681 #else /* not HAVE_LIBCURL */
6682 err = IE_NOTSUP;
6683 #endif
6685 return err;
6689 /* Update data about user assigned to given box.
6690 * @context is session context
6691 * @box is box identification. aifoIsds, address->adCode,
6692 * address->adDistrict members are ignored.
6693 * @old_user identifies user to update, aifo_ticket member is ignored
6694 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6695 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6696 * NULL, if you don't care.*/
6697 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6698 const struct isds_DbOwnerInfo *box,
6699 const struct isds_DbUserInfo *old_user,
6700 const struct isds_DbUserInfo *new_user,
6701 char **refnumber) {
6702 isds_error err = IE_SUCCESS;
6703 #if HAVE_LIBCURL
6704 xmlNsPtr isds_ns = NULL;
6705 xmlNodePtr request = NULL;
6706 xmlNodePtr node;
6707 #endif
6710 if (!context) return IE_INVALID_CONTEXT;
6711 zfree(context->long_message);
6712 if (!box || !old_user || !new_user) return IE_INVAL;
6715 #if HAVE_LIBCURL
6716 /* Build UpdateDataBoxUser request */
6717 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6718 if (!request) {
6719 isds_log_message(context,
6720 _("Could build UpdateDataBoxUser request"));
6721 return IE_ERROR;
6723 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6724 if(!isds_ns) {
6725 isds_log_message(context, _("Could not create ISDS name space"));
6726 xmlFreeNode(request);
6727 return IE_ERROR;
6729 xmlSetNs(request, isds_ns);
6731 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6732 err = insert_DbOwnerInfo(context, box, node);
6733 if (err) goto leave;
6735 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6736 err = insert_DbUserInfo(context, old_user, 0, node);
6737 if (err) goto leave;
6739 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6740 err = insert_DbUserInfo(context, new_user, 0, node);
6741 if (err) goto leave;
6743 /* Send it to server and process response */
6744 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6745 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6747 leave:
6748 xmlFreeNode(request);
6749 #else /* not HAVE_LIBCURL */
6750 err = IE_NOTSUP;
6751 #endif
6753 return err;
6757 /* Undocumented function.
6758 * @context is session context
6759 * @box_id is UTF-8 encoded box identifier
6760 * @token is UTF-8 encoded temporary password
6761 * @user_id outputs UTF-8 encoded reallocated user identifier
6762 * @password outpus UTF-8 encoded reallocated user password
6763 * Output arguments will be nulled in case of error */
6764 isds_error isds_activate(struct isds_ctx *context,
6765 const char *box_id, const char *token,
6766 char **user_id, char **password) {
6767 isds_error err = IE_SUCCESS;
6768 #if HAVE_LIBCURL
6769 xmlNsPtr isds_ns = NULL;
6770 xmlNodePtr request = NULL, node;
6771 xmlDocPtr response = NULL;
6772 xmlXPathContextPtr xpath_ctx = NULL;
6773 xmlXPathObjectPtr result = NULL;
6774 #endif
6777 if (!context) return IE_INVALID_CONTEXT;
6778 zfree(context->long_message);
6780 if (user_id) zfree(*user_id);
6781 if (password) zfree(*password);
6783 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6786 #if HAVE_LIBCURL
6787 /* Build Activate request */
6788 request = xmlNewNode(NULL, BAD_CAST "Activate");
6789 if (!request) {
6790 isds_log_message(context, _("Could build Activate request"));
6791 return IE_ERROR;
6793 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6794 if(!isds_ns) {
6795 isds_log_message(context, _("Could not create ISDS name space"));
6796 xmlFreeNode(request);
6797 return IE_ERROR;
6799 xmlSetNs(request, isds_ns);
6801 INSERT_STRING(request, "dbAccessDataId", token);
6802 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6803 INSERT_STRING(request, "dbID", box_id);
6806 /* Send request and check response*/
6807 err = send_destroy_request_check_response(context,
6808 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6809 &response, NULL, NULL);
6810 if (err) goto leave;
6813 /* Extract data */
6814 xpath_ctx = xmlXPathNewContext(response);
6815 if (!xpath_ctx) {
6816 err = IE_ERROR;
6817 goto leave;
6819 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6820 err = IE_ERROR;
6821 goto leave;
6823 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6824 xpath_ctx);
6825 if (!result) {
6826 err = IE_ERROR;
6827 goto leave;
6829 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6830 isds_log_message(context, _("Missing ActivateResponse element"));
6831 err = IE_ISDS;
6832 goto leave;
6834 if (result->nodesetval->nodeNr > 1) {
6835 isds_log_message(context, _("Multiple ActivateResponse element"));
6836 err = IE_ISDS;
6837 goto leave;
6839 xpath_ctx->node = result->nodesetval->nodeTab[0];
6840 xmlXPathFreeObject(result); result = NULL;
6842 EXTRACT_STRING("isds:userId", *user_id);
6843 if (!*user_id)
6844 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6845 "but did not return `userId' element.\n"));
6847 EXTRACT_STRING("isds:password", *password);
6848 if (!*password)
6849 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6850 "but did not return `password' element.\n"));
6852 leave:
6853 xmlXPathFreeObject(result);
6854 xmlXPathFreeContext(xpath_ctx);
6855 xmlFreeDoc(response);
6856 xmlFreeNode(request);
6858 if (!err)
6859 isds_log(ILF_ISDS, ILL_DEBUG,
6860 _("Activate request processed by server successfully.\n"));
6861 #else /* not HAVE_LIBCURL */
6862 err = IE_NOTSUP;
6863 #endif
6865 return err;
6869 /* Reset credentials of user assigned to given box.
6870 * @context is session context
6871 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
6872 * members are ignored.
6873 * @user identifies user to reset password, aifo_ticket member is ignored
6874 * @fee_paid is true if fee has been paid, false otherwise
6875 * @approval is optional external approval of box manipulation
6876 * @credentials_delivery is NULL if new password should be delivered off-line
6877 * to the user. It is valid pointer if user should obtain new password on-line
6878 * on dedicated web server. Then input @credentials_delivery.email value is
6879 * user's e-mail address user must provide to dedicated web server together
6880 * with @credentials_delivery.token. The output reallocated token user needs
6881 * to use to authorize on the web server to view his new password. Output
6882 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6883 * ISDS changed up on this call. (No reason why server could change the name
6884 * is known now.)
6885 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6886 * NULL, if you don't care.*/
6887 isds_error isds_reset_password(struct isds_ctx *context,
6888 const struct isds_DbOwnerInfo *box,
6889 const struct isds_DbUserInfo *user,
6890 const _Bool fee_paid, const struct isds_approval *approval,
6891 struct isds_credentials_delivery *credentials_delivery,
6892 char **refnumber) {
6893 isds_error err = IE_SUCCESS;
6894 #if HAVE_LIBCURL
6895 xmlNsPtr isds_ns = NULL;
6896 xmlNodePtr request = NULL, node;
6897 xmlDocPtr response = NULL;
6898 #endif
6901 if (!context) return IE_INVALID_CONTEXT;
6902 zfree(context->long_message);
6904 if (credentials_delivery) {
6905 zfree(credentials_delivery->token);
6906 zfree(credentials_delivery->new_user_name);
6908 if (!box || !user) return IE_INVAL;
6911 #if HAVE_LIBCURL
6912 /* Build NewAccessData request */
6913 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6914 if (!request) {
6915 isds_log_message(context,
6916 _("Could build NewAccessData request"));
6917 return IE_ERROR;
6919 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6920 if(!isds_ns) {
6921 isds_log_message(context, _("Could not create ISDS name space"));
6922 xmlFreeNode(request);
6923 return IE_ERROR;
6925 xmlSetNs(request, isds_ns);
6927 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6928 err = insert_DbOwnerInfo(context, box, node);
6929 if (err) goto leave;
6931 INSERT_ELEMENT(node, request, "dbUserInfo");
6932 err = insert_DbUserInfo(context, user, 0, node);
6933 if (err) goto leave;
6935 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6937 err = insert_credentials_delivery(context, credentials_delivery, request);
6938 if (err) goto leave;
6940 err = insert_GExtApproval(context, approval, request);
6941 if (err) goto leave;
6943 /* Send request and check response*/
6944 err = send_destroy_request_check_response(context,
6945 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6946 &response, (xmlChar **) refnumber, NULL);
6947 if (err) goto leave;
6950 /* Extract optional token */
6951 err = extract_credentials_delivery(context, credentials_delivery,
6952 response, "NewAccessData");
6954 leave:
6955 xmlFreeDoc(response);
6956 xmlFreeNode(request);
6958 if (!err)
6959 isds_log(ILF_ISDS, ILL_DEBUG,
6960 _("NewAccessData request processed by server "
6961 "successfully.\n"));
6962 #else /* not HAVE_LIBCURL */
6963 err = IE_NOTSUP;
6964 #endif
6966 return err;
6970 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6971 * code, destroy response and log success.
6972 * @context is ISDS session context.
6973 * @service_name is name of SERVICE_DB_MANIPULATION service
6974 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
6975 * members are ignored.
6976 * @user identifies user to remove
6977 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
6978 * @credentials_delivery is NULL if new user's password should be delivered
6979 * off-line to the user. It is valid pointer if user should obtain new
6980 * password on-line on dedicated web server. Then input
6981 * @credentials_delivery.email value is user's e-mail address user must
6982 * provide to dedicated web server together with @credentials_delivery.token.
6983 * The output reallocated token user needs to use to authorize on the web
6984 * server to view his new password. Output reallocated
6985 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6986 * assingned or changed up on this call.
6987 * @approval is optional external approval of box manipulation
6988 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6989 * NULL, if you don't care. */
6990 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6991 struct isds_ctx *context, const xmlChar *service_name,
6992 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6993 _Bool honor_aifo_ticket,
6994 struct isds_credentials_delivery *credentials_delivery,
6995 const struct isds_approval *approval, xmlChar **refnumber) {
6996 isds_error err = IE_SUCCESS;
6997 #if HAVE_LIBCURL
6998 xmlNsPtr isds_ns = NULL;
6999 xmlNodePtr request = NULL, node;
7000 xmlDocPtr response = NULL;
7001 #endif
7004 if (!context) return IE_INVALID_CONTEXT;
7005 zfree(context->long_message);
7006 if (credentials_delivery) {
7007 zfree(credentials_delivery->token);
7008 zfree(credentials_delivery->new_user_name);
7010 if (!service_name || service_name[0] == '\0' || !box || !user)
7011 return IE_INVAL;
7014 #if HAVE_LIBCURL
7015 /* Build NewAccessData or similar request */
7016 request = xmlNewNode(NULL, service_name);
7017 if (!request) {
7018 char *service_name_locale = _isds_utf82locale((char *) service_name);
7019 isds_printf_message(context, _("Could not build %s request"),
7020 service_name_locale);
7021 free(service_name_locale);
7022 return IE_ERROR;
7024 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7025 if(!isds_ns) {
7026 isds_log_message(context, _("Could not create ISDS name space"));
7027 xmlFreeNode(request);
7028 return IE_ERROR;
7030 xmlSetNs(request, isds_ns);
7032 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7033 err = insert_DbOwnerInfo(context, box, node);
7034 if (err) goto leave;
7036 INSERT_ELEMENT(node, request, "dbUserInfo");
7037 err = insert_DbUserInfo(context, user, honor_aifo_ticket, node);
7038 if (err) goto leave;
7040 err = insert_credentials_delivery(context, credentials_delivery, request);
7041 if (err) goto leave;
7043 err = insert_GExtApproval(context, approval, request);
7044 if (err) goto leave;
7047 /* Send request and check response*/
7048 err = send_destroy_request_check_response(context,
7049 SERVICE_DB_MANIPULATION, service_name, &request, &response,
7050 refnumber, NULL);
7052 xmlFreeNode(request);
7053 request = NULL;
7055 /* Pick up credentials_delivery if requested */
7056 err = extract_credentials_delivery(context, credentials_delivery, response,
7057 (char *)service_name);
7059 leave:
7060 xmlFreeDoc(response);
7061 if (request) xmlFreeNode(request);
7063 if (!err) {
7064 char *service_name_locale = _isds_utf82locale((char *) service_name);
7065 isds_log(ILF_ISDS, ILL_DEBUG,
7066 _("%s request processed by server successfully.\n"),
7067 service_name_locale);
7068 free(service_name_locale);
7070 #else /* not HAVE_LIBCURL */
7071 err = IE_NOTSUP;
7072 #endif
7074 return err;
7078 /* Assign new user to given box.
7079 * @context is session context
7080 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7081 * members are ignored.
7082 * @user defines new user to add
7083 * @credentials_delivery is NULL if new user's password should be delivered
7084 * off-line to the user. It is valid pointer if user should obtain new
7085 * password on-line on dedicated web server. Then input
7086 * @credentials_delivery.email value is user's e-mail address user must
7087 * provide to dedicated web server together with @credentials_delivery.token.
7088 * The output reallocated token user needs to use to authorize on the web
7089 * server to view his new password. Output reallocated
7090 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7091 * assingned up on this call.
7092 * @approval is optional external approval of box manipulation
7093 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7094 * NULL, if you don't care.*/
7095 isds_error isds_add_user(struct isds_ctx *context,
7096 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7097 struct isds_credentials_delivery *credentials_delivery,
7098 const struct isds_approval *approval, char **refnumber) {
7099 return build_send_manipulationboxuser_request_check_drop_response(context,
7100 BAD_CAST "AddDataBoxUser", box, user, 1, credentials_delivery,
7101 approval, (xmlChar **) refnumber);
7105 /* Remove user assigned to given box.
7106 * @context is session context
7107 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7108 * members are ignored.
7109 * @user identifies user to remove, aifo_ticket member is ignored
7110 * @approval is optional external approval of box manipulation
7111 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7112 * NULL, if you don't care.*/
7113 isds_error isds_delete_user(struct isds_ctx *context,
7114 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7115 const struct isds_approval *approval, char **refnumber) {
7116 return build_send_manipulationboxuser_request_check_drop_response(context,
7117 BAD_CAST "DeleteDataBoxUser", box, user, 0, NULL, approval,
7118 (xmlChar **) refnumber);
7122 /* Get list of boxes in ZIP archive.
7123 * @context is session context
7124 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7125 * System recognizes following values currently: ALL (all boxes), UPG
7126 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7127 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7128 * commercial messages). This argument is a string because specification
7129 * states new values can appear in the future. Not all list types are
7130 * available to all users.
7131 * @buffer is automatically reallocated memory to store the list of boxes. The
7132 * list is zipped CSV file.
7133 * @buffer_length is size of @buffer data in bytes.
7134 * In case of error @buffer will be freed and @buffer_length will be
7135 * undefined.*/
7136 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7137 const char *list_identifier, void **buffer, size_t *buffer_length) {
7138 isds_error err = IE_SUCCESS;
7139 #if HAVE_LIBCURL
7140 xmlNsPtr isds_ns = NULL;
7141 xmlNodePtr request = NULL, node;
7142 xmlDocPtr response = NULL;
7143 xmlXPathContextPtr xpath_ctx = NULL;
7144 xmlXPathObjectPtr result = NULL;
7145 char *string = NULL;
7146 #endif
7149 if (!context) return IE_INVALID_CONTEXT;
7150 zfree(context->long_message);
7151 if (buffer) zfree(*buffer);
7152 if (!buffer || !buffer_length) return IE_INVAL;
7155 #if HAVE_LIBCURL
7156 /* Check if connection is established
7157 * TODO: This check should be done downstairs. */
7158 if (!context->curl) return IE_CONNECTION_CLOSED;
7161 /* Build AuthenticateMessage request */
7162 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7163 if (!request) {
7164 isds_log_message(context,
7165 _("Could not build GetDataBoxList request"));
7166 return IE_ERROR;
7168 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7169 if(!isds_ns) {
7170 isds_log_message(context, _("Could not create ISDS name space"));
7171 xmlFreeNode(request);
7172 return IE_ERROR;
7174 xmlSetNs(request, isds_ns);
7175 INSERT_STRING(request, "dblType", list_identifier);
7177 /* Send request to server and process response */
7178 err = send_destroy_request_check_response(context,
7179 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7180 &response, NULL, NULL);
7181 if (err) goto leave;
7184 /* Extract Base-64 encoded ZIP file */
7185 xpath_ctx = xmlXPathNewContext(response);
7186 if (!xpath_ctx) {
7187 err = IE_ERROR;
7188 goto leave;
7190 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7191 err = IE_ERROR;
7192 goto leave;
7194 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7196 /* Decode non-empty archive */
7197 if (string && string[0] != '\0') {
7198 *buffer_length = _isds_b64decode(string, buffer);
7199 if (*buffer_length == (size_t) -1) {
7200 isds_printf_message(context,
7201 _("Error while Base64-decoding box list archive"));
7202 err = IE_ERROR;
7203 goto leave;
7208 leave:
7209 free(string);
7210 xmlXPathFreeObject(result);
7211 xmlXPathFreeContext(xpath_ctx);
7212 xmlFreeDoc(response);
7213 xmlFreeNode(request);
7215 if (!err) {
7216 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7217 "processed by server successfully.\n"));
7219 #else /* not HAVE_LIBCURL */
7220 err = IE_NOTSUP;
7221 #endif
7223 return err;
7227 /* Find boxes suiting given criteria.
7228 * @criteria is filter. You should fill in at least some members. aifoIsds,
7229 * address->adCode, address->adDistrict members are ignored.
7230 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7231 * possibly empty. Input NULL or valid old structure.
7232 * @return:
7233 * IE_SUCCESS if search succeeded, @boxes contains useful data
7234 * IE_NOEXIST if no such box exists, @boxes will be NULL
7235 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7236 * contains still valid data
7237 * other code if something bad happens. @boxes will be NULL. */
7238 isds_error isds_FindDataBox(struct isds_ctx *context,
7239 const struct isds_DbOwnerInfo *criteria,
7240 struct isds_list **boxes) {
7241 isds_error err = IE_SUCCESS;
7242 #if HAVE_LIBCURL
7243 _Bool truncated = 0;
7244 xmlNsPtr isds_ns = NULL;
7245 xmlNodePtr request = NULL;
7246 xmlDocPtr response = NULL;
7247 xmlChar *code = NULL, *message = NULL;
7248 xmlNodePtr db_owner_info;
7249 xmlXPathContextPtr xpath_ctx = NULL;
7250 xmlXPathObjectPtr result = NULL;
7251 xmlChar *string = NULL;
7252 #endif
7255 if (!context) return IE_INVALID_CONTEXT;
7256 zfree(context->long_message);
7257 if (!boxes) return IE_INVAL;
7258 isds_list_free(boxes);
7260 if (!criteria) {
7261 return IE_INVAL;
7264 #if HAVE_LIBCURL
7265 /* Check if connection is established
7266 * TODO: This check should be done downstairs. */
7267 if (!context->curl) return IE_CONNECTION_CLOSED;
7270 /* Build FindDataBox request */
7271 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
7272 if (!request) {
7273 isds_log_message(context,
7274 _("Could build FindDataBox request"));
7275 return IE_ERROR;
7277 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7278 if(!isds_ns) {
7279 isds_log_message(context, _("Could not create ISDS name space"));
7280 xmlFreeNode(request);
7281 return IE_ERROR;
7283 xmlSetNs(request, isds_ns);
7284 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7285 if (!db_owner_info) {
7286 isds_log_message(context, _("Could not add dbOwnerInfo child to "
7287 "FindDataBox element"));
7288 xmlFreeNode(request);
7289 return IE_ERROR;
7292 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
7293 if (err) goto leave;
7296 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
7298 /* Sent request */
7299 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7301 /* Destroy request */
7302 xmlFreeNode(request); request = NULL;
7304 if (err) {
7305 isds_log(ILF_ISDS, ILL_DEBUG,
7306 _("Processing ISDS response on FindDataBox "
7307 "request failed\n"));
7308 goto leave;
7311 /* Check for response status */
7312 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7313 &code, &message, NULL);
7314 if (err) {
7315 isds_log(ILF_ISDS, ILL_DEBUG,
7316 _("ISDS response on FindDataBox request is missing status\n"));
7317 goto leave;
7320 /* Request processed, but nothing found */
7321 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7322 !xmlStrcmp(code, BAD_CAST "5001")) {
7323 char *code_locale = _isds_utf82locale((char*)code);
7324 char *message_locale = _isds_utf82locale((char*)message);
7325 isds_log(ILF_ISDS, ILL_DEBUG,
7326 _("Server did not found any box on FindDataBox request "
7327 "(code=%s, message=%s)\n"), code_locale, message_locale);
7328 isds_log_message(context, message_locale);
7329 free(code_locale);
7330 free(message_locale);
7331 err = IE_NOEXIST;
7332 goto leave;
7335 /* Warning, not a error */
7336 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7337 char *code_locale = _isds_utf82locale((char*)code);
7338 char *message_locale = _isds_utf82locale((char*)message);
7339 isds_log(ILF_ISDS, ILL_DEBUG,
7340 _("Server truncated response on FindDataBox request "
7341 "(code=%s, message=%s)\n"), code_locale, message_locale);
7342 isds_log_message(context, message_locale);
7343 free(code_locale);
7344 free(message_locale);
7345 truncated = 1;
7348 /* Other error */
7349 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7350 char *code_locale = _isds_utf82locale((char*)code);
7351 char *message_locale = _isds_utf82locale((char*)message);
7352 isds_log(ILF_ISDS, ILL_DEBUG,
7353 _("Server refused FindDataBox request "
7354 "(code=%s, message=%s)\n"), code_locale, message_locale);
7355 isds_log_message(context, message_locale);
7356 free(code_locale);
7357 free(message_locale);
7358 err = IE_ISDS;
7359 goto leave;
7362 xpath_ctx = xmlXPathNewContext(response);
7363 if (!xpath_ctx) {
7364 err = IE_ERROR;
7365 goto leave;
7367 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7368 err = IE_ERROR;
7369 goto leave;
7372 /* Extract boxes if they present */
7373 result = xmlXPathEvalExpression(BAD_CAST
7374 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7375 xpath_ctx);
7376 if (!result) {
7377 err = IE_ERROR;
7378 goto leave;
7380 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7381 struct isds_list *item, *prev_item = NULL;
7382 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7383 item = calloc(1, sizeof(*item));
7384 if (!item) {
7385 err = IE_NOMEM;
7386 goto leave;
7389 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7390 if (i == 0) *boxes = item;
7391 else prev_item->next = item;
7392 prev_item = item;
7394 xpath_ctx->node = result->nodesetval->nodeTab[i];
7395 err = extract_DbOwnerInfo(context,
7396 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7397 if (err) goto leave;
7401 leave:
7402 if (err) {
7403 isds_list_free(boxes);
7404 } else {
7405 if (truncated) err = IE_2BIG;
7408 free(string);
7409 xmlFreeNode(request);
7410 xmlXPathFreeObject(result);
7411 xmlXPathFreeContext(xpath_ctx);
7413 free(code);
7414 free(message);
7415 xmlFreeDoc(response);
7417 if (!err)
7418 isds_log(ILF_ISDS, ILL_DEBUG,
7419 _("FindDataBox request processed by server successfully.\n"));
7420 #else /* not HAVE_LIBCURL */
7421 err = IE_NOTSUP;
7422 #endif
7424 return err;
7428 #if HAVE_LIBCURL
7429 /* Convert a string with match markers into a plain string with list of
7430 * pointers to the matches
7431 * @string is an UTF-8 encoded non-constant string with match markers
7432 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7433 * The markers will be removed from the string.
7434 * @starts is a reallocated list of static pointers into the @string pointing
7435 * to places where match start markers occured.
7436 * @ends is a reallocated list of static pointers into the @string pointing
7437 * to places where match end markers occured.
7438 * @return IE_SUCCESS in case of no failure. */
7439 static isds_error interpret_matches(xmlChar *string,
7440 struct isds_list **starts, struct isds_list **ends) {
7441 isds_error err = IE_SUCCESS;
7442 xmlChar *pointer, *destination, *source;
7443 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7445 isds_list_free(starts);
7446 isds_list_free(ends);
7447 if (NULL == starts || NULL == ends) return IE_INVAL;
7448 if (NULL == string) return IE_SUCCESS;
7450 for (pointer = string; *pointer != '\0';) {
7451 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7452 /* Remove the start marker */
7453 for (source = pointer + 14, destination = pointer;
7454 *source != '\0'; source++, destination++) {
7455 *destination = *source;
7457 *destination = '\0';
7458 /* Append the pointer into the list */
7459 item = calloc(1, sizeof(*item));
7460 if (!item) {
7461 err = IE_NOMEM;
7462 goto leave;
7464 item->destructor = (void (*)(void **))NULL;
7465 item->data = pointer;
7466 if (NULL == prev_start) *starts = item;
7467 else prev_start->next = item;
7468 prev_start = item;
7469 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7470 /* Remove the end marker */
7471 for (source = pointer + 12, destination = pointer;
7472 *source != '\0'; source++, destination++) {
7473 *destination = *source;
7475 *destination = '\0';
7476 /* Append the pointer into the list */
7477 item = calloc(1, sizeof(*item));
7478 if (!item) {
7479 err = IE_NOMEM;
7480 goto leave;
7482 item->destructor = (void (*)(void **))NULL;
7483 item->data = pointer;
7484 if (NULL == prev_end) *ends = item;
7485 else prev_end->next = item;
7486 prev_end = item;
7487 } else {
7488 pointer++;
7492 leave:
7493 if (err) {
7494 isds_list_free(starts);
7495 isds_list_free(ends);
7497 return err;
7501 /* Convert isds:dbResult XML tree into structure
7502 * @context is ISDS context.
7503 * @fulltext_result is automatically reallocated found box structure.
7504 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7505 * @collect_matches is true to interpret match markers.
7506 * In case of error @result will be freed. */
7507 static isds_error extract_dbResult(struct isds_ctx *context,
7508 struct isds_fulltext_result **fulltext_result,
7509 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7510 isds_error err = IE_SUCCESS;
7511 xmlXPathObjectPtr result = NULL;
7512 char *string = NULL;
7514 if (NULL == context) return IE_INVALID_CONTEXT;
7515 if (NULL == fulltext_result) return IE_INVAL;
7516 isds_fulltext_result_free(fulltext_result);
7517 if (!xpath_ctx) return IE_INVAL;
7520 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7521 if (NULL == *fulltext_result) {
7522 err = IE_NOMEM;
7523 goto leave;
7526 /* Extract data */
7527 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7529 EXTRACT_STRING("isds:dbType", string);
7530 if (NULL == string) {
7531 err = IE_ISDS;
7532 isds_log_message(context, _("Empty isds:dbType element"));
7533 goto leave;
7535 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7536 if (err) {
7537 if (err == IE_ENUM) {
7538 err = IE_ISDS;
7539 char *string_locale = _isds_utf82locale(string);
7540 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7541 string_locale);
7542 free(string_locale);
7544 goto leave;
7546 zfree(string);
7548 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7549 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7551 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7552 if (err) goto leave;
7554 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7555 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7556 (*fulltext_result)->dbEffectiveOVM);
7558 EXTRACT_STRING("isds:dbSendOptions", string);
7559 if (NULL == string) {
7560 err = IE_ISDS;
7561 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7562 goto leave;
7564 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7565 (*fulltext_result)->active = 1;
7566 (*fulltext_result)->public_sending = 1;
7567 (*fulltext_result)->commercial_sending = 0;
7568 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7569 (*fulltext_result)->active = 1;
7570 (*fulltext_result)->public_sending = 1;
7571 (*fulltext_result)->commercial_sending = 1;
7572 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7573 (*fulltext_result)->active = 1;
7574 (*fulltext_result)->public_sending = 0;
7575 (*fulltext_result)->commercial_sending = 1;
7576 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7577 (*fulltext_result)->active = 1;
7578 (*fulltext_result)->public_sending = 0;
7579 (*fulltext_result)->commercial_sending = 0;
7580 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7581 (*fulltext_result)->active = 0;
7582 (*fulltext_result)->public_sending = 0;
7583 (*fulltext_result)->commercial_sending = 0;
7584 } else {
7585 err = IE_ISDS;
7586 char *string_locale = _isds_utf82locale(string);
7587 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7588 string_locale);
7589 free(string_locale);
7590 goto leave;
7592 zfree(string);
7594 /* Interpret match marks */
7595 if (collect_matches) {
7596 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7597 &((*fulltext_result)->name_match_start),
7598 &((*fulltext_result)->name_match_end));
7599 if (err) goto leave;
7600 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7601 &((*fulltext_result)->address_match_start),
7602 &((*fulltext_result)->address_match_end));
7603 if (err) goto leave;
7606 leave:
7607 if (err) isds_fulltext_result_free(fulltext_result);
7608 free(string);
7609 xmlXPathFreeObject(result);
7610 return err;
7612 #endif /* HAVE_LIBCURL */
7615 /* Find boxes matching a given full-text criteria.
7616 * @context is a session context
7617 * @query is a non-empty string which consists of words to search
7618 * @target selects box attributes to search for @query words. Pass NULL if you
7619 * don't care.
7620 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7621 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7622 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7623 * which is DBTYPE_SYSTEM.
7624 * @page_size defines count of boxes to constitute a response page. It counts
7625 * from zero. Pass NULL to let server to use a default value (50 now).
7626 * @page_number defines ordinar number of the response page to return. It
7627 * counts from zero. Pass NULL to let server to use a default value (0 now).
7628 * @track_matches points to true for marking @query words found in the box
7629 * attributes. It points to false for not marking. Pass NULL to let the server
7630 * to use default value (false now).
7631 * @total_matching_boxes outputs reallocated number of all boxes matching the
7632 * query. Will be pointer to NULL if server did not provide the value.
7633 * Pass NULL if you don't care.
7634 * @current_page_beginning outputs reallocated ordinar number of the first box
7635 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7636 * server did not provide the value. Pass NULL if you don't care.
7637 * @current_page_size outputs reallocated count of boxes in the this @boxes
7638 * page. It will be pointer to NULL if the server did not provide the value.
7639 * Pass NULL if you don't care.
7640 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7641 * is the last one, false if more boxes match, NULL if the server did not
7642 * provude the value. Pass NULL if you don't care.
7643 * @boxes outputs reallocated list of isds_fulltext_result structures,
7644 * possibly empty.
7645 * @return:
7646 * IE_SUCCESS if search succeeded
7647 * IE_2BIG if @page_size is too large
7648 * other code if something bad happens; output arguments will be NULL. */
7649 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7650 const char *query,
7651 const isds_fulltext_target *target,
7652 const isds_DbType *box_type,
7653 const unsigned long int *page_size,
7654 const unsigned long int *page_number,
7655 const _Bool *track_matches,
7656 unsigned long int **total_matching_boxes,
7657 unsigned long int **current_page_beginning,
7658 unsigned long int **current_page_size,
7659 _Bool **last_page,
7660 struct isds_list **boxes) {
7661 isds_error err = IE_SUCCESS;
7662 #if HAVE_LIBCURL
7663 xmlNsPtr isds_ns = NULL;
7664 xmlNodePtr request = NULL;
7665 xmlDocPtr response = NULL;
7666 xmlNodePtr node;
7667 xmlXPathContextPtr xpath_ctx = NULL;
7668 xmlXPathObjectPtr result = NULL;
7669 const xmlChar *static_string = NULL;
7670 xmlChar *string = NULL;
7672 const xmlChar *codes[] = {
7673 BAD_CAST "1004",
7674 BAD_CAST "1152",
7675 BAD_CAST "1153",
7676 BAD_CAST "1154",
7677 BAD_CAST "1155",
7678 BAD_CAST "1156",
7679 BAD_CAST "9002",
7680 NULL
7682 const char *meanings[] = {
7683 N_("You are not allowed to perform the search"),
7684 N_("The query string is empty"),
7685 N_("Searched box ID is malformed"),
7686 N_("Searched organization ID is malformed"),
7687 N_("Invalid input"),
7688 N_("Requested page size is too large"),
7689 N_("Search engine internal error")
7691 const isds_error errors[] = {
7692 IE_ISDS,
7693 IE_INVAL,
7694 IE_INVAL,
7695 IE_INVAL,
7696 IE_INVAL,
7697 IE_2BIG,
7698 IE_ISDS
7700 struct code_map_isds_error map = {
7701 .codes = codes,
7702 .meanings = meanings,
7703 .errors = errors
7705 #endif
7708 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7709 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7710 if (NULL != current_page_size) zfree(*current_page_size);
7711 if (NULL != last_page) zfree(*last_page);
7712 isds_list_free(boxes);
7714 if (NULL == context) return IE_INVALID_CONTEXT;
7715 zfree(context->long_message);
7717 if (NULL == boxes) return IE_INVAL;
7719 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
7720 isds_log_message(context, _("Query string must be non-empty"));
7721 return IE_INVAL;
7724 #if HAVE_LIBCURL
7725 /* Check if connection is established
7726 * TODO: This check should be done downstairs. */
7727 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7729 /* Build FindDataBox request */
7730 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
7731 if (NULL == request) {
7732 isds_log_message(context,
7733 _("Could not build ISDSSearch2 request"));
7734 return IE_ERROR;
7736 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7737 if(NULL == isds_ns) {
7738 isds_log_message(context, _("Could not create ISDS name space"));
7739 xmlFreeNode(request);
7740 return IE_ERROR;
7742 xmlSetNs(request, isds_ns);
7744 INSERT_STRING(request, "searchText", query);
7746 if (NULL != target) {
7747 static_string = isds_fulltext_target2string(*(target));
7748 if (NULL == static_string) {
7749 isds_printf_message(context, _("Invalid target value: %d"),
7750 *(target));
7751 err = IE_ENUM;
7752 goto leave;
7755 INSERT_STRING(request, "searchType", static_string);
7756 static_string = NULL;
7758 if (NULL != box_type) {
7759 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7760 if (DBTYPE_SYSTEM == *box_type) {
7761 static_string = BAD_CAST "ALL";
7762 } else if (DBTYPE_OVM_MAIN == *box_type) {
7763 static_string = BAD_CAST "OVM_MAIN";
7764 } else {
7765 static_string = isds_DbType2string(*(box_type));
7766 if (NULL == static_string) {
7767 isds_printf_message(context, _("Invalid box type value: %d"),
7768 *(box_type));
7769 err = IE_ENUM;
7770 goto leave;
7774 INSERT_STRING(request, "searchScope", static_string);
7775 static_string = NULL;
7777 INSERT_ULONGINT(request, "page", page_number, string);
7778 INSERT_ULONGINT(request, "pageSize", page_size, string);
7779 INSERT_BOOLEAN(request, "highlighting", track_matches);
7781 /* Send request and check response */
7782 err = send_destroy_request_check_response(context,
7783 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
7784 &request, &response, NULL, &map);
7785 if (err) goto leave;
7787 /* Parse response */
7788 xpath_ctx = xmlXPathNewContext(response);
7789 if (NULL == xpath_ctx) {
7790 err = IE_ERROR;
7791 goto leave;
7793 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7794 err = IE_ERROR;
7795 goto leave;
7797 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
7798 xpath_ctx);
7799 if (!result) {
7800 err = IE_ERROR;
7801 goto leave;
7803 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7804 isds_log_message(context, _("Missing ISDSSearch2 element"));
7805 err = IE_ISDS;
7806 goto leave;
7808 if (result->nodesetval->nodeNr > 1) {
7809 isds_log_message(context, _("Multiple ISDSSearch2 element"));
7810 err = IE_ISDS;
7811 goto leave;
7813 xpath_ctx->node = result->nodesetval->nodeTab[0];
7814 xmlXPathFreeObject(result); result = NULL;
7817 /* Extract counters */
7818 if (NULL != total_matching_boxes) {
7819 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
7821 if (NULL != current_page_size) {
7822 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
7824 if (NULL != current_page_beginning) {
7825 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
7827 if (NULL != last_page) {
7828 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
7830 xmlXPathFreeObject(result); result = NULL;
7832 /* Extract boxes if they present */
7833 result = xmlXPathEvalExpression(BAD_CAST
7834 "isds:dbResults/isds:dbResult", xpath_ctx);
7835 if (NULL == result) {
7836 err = IE_ERROR;
7837 goto leave;
7839 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7840 struct isds_list *item, *prev_item = NULL;
7841 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7842 item = calloc(1, sizeof(*item));
7843 if (!item) {
7844 err = IE_NOMEM;
7845 goto leave;
7848 item->destructor = (void (*)(void **))isds_fulltext_result_free;
7849 if (i == 0) *boxes = item;
7850 else prev_item->next = item;
7851 prev_item = item;
7853 xpath_ctx->node = result->nodesetval->nodeTab[i];
7854 err = extract_dbResult(context,
7855 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
7856 (NULL == track_matches) ? 0 : *track_matches);
7857 if (err) goto leave;
7861 leave:
7862 if (err) {
7863 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7864 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7865 if (NULL != current_page_size) zfree(*current_page_size);
7866 if (NULL != last_page) zfree(*last_page);
7867 isds_list_free(boxes);
7870 free(string);
7871 xmlFreeNode(request);
7872 xmlXPathFreeObject(result);
7873 xmlXPathFreeContext(xpath_ctx);
7874 xmlFreeDoc(response);
7876 if (!err)
7877 isds_log(ILF_ISDS, ILL_DEBUG,
7878 _("ISDSSearch2 request processed by server successfully.\n"));
7879 #else /* not HAVE_LIBCURL */
7880 err = IE_NOTSUP;
7881 #endif
7883 return err;
7887 /* Get status of a box.
7888 * @context is ISDS session context.
7889 * @box_id is UTF-8 encoded box identifier as zero terminated string
7890 * @box_status is return value of box status.
7891 * @return:
7892 * IE_SUCCESS if box has been found and its status retrieved
7893 * IE_NOEXIST if box is not known to ISDS server
7894 * or other appropriate error.
7895 * You can use isds_DbState to enumerate box status. However out of enum
7896 * range value can be returned too. This is feature because ISDS
7897 * specification leaves the set of values open.
7898 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7899 * the box has been deleted, but ISDS still lists its former existence. */
7900 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7901 long int *box_status) {
7902 isds_error err = IE_SUCCESS;
7903 #if HAVE_LIBCURL
7904 xmlNsPtr isds_ns = NULL;
7905 xmlNodePtr request = NULL, db_id;
7906 xmlDocPtr response = NULL;
7907 xmlXPathContextPtr xpath_ctx = NULL;
7908 xmlXPathObjectPtr result = NULL;
7909 xmlChar *string = NULL;
7911 const xmlChar *codes[] = {
7912 BAD_CAST "5001",
7913 BAD_CAST "1007",
7914 BAD_CAST "2011",
7915 NULL
7917 const char *meanings[] = {
7918 "The box does not exist",
7919 "Box ID is malformed",
7920 "Box ID malformed",
7922 const isds_error errors[] = {
7923 IE_NOEXIST,
7924 IE_INVAL,
7925 IE_INVAL,
7927 struct code_map_isds_error map = {
7928 .codes = codes,
7929 .meanings = meanings,
7930 .errors = errors
7932 #endif
7934 if (!context) return IE_INVALID_CONTEXT;
7935 zfree(context->long_message);
7936 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7938 #if HAVE_LIBCURL
7939 /* Check if connection is established
7940 * TODO: This check should be done downstairs. */
7941 if (!context->curl) return IE_CONNECTION_CLOSED;
7944 /* Build CheckDataBox request */
7945 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7946 if (!request) {
7947 isds_log_message(context,
7948 _("Could build CheckDataBox request"));
7949 return IE_ERROR;
7951 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7952 if(!isds_ns) {
7953 isds_log_message(context, _("Could not create ISDS name space"));
7954 xmlFreeNode(request);
7955 return IE_ERROR;
7957 xmlSetNs(request, isds_ns);
7958 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7959 if (!db_id) {
7960 isds_log_message(context, _("Could not add dbID child to "
7961 "CheckDataBox element"));
7962 xmlFreeNode(request);
7963 return IE_ERROR;
7967 /* Send request and check response*/
7968 err = send_destroy_request_check_response(context,
7969 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7970 &request, &response, NULL, &map);
7971 if (err) goto leave;
7974 /* Extract data */
7975 xpath_ctx = xmlXPathNewContext(response);
7976 if (!xpath_ctx) {
7977 err = IE_ERROR;
7978 goto leave;
7980 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7981 err = IE_ERROR;
7982 goto leave;
7984 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7985 xpath_ctx);
7986 if (!result) {
7987 err = IE_ERROR;
7988 goto leave;
7990 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7991 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7992 err = IE_ISDS;
7993 goto leave;
7995 if (result->nodesetval->nodeNr > 1) {
7996 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7997 err = IE_ISDS;
7998 goto leave;
8000 xpath_ctx->node = result->nodesetval->nodeTab[0];
8001 xmlXPathFreeObject(result); result = NULL;
8003 EXTRACT_LONGINT("isds:dbState", box_status, 1);
8006 leave:
8007 free(string);
8008 xmlXPathFreeObject(result);
8009 xmlXPathFreeContext(xpath_ctx);
8011 xmlFreeDoc(response);
8013 if (!err)
8014 isds_log(ILF_ISDS, ILL_DEBUG,
8015 _("CheckDataBox request processed by server successfully.\n"));
8016 #else /* not HAVE_LIBCURL */
8017 err = IE_NOTSUP;
8018 #endif
8020 return err;
8024 /* Get list of permissions to send commercial messages.
8025 * @context is ISDS session context.
8026 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
8027 * @permissions is a reallocated list of permissions (struct
8028 * isds_commercial_permission*) to send commercial messages from @box_id. The
8029 * order of permissions is significant as the server applies the permissions
8030 * and associated pre-paid credits in the order. Empty list means no
8031 * permission.
8032 * @return:
8033 * IE_SUCCESS if the list has been obtained correctly,
8034 * or other appropriate error. */
8035 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
8036 const char *box_id, struct isds_list **permissions) {
8037 isds_error err = IE_SUCCESS;
8038 #if HAVE_LIBCURL
8039 xmlDocPtr response = NULL;
8040 xmlXPathContextPtr xpath_ctx = NULL;
8041 xmlXPathObjectPtr result = NULL;
8042 #endif
8044 if (!context) return IE_INVALID_CONTEXT;
8045 zfree(context->long_message);
8046 if (NULL == permissions) return IE_INVAL;
8047 isds_list_free(permissions);
8048 if (NULL == box_id) return IE_INVAL;
8050 #if HAVE_LIBCURL
8051 /* Check if connection is established */
8052 if (!context->curl) return IE_CONNECTION_CLOSED;
8054 /* Do request and check for success */
8055 err = build_send_dbid_request_check_response(context,
8056 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
8057 BAD_CAST box_id, NULL, &response, NULL);
8058 if (!err) {
8059 isds_log(ILF_ISDS, ILL_DEBUG,
8060 _("PDZInfo request processed by server successfully.\n"));
8063 /* Extract data */
8064 /* Prepare structure */
8065 xpath_ctx = xmlXPathNewContext(response);
8066 if (!xpath_ctx) {
8067 err = IE_ERROR;
8068 goto leave;
8070 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8071 err = IE_ERROR;
8072 goto leave;
8075 /* Set context node */
8076 result = xmlXPathEvalExpression(BAD_CAST
8077 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8078 xpath_ctx);
8079 if (!result) {
8080 err = IE_ERROR;
8081 goto leave;
8083 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8084 struct isds_list *prev_item = NULL;
8086 /* Iterate over all permission records */
8087 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8088 struct isds_list *item;
8090 /* Prepare structure */
8091 item = calloc(1, sizeof(*item));
8092 if (!item) {
8093 err = IE_NOMEM;
8094 goto leave;
8096 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8097 if (i == 0) *permissions = item;
8098 else prev_item->next = item;
8099 prev_item = item;
8101 /* Extract it */
8102 xpath_ctx->node = result->nodesetval->nodeTab[i];
8103 err = extract_DbPDZRecord(context,
8104 (struct isds_commercial_permission **) (&item->data),
8105 xpath_ctx);
8106 if (err) goto leave;
8110 leave:
8111 if (err) {
8112 isds_list_free(permissions);
8115 xmlXPathFreeObject(result);
8116 xmlXPathFreeContext(xpath_ctx);
8117 xmlFreeDoc(response);
8119 #else /* not HAVE_LIBCURL */
8120 err = IE_NOTSUP;
8121 #endif
8123 return err;
8127 /* Get details about credit for sending pre-paid commercial messages.
8128 * @context is ISDS session context.
8129 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8130 * @from_date is first day of credit history to return in @history. Only
8131 * tm_year, tm_mon and tm_mday carry sane value.
8132 * @to_date is last day of credit history to return in @history. Only
8133 * tm_year, tm_mon and tm_mday carry sane value.
8134 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8135 * if you don't care. This and all other credit values are integers in
8136 * hundredths of Czech Crowns.
8137 * @email outputs notification e-mail address where notifications about credit
8138 * are sent. This is automatically reallocated string. Pass NULL if you don't
8139 * care. It can return NULL if no address is defined.
8140 * @history outputs auto-reallocated list of pointers to struct
8141 * isds_credit_event. Events in closed interval @from_time to @to_time are
8142 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8143 * are sorted by time.
8144 * @return:
8145 * IE_SUCCESS if the credit details have been obtained correctly,
8146 * or other appropriate error. Please note that server allows to retrieve
8147 * only limited history of events. */
8148 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8149 const char *box_id,
8150 const struct tm *from_date, const struct tm *to_date,
8151 long int *credit, char **email, struct isds_list **history) {
8152 isds_error err = IE_SUCCESS;
8153 #if HAVE_LIBCURL
8154 char *box_id_locale = NULL;
8155 xmlNodePtr request = NULL, node;
8156 xmlNsPtr isds_ns = NULL;
8157 xmlChar *string = NULL;
8159 xmlDocPtr response = NULL;
8160 xmlXPathContextPtr xpath_ctx = NULL;
8161 xmlXPathObjectPtr result = NULL;
8163 const xmlChar *codes[] = {
8164 BAD_CAST "1004",
8165 BAD_CAST "2011",
8166 BAD_CAST "1093",
8167 BAD_CAST "1137",
8168 BAD_CAST "1058",
8169 NULL
8171 const char *meanings[] = {
8172 "Insufficient priviledges for the box",
8173 "The box does not exist",
8174 "Date is too long (history is not available after 15 months)",
8175 "Interval is too long (limit is 3 months)",
8176 "Invalid date"
8178 const isds_error errors[] = {
8179 IE_ISDS,
8180 IE_NOEXIST,
8181 IE_DATE,
8182 IE_DATE,
8183 IE_DATE,
8185 struct code_map_isds_error map = {
8186 .codes = codes,
8187 .meanings = meanings,
8188 .errors = errors
8190 #endif
8192 if (!context) return IE_INVALID_CONTEXT;
8193 zfree(context->long_message);
8195 /* Free output argument */
8196 if (NULL != credit) *credit = 0;
8197 if (NULL != email) zfree(*email);
8198 isds_list_free(history);
8200 if (NULL == box_id) return IE_INVAL;
8202 #if HAVE_LIBCURL
8203 /* Check if connection is established */
8204 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8206 box_id_locale = _isds_utf82locale((char*)box_id);
8207 if (NULL == box_id_locale) {
8208 err = IE_NOMEM;
8209 goto leave;
8212 /* Build request */
8213 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8214 if (NULL == request) {
8215 isds_printf_message(context,
8216 _("Could not build DataBoxCreditInfo request for %s box"),
8217 box_id_locale);
8218 err = IE_ERROR;
8219 goto leave;
8221 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8222 if(!isds_ns) {
8223 isds_log_message(context, _("Could not create ISDS name space"));
8224 err = IE_ERROR;
8225 goto leave;
8227 xmlSetNs(request, isds_ns);
8229 /* Add mandatory XSD:tIdDbInput child */
8230 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8231 /* Add mandatory dates elements with optional values */
8232 if (from_date) {
8233 err = tm2datestring(from_date, &string);
8234 if (err) {
8235 isds_log_message(context,
8236 _("Could not convert `from_date' argument to ISO date "
8237 "string"));
8238 goto leave;
8240 INSERT_STRING(request, "ciFromDate", string);
8241 zfree(string);
8242 } else {
8243 INSERT_STRING(request, "ciFromDate", NULL);
8245 if (to_date) {
8246 err = tm2datestring(to_date, &string);
8247 if (err) {
8248 isds_log_message(context,
8249 _("Could not convert `to_date' argument to ISO date "
8250 "string"));
8251 goto leave;
8253 INSERT_STRING(request, "ciTodate", string);
8254 zfree(string);
8255 } else {
8256 INSERT_STRING(request, "ciTodate", NULL);
8259 /* Send request and check response*/
8260 err = send_destroy_request_check_response(context,
8261 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8262 &request, &response, NULL, &map);
8263 if (err) goto leave;
8266 /* Extract data */
8267 /* Set context to the root */
8268 xpath_ctx = xmlXPathNewContext(response);
8269 if (!xpath_ctx) {
8270 err = IE_ERROR;
8271 goto leave;
8273 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8274 err = IE_ERROR;
8275 goto leave;
8277 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8278 xpath_ctx);
8279 if (!result) {
8280 err = IE_ERROR;
8281 goto leave;
8283 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8284 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8285 err = IE_ISDS;
8286 goto leave;
8288 if (result->nodesetval->nodeNr > 1) {
8289 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8290 err = IE_ISDS;
8291 goto leave;
8293 xpath_ctx->node = result->nodesetval->nodeTab[0];
8294 xmlXPathFreeObject(result); result = NULL;
8296 /* Extract common data */
8297 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8298 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8300 /* Extract records */
8301 if (NULL == history) goto leave;
8302 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8303 xpath_ctx);
8304 if (!result) {
8305 err = IE_ERROR;
8306 goto leave;
8308 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8309 struct isds_list *prev_item = NULL;
8311 /* Iterate over all records */
8312 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8313 struct isds_list *item;
8315 /* Prepare structure */
8316 item = calloc(1, sizeof(*item));
8317 if (!item) {
8318 err = IE_NOMEM;
8319 goto leave;
8321 item->destructor = (void(*)(void**))isds_credit_event_free;
8322 if (i == 0) *history = item;
8323 else prev_item->next = item;
8324 prev_item = item;
8326 /* Extract it */
8327 xpath_ctx->node = result->nodesetval->nodeTab[i];
8328 err = extract_CiRecord(context,
8329 (struct isds_credit_event **) (&item->data),
8330 xpath_ctx);
8331 if (err) goto leave;
8335 leave:
8336 if (!err) {
8337 isds_log(ILF_ISDS, ILL_DEBUG,
8338 _("DataBoxCreditInfo request processed by server successfully.\n"));
8340 if (err) {
8341 isds_list_free(history);
8342 if (NULL != email) zfree(*email)
8345 free(box_id_locale);
8346 xmlXPathFreeObject(result);
8347 xmlXPathFreeContext(xpath_ctx);
8348 xmlFreeDoc(response);
8350 #else /* not HAVE_LIBCURL */
8351 err = IE_NOTSUP;
8352 #endif
8354 return err;
8358 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8359 * code, destroy response and log success.
8360 * @context is ISDS session context.
8361 * @service_name is name of SERVICE_DB_MANIPULATION service
8362 * @box_id is UTF-8 encoded box identifier as zero terminated string
8363 * @approval is optional external approval of box manipulation
8364 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8365 * NULL, if you don't care. */
8366 static isds_error build_send_manipulationdbid_request_check_drop_response(
8367 struct isds_ctx *context, const xmlChar *service_name,
8368 const xmlChar *box_id, const struct isds_approval *approval,
8369 xmlChar **refnumber) {
8370 isds_error err = IE_SUCCESS;
8371 #if HAVE_LIBCURL
8372 xmlDocPtr response = NULL;
8373 #endif
8375 if (!context) return IE_INVALID_CONTEXT;
8376 zfree(context->long_message);
8377 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8379 #if HAVE_LIBCURL
8380 /* Check if connection is established */
8381 if (!context->curl) return IE_CONNECTION_CLOSED;
8383 /* Do request and check for success */
8384 err = build_send_dbid_request_check_response(context,
8385 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8386 &response, refnumber);
8387 xmlFreeDoc(response);
8389 if (!err) {
8390 char *service_name_locale = _isds_utf82locale((char *) service_name);
8391 isds_log(ILF_ISDS, ILL_DEBUG,
8392 _("%s request processed by server successfully.\n"),
8393 service_name_locale);
8394 free(service_name_locale);
8396 #else /* not HAVE_LIBCURL */
8397 err = IE_NOTSUP;
8398 #endif
8400 return err;
8404 /* Switch box into state where box can receive commercial messages (off by
8405 * default)
8406 * @context is ISDS session context.
8407 * @box_id is UTF-8 encoded box identifier as zero terminated string
8408 * @allow is true for enable, false for disable commercial messages income
8409 * @approval is optional external approval of box manipulation
8410 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8411 * NULL, if you don't care. */
8412 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8413 const char *box_id, const _Bool allow,
8414 const struct isds_approval *approval, char **refnumber) {
8415 return build_send_manipulationdbid_request_check_drop_response(context,
8416 (allow) ? BAD_CAST "SetOpenAddressing" :
8417 BAD_CAST "ClearOpenAddressing",
8418 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8422 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8423 * message acceptance). This is just a box permission. Sender must apply
8424 * such role by sending each message.
8425 * @context is ISDS session context.
8426 * @box_id is UTF-8 encoded box identifier as zero terminated string
8427 * @allow is true for enable, false for disable OVM role permission
8428 * @approval is optional external approval of box manipulation
8429 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8430 * NULL, if you don't care. */
8431 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
8432 const char *box_id, const _Bool allow,
8433 const struct isds_approval *approval, char **refnumber) {
8434 return build_send_manipulationdbid_request_check_drop_response(context,
8435 (allow) ? BAD_CAST "SetEffectiveOVM" :
8436 BAD_CAST "ClearEffectiveOVM",
8437 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8441 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8442 * code, destroy response and log success.
8443 * @context is ISDS session context.
8444 * @service_name is name of SERVICE_DB_MANIPULATION service
8445 * @owner is structure describing box. aifoIsds, address->adCode,
8446 * address->adDistrict members are ignored.
8447 * @approval is optional external approval of box manipulation
8448 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8449 * NULL, if you don't care. */
8450 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8451 struct isds_ctx *context, const xmlChar *service_name,
8452 const struct isds_DbOwnerInfo *owner,
8453 const struct isds_approval *approval, xmlChar **refnumber) {
8454 isds_error err = IE_SUCCESS;
8455 #if HAVE_LIBCURL
8456 char *service_name_locale = NULL;
8457 xmlNodePtr request = NULL, db_owner_info;
8458 xmlNsPtr isds_ns = NULL;
8459 #endif
8462 if (!context) return IE_INVALID_CONTEXT;
8463 zfree(context->long_message);
8464 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8466 #if HAVE_LIBCURL
8467 service_name_locale = _isds_utf82locale((char*)service_name);
8468 if (!service_name_locale) {
8469 err = IE_NOMEM;
8470 goto leave;
8473 /* Build request */
8474 request = xmlNewNode(NULL, service_name);
8475 if (!request) {
8476 isds_printf_message(context,
8477 _("Could not build %s request"), service_name_locale);
8478 err = IE_ERROR;
8479 goto leave;
8481 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8482 if(!isds_ns) {
8483 isds_log_message(context, _("Could not create ISDS name space"));
8484 err = IE_ERROR;
8485 goto leave;
8487 xmlSetNs(request, isds_ns);
8490 /* Add XSD:tOwnerInfoInput child*/
8491 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8492 err = insert_DbOwnerInfo(context, owner, db_owner_info);
8493 if (err) goto leave;
8495 /* Add XSD:gExtApproval*/
8496 err = insert_GExtApproval(context, approval, request);
8497 if (err) goto leave;
8499 /* Send it to server and process response */
8500 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8501 service_name, &request, refnumber);
8503 leave:
8504 xmlFreeNode(request);
8505 free(service_name_locale);
8506 #else /* not HAVE_LIBCURL */
8507 err = IE_NOTSUP;
8508 #endif
8510 return err;
8514 /* Switch box accessibility state on request of box owner.
8515 * Despite the name, owner must do the request off-line. This function is
8516 * designed for such off-line meeting points (e.g. Czech POINT).
8517 * @context is ISDS session context.
8518 * @box identifies box to switch accessibility state. aifoIsds,
8519 * address->adCode, address->adDistrict members are ignored.
8520 * @allow is true for making accessible, false to disallow access.
8521 * @approval is optional external approval of box manipulation
8522 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8523 * NULL, if you don't care. */
8524 isds_error isds_switch_box_accessibility_on_owner_request(
8525 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8526 const _Bool allow, const struct isds_approval *approval,
8527 char **refnumber) {
8528 return build_send_manipulationdbowner_request_check_drop_response(context,
8529 (allow) ? BAD_CAST "EnableOwnDataBox" :
8530 BAD_CAST "DisableOwnDataBox",
8531 box, approval, (xmlChar **) refnumber);
8535 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8536 * date.
8537 * @context is ISDS session context.
8538 * @box identifies box to switch accessibility state. aifoIsds,
8539 * address->adCode, address->adDistrict members are ignored.
8540 * @since is date since accessibility has been denied. This can be past too.
8541 * Only tm_year, tm_mon and tm_mday carry sane value.
8542 * @approval is optional external approval of box manipulation
8543 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8544 * NULL, if you don't care. */
8545 isds_error isds_disable_box_accessibility_externaly(
8546 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8547 const struct tm *since, const struct isds_approval *approval,
8548 char **refnumber) {
8549 isds_error err = IE_SUCCESS;
8550 #if HAVE_LIBCURL
8551 char *service_name_locale = NULL;
8552 xmlNodePtr request = NULL, node;
8553 xmlNsPtr isds_ns = NULL;
8554 xmlChar *string = NULL;
8555 #endif
8558 if (!context) return IE_INVALID_CONTEXT;
8559 zfree(context->long_message);
8560 if (!box || !since) return IE_INVAL;
8562 #if HAVE_LIBCURL
8563 /* Build request */
8564 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
8565 if (!request) {
8566 isds_printf_message(context,
8567 _("Could not build %s request"), "DisableDataBoxExternally");
8568 err = IE_ERROR;
8569 goto leave;
8571 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8572 if(!isds_ns) {
8573 isds_log_message(context, _("Could not create ISDS name space"));
8574 err = IE_ERROR;
8575 goto leave;
8577 xmlSetNs(request, isds_ns);
8580 /* Add @box identification */
8581 INSERT_ELEMENT(node, request, "dbOwnerInfo");
8582 err = insert_DbOwnerInfo(context, box, node);
8583 if (err) goto leave;
8585 /* Add @since date */
8586 err = tm2datestring(since, &string);
8587 if(err) {
8588 isds_log_message(context,
8589 _("Could not convert `since' argument to ISO date string"));
8590 goto leave;
8592 INSERT_STRING(request, "dbOwnerDisableDate", string);
8593 zfree(string);
8595 /* Add @approval */
8596 err = insert_GExtApproval(context, approval, request);
8597 if (err) goto leave;
8599 /* Send it to server and process response */
8600 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8601 BAD_CAST "DisableDataBoxExternally", &request,
8602 (xmlChar **) refnumber);
8604 leave:
8605 free(string);
8606 xmlFreeNode(request);
8607 free(service_name_locale);
8608 #else /* not HAVE_LIBCURL */
8609 err = IE_NOTSUP;
8610 #endif
8612 return err;
8616 #if HAVE_LIBCURL
8617 /* Insert struct isds_message data (envelope (recipient data optional) and
8618 * documents into XML tree
8619 * @context is session context
8620 * @outgoing_message is libisds structure with message data
8621 * @create_message is XML CreateMessage or CreateMultipleMessage element
8622 * @process_recipient true for recipient data serialization, false for no
8623 * serialization */
8624 static isds_error insert_envelope_files(struct isds_ctx *context,
8625 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8626 const _Bool process_recipient) {
8628 isds_error err = IE_SUCCESS;
8629 xmlNodePtr envelope, dm_files, node;
8630 xmlChar *string = NULL;
8632 if (!context) return IE_INVALID_CONTEXT;
8633 if (!outgoing_message || !create_message) return IE_INVAL;
8636 /* Build envelope */
8637 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8638 if (!envelope) {
8639 isds_printf_message(context, _("Could not add dmEnvelope child to "
8640 "%s element"), create_message->name);
8641 return IE_ERROR;
8644 if (!outgoing_message->envelope) {
8645 isds_log_message(context, _("Outgoing message is missing envelope"));
8646 err = IE_INVAL;
8647 goto leave;
8650 /* Insert optional message type */
8651 err = insert_message_type(context, outgoing_message->envelope->dmType,
8652 envelope);
8653 if (err) goto leave;
8655 INSERT_STRING(envelope, "dmSenderOrgUnit",
8656 outgoing_message->envelope->dmSenderOrgUnit);
8657 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8658 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8660 if (process_recipient) {
8661 if (!outgoing_message->envelope->dbIDRecipient) {
8662 isds_log_message(context,
8663 _("Outgoing message is missing recipient box identifier"));
8664 err = IE_INVAL;
8665 goto leave;
8667 INSERT_STRING(envelope, "dbIDRecipient",
8668 outgoing_message->envelope->dbIDRecipient);
8670 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8671 outgoing_message->envelope->dmRecipientOrgUnit);
8672 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8673 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8674 INSERT_STRING(envelope, "dmToHands",
8675 outgoing_message->envelope->dmToHands);
8678 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8679 "dmAnnotation");
8680 INSERT_STRING(envelope, "dmAnnotation",
8681 outgoing_message->envelope->dmAnnotation);
8683 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8684 0, 50, "dmRecipientRefNumber");
8685 INSERT_STRING(envelope, "dmRecipientRefNumber",
8686 outgoing_message->envelope->dmRecipientRefNumber);
8688 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8689 0, 50, "dmSenderRefNumber");
8690 INSERT_STRING(envelope, "dmSenderRefNumber",
8691 outgoing_message->envelope->dmSenderRefNumber);
8693 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8694 0, 50, "dmRecipientIdent");
8695 INSERT_STRING(envelope, "dmRecipientIdent",
8696 outgoing_message->envelope->dmRecipientIdent);
8698 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8699 0, 50, "dmSenderIdent");
8700 INSERT_STRING(envelope, "dmSenderIdent",
8701 outgoing_message->envelope->dmSenderIdent);
8703 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8704 outgoing_message->envelope->dmLegalTitleLaw, string);
8705 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8706 outgoing_message->envelope->dmLegalTitleYear, string);
8707 INSERT_STRING(envelope, "dmLegalTitleSect",
8708 outgoing_message->envelope->dmLegalTitleSect);
8709 INSERT_STRING(envelope, "dmLegalTitlePar",
8710 outgoing_message->envelope->dmLegalTitlePar);
8711 INSERT_STRING(envelope, "dmLegalTitlePoint",
8712 outgoing_message->envelope->dmLegalTitlePoint);
8714 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8715 outgoing_message->envelope->dmPersonalDelivery);
8716 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8717 outgoing_message->envelope->dmAllowSubstDelivery);
8719 /* ???: Should we require value for dbEffectiveOVM sender?
8720 * ISDS has default as true */
8721 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8722 INSERT_BOOLEAN(envelope, "dmOVM",
8723 outgoing_message->envelope->dmPublishOwnID);
8726 /* Append dmFiles */
8727 if (!outgoing_message->documents) {
8728 isds_log_message(context,
8729 _("Outgoing message is missing list of documents"));
8730 err = IE_INVAL;
8731 goto leave;
8733 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8734 if (!dm_files) {
8735 isds_printf_message(context, _("Could not add dmFiles child to "
8736 "%s element"), create_message->name);
8737 err = IE_ERROR;
8738 goto leave;
8741 /* Check for document hierarchy */
8742 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8743 if (err) goto leave;
8745 /* Process each document */
8746 for (struct isds_list *item =
8747 (struct isds_list *) outgoing_message->documents;
8748 item; item = item->next) {
8749 if (!item->data) {
8750 isds_log_message(context,
8751 _("List of documents contains empty item"));
8752 err = IE_INVAL;
8753 goto leave;
8755 /* FIXME: Check for dmFileMetaType and for document references.
8756 * Only first document can be of MAIN type */
8757 err = insert_document(context, (struct isds_document*) item->data,
8758 dm_files);
8760 if (err) goto leave;
8763 leave:
8764 free(string);
8765 return err;
8767 #endif /* HAVE_LIBCURL */
8770 /* Send a message via ISDS to a recipient
8771 * @context is session context
8772 * @outgoing_message is message to send; Some members are mandatory (like
8773 * dbIDRecipient), some are optional and some are irrelevant (especially data
8774 * about sender). Included pointer to isds_list documents must contain at
8775 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8776 * members will be filled with valid data from ISDS. Exact list of write
8777 * members is subject to change. Currently dmID is changed.
8778 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8779 isds_error isds_send_message(struct isds_ctx *context,
8780 struct isds_message *outgoing_message) {
8782 isds_error err = IE_SUCCESS;
8783 #if HAVE_LIBCURL
8784 xmlNsPtr isds_ns = NULL;
8785 xmlNodePtr request = NULL;
8786 xmlDocPtr response = NULL;
8787 xmlChar *code = NULL, *message = NULL;
8788 xmlXPathContextPtr xpath_ctx = NULL;
8789 xmlXPathObjectPtr result = NULL;
8790 /*_Bool message_is_complete = 0;*/
8791 #endif
8793 if (!context) return IE_INVALID_CONTEXT;
8794 zfree(context->long_message);
8795 if (!outgoing_message) return IE_INVAL;
8797 #if HAVE_LIBCURL
8798 /* Check if connection is established
8799 * TODO: This check should be done downstairs. */
8800 if (!context->curl) return IE_CONNECTION_CLOSED;
8803 /* Build CreateMessage request */
8804 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8805 if (!request) {
8806 isds_log_message(context,
8807 _("Could not build CreateMessage request"));
8808 return IE_ERROR;
8810 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8811 if(!isds_ns) {
8812 isds_log_message(context, _("Could not create ISDS name space"));
8813 xmlFreeNode(request);
8814 return IE_ERROR;
8816 xmlSetNs(request, isds_ns);
8818 /* Append envelope and files */
8819 err = insert_envelope_files(context, outgoing_message, request, 1);
8820 if (err) goto leave;
8823 /* Signal we can serialize message since now */
8824 /*message_is_complete = 1;*/
8827 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8829 /* Sent request */
8830 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8832 /* Don't' destroy request, we want to provide it to application later */
8834 if (err) {
8835 isds_log(ILF_ISDS, ILL_DEBUG,
8836 _("Processing ISDS response on CreateMessage "
8837 "request failed\n"));
8838 goto leave;
8841 /* Check for response status */
8842 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8843 &code, &message, NULL);
8844 if (err) {
8845 isds_log(ILF_ISDS, ILL_DEBUG,
8846 _("ISDS response on CreateMessage request "
8847 "is missing status\n"));
8848 goto leave;
8851 /* Request processed, but refused by server or server failed */
8852 if (xmlStrcmp(code, BAD_CAST "0000")) {
8853 char *box_id_locale =
8854 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8855 char *code_locale = _isds_utf82locale((char*)code);
8856 char *message_locale = _isds_utf82locale((char*)message);
8857 isds_log(ILF_ISDS, ILL_DEBUG,
8858 _("Server did not accept message for %s on CreateMessage "
8859 "request (code=%s, message=%s)\n"),
8860 box_id_locale, code_locale, message_locale);
8861 isds_log_message(context, message_locale);
8862 free(box_id_locale);
8863 free(code_locale);
8864 free(message_locale);
8865 err = IE_ISDS;
8866 goto leave;
8870 /* Extract data */
8871 xpath_ctx = xmlXPathNewContext(response);
8872 if (!xpath_ctx) {
8873 err = IE_ERROR;
8874 goto leave;
8876 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8877 err = IE_ERROR;
8878 goto leave;
8880 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8881 xpath_ctx);
8882 if (!result) {
8883 err = IE_ERROR;
8884 goto leave;
8886 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8887 isds_log_message(context, _("Missing CreateMessageResponse element"));
8888 err = IE_ISDS;
8889 goto leave;
8891 if (result->nodesetval->nodeNr > 1) {
8892 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8893 err = IE_ISDS;
8894 goto leave;
8896 xpath_ctx->node = result->nodesetval->nodeTab[0];
8897 xmlXPathFreeObject(result); result = NULL;
8899 if (outgoing_message->envelope->dmID) {
8900 free(outgoing_message->envelope->dmID);
8901 outgoing_message->envelope->dmID = NULL;
8903 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8904 if (!outgoing_message->envelope->dmID) {
8905 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8906 "but did not return assigned message ID\n"));
8909 leave:
8910 /* TODO: Serialize message into structure member raw */
8911 /* XXX: Each web service transport message in different format.
8912 * Therefore it's not possible to save them directly.
8913 * To save them, one must figure out common format.
8914 * We can leave it on application, or we can implement the ESS format. */
8915 /*if (message_is_complete) {
8916 if (outgoing_message->envelope->dmID) {
8918 /* Add assigned message ID as first child*/
8919 /*xmlNodePtr dmid_text = xmlNewText(
8920 (xmlChar *) outgoing_message->envelope->dmID);
8921 if (!dmid_text) goto serialization_failed;
8923 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8924 BAD_CAST "dmID");
8925 if (!dmid_element) {
8926 xmlFreeNode(dmid_text);
8927 goto serialization_failed;
8930 xmlNodePtr dmid_element_with_text =
8931 xmlAddChild(dmid_element, dmid_text);
8932 if (!dmid_element_with_text) {
8933 xmlFreeNode(dmid_element);
8934 xmlFreeNode(dmid_text);
8935 goto serialization_failed;
8938 node = xmlAddPrevSibling(envelope->childern,
8939 dmid_element_with_text);
8940 if (!node) {
8941 xmlFreeNodeList(dmid_element_with_text);
8942 goto serialization_failed;
8946 /* Serialize message with ID into raw */
8947 /*buffer = serialize_element(envelope)*/
8948 /* }
8950 serialization_failed:
8954 /* Clean up */
8955 xmlXPathFreeObject(result);
8956 xmlXPathFreeContext(xpath_ctx);
8958 free(code);
8959 free(message);
8960 xmlFreeDoc(response);
8961 xmlFreeNode(request);
8963 if (!err)
8964 isds_log(ILF_ISDS, ILL_DEBUG,
8965 _("CreateMessage request processed by server "
8966 "successfully.\n"));
8967 #else /* not HAVE_LIBCURL */
8968 err = IE_NOTSUP;
8969 #endif
8971 return err;
8975 /* Send a message via ISDS to a multiple recipients
8976 * @context is session context
8977 * @outgoing_message is message to send; Some members are mandatory,
8978 * some are optional and some are irrelevant (especially data
8979 * about sender). Data about recipient will be substituted by ISDS from
8980 * @copies. Included pointer to isds_list documents must
8981 * contain at least one document of FILEMETATYPE_MAIN.
8982 * @copies is list of isds_message_copy structures addressing all desired
8983 * recipients. This is read-write structure, some members will be filled with
8984 * valid data from ISDS (message IDs, error codes, error descriptions).
8985 * @return
8986 * ISDS_SUCCESS if all messages have been sent
8987 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8988 * succeeded messages can be identified by copies->data->error),
8989 * or other error code if something other goes wrong. */
8990 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8991 const struct isds_message *outgoing_message,
8992 struct isds_list *copies) {
8994 isds_error err = IE_SUCCESS;
8995 #if HAVE_LIBCURL
8996 isds_error append_err;
8997 xmlNsPtr isds_ns = NULL;
8998 xmlNodePtr request = NULL, recipients, recipient, node;
8999 struct isds_list *item;
9000 struct isds_message_copy *copy;
9001 xmlDocPtr response = NULL;
9002 xmlChar *code = NULL, *message = NULL;
9003 xmlXPathContextPtr xpath_ctx = NULL;
9004 xmlXPathObjectPtr result = NULL;
9005 xmlChar *string = NULL;
9006 int i;
9007 #endif
9009 if (!context) return IE_INVALID_CONTEXT;
9010 zfree(context->long_message);
9011 if (!outgoing_message || !copies) return IE_INVAL;
9013 #if HAVE_LIBCURL
9014 /* Check if connection is established
9015 * TODO: This check should be done downstairs. */
9016 if (!context->curl) return IE_CONNECTION_CLOSED;
9019 /* Build CreateMultipleMessage request */
9020 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
9021 if (!request) {
9022 isds_log_message(context,
9023 _("Could not build CreateMultipleMessage request"));
9024 return IE_ERROR;
9026 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9027 if(!isds_ns) {
9028 isds_log_message(context, _("Could not create ISDS name space"));
9029 xmlFreeNode(request);
9030 return IE_ERROR;
9032 xmlSetNs(request, isds_ns);
9035 /* Build recipients */
9036 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
9037 if (!recipients) {
9038 isds_log_message(context, _("Could not add dmRecipients child to "
9039 "CreateMultipleMessage element"));
9040 xmlFreeNode(request);
9041 return IE_ERROR;
9044 /* Insert each recipient */
9045 for (item = copies; item; item = item->next) {
9046 copy = (struct isds_message_copy *) item->data;
9047 if (!copy) {
9048 isds_log_message(context,
9049 _("`copies' list item contains empty data"));
9050 err = IE_INVAL;
9051 goto leave;
9054 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
9055 if (!recipient) {
9056 isds_log_message(context, _("Could not add dmRecipient child to "
9057 "dmRecipients element"));
9058 err = IE_ERROR;
9059 goto leave;
9062 if (!copy->dbIDRecipient) {
9063 isds_log_message(context,
9064 _("Message copy is missing recipient box identifier"));
9065 err = IE_INVAL;
9066 goto leave;
9068 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
9069 INSERT_STRING(recipient, "dmRecipientOrgUnit",
9070 copy->dmRecipientOrgUnit);
9071 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
9072 copy->dmRecipientOrgUnitNum, string);
9073 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
9076 /* Append envelope and files */
9077 err = insert_envelope_files(context, outgoing_message, request, 0);
9078 if (err) goto leave;
9081 isds_log(ILF_ISDS, ILL_DEBUG,
9082 _("Sending CreateMultipleMessage request to ISDS\n"));
9084 /* Sent request */
9085 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9086 if (err) {
9087 isds_log(ILF_ISDS, ILL_DEBUG,
9088 _("Processing ISDS response on CreateMultipleMessage "
9089 "request failed\n"));
9090 goto leave;
9093 /* Check for response status */
9094 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9095 &code, &message, NULL);
9096 if (err) {
9097 isds_log(ILF_ISDS, ILL_DEBUG,
9098 _("ISDS response on CreateMultipleMessage request "
9099 "is missing status\n"));
9100 goto leave;
9103 /* Request processed, but some copies failed */
9104 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9105 char *box_id_locale =
9106 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9107 char *code_locale = _isds_utf82locale((char*)code);
9108 char *message_locale = _isds_utf82locale((char*)message);
9109 isds_log(ILF_ISDS, ILL_DEBUG,
9110 _("Server did accept message for multiple recipients "
9111 "on CreateMultipleMessage request but delivery to "
9112 "some of them failed (code=%s, message=%s)\n"),
9113 box_id_locale, code_locale, message_locale);
9114 isds_log_message(context, message_locale);
9115 free(box_id_locale);
9116 free(code_locale);
9117 free(message_locale);
9118 err = IE_PARTIAL_SUCCESS;
9121 /* Request refused by server as whole */
9122 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9123 char *box_id_locale =
9124 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9125 char *code_locale = _isds_utf82locale((char*)code);
9126 char *message_locale = _isds_utf82locale((char*)message);
9127 isds_log(ILF_ISDS, ILL_DEBUG,
9128 _("Server did not accept message for multiple recipients "
9129 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9130 box_id_locale, code_locale, message_locale);
9131 isds_log_message(context, message_locale);
9132 free(box_id_locale);
9133 free(code_locale);
9134 free(message_locale);
9135 err = IE_ISDS;
9136 goto leave;
9140 /* Extract data */
9141 xpath_ctx = xmlXPathNewContext(response);
9142 if (!xpath_ctx) {
9143 err = IE_ERROR;
9144 goto leave;
9146 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9147 err = IE_ERROR;
9148 goto leave;
9150 result = xmlXPathEvalExpression(
9151 BAD_CAST "/isds:CreateMultipleMessageResponse"
9152 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9153 xpath_ctx);
9154 if (!result) {
9155 err = IE_ERROR;
9156 goto leave;
9158 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9159 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9160 err = IE_ISDS;
9161 goto leave;
9164 /* Extract message ID and delivery status for each copy */
9165 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9166 item = item->next, i++) {
9167 copy = (struct isds_message_copy *) item->data;
9168 xpath_ctx->node = result->nodesetval->nodeTab[i];
9170 append_err = append_TMStatus(context, copy, xpath_ctx);
9171 if (append_err) {
9172 err = append_err;
9173 goto leave;
9176 if (item || i < result->nodesetval->nodeNr) {
9177 isds_printf_message(context, _("ISDS returned unexpected number of "
9178 "message copy delivery states: %d"),
9179 result->nodesetval->nodeNr);
9180 err = IE_ISDS;
9181 goto leave;
9185 leave:
9186 /* Clean up */
9187 free(string);
9188 xmlXPathFreeObject(result);
9189 xmlXPathFreeContext(xpath_ctx);
9191 free(code);
9192 free(message);
9193 xmlFreeDoc(response);
9194 xmlFreeNode(request);
9196 if (!err)
9197 isds_log(ILF_ISDS, ILL_DEBUG,
9198 _("CreateMultipleMessageResponse request processed by server "
9199 "successfully.\n"));
9200 #else /* not HAVE_LIBCURL */
9201 err = IE_NOTSUP;
9202 #endif
9204 return err;
9208 /* Get list of messages. This is common core for getting sent or received
9209 * messages.
9210 * Any criterion argument can be NULL, if you don't care about it.
9211 * @context is session context. Must not be NULL.
9212 * @outgoing_direction is true if you want list of outgoing messages,
9213 * it's false if you want incoming messages.
9214 * @from_time is minimal time and date of message sending inclusive.
9215 * @to_time is maximal time and date of message sending inclusive
9216 * @organization_unit_number is number of sender/recipient respectively.
9217 * @status_filter is bit field of isds_message_status values. Use special
9218 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9219 * all values, you can use bit-wise arithmetic if you want.)
9220 * @offset is index of first message we are interested in. First message is 1.
9221 * Set to 0 (or 1) if you don't care.
9222 * @number is maximal length of list you want to get as input value, outputs
9223 * number of messages matching these criteria. Can be NULL if you don't care
9224 * (applies to output value either).
9225 * @messages is automatically reallocated list of isds_message's. Be ware that
9226 * it returns only brief overview (envelope and some other fields) about each
9227 * message, not the complete message. FIXME: Specify exact fields.
9228 * The list is sorted by delivery time in ascending order.
9229 * Use NULL if you don't care about don't need the data (useful if you want to
9230 * know only the @number). If you provide &NULL, list will be allocated on
9231 * heap, if you provide pointer to non-NULL, list will be freed automatically
9232 * at first. Also in case of error the list will be NULLed.
9233 * @return IE_SUCCESS or appropriate error code. */
9234 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9235 _Bool outgoing_direction,
9236 const struct timeval *from_time, const struct timeval *to_time,
9237 const long int *organization_unit_number,
9238 const unsigned int status_filter,
9239 const unsigned long int offset, unsigned long int *number,
9240 struct isds_list **messages) {
9242 isds_error err = IE_SUCCESS;
9243 #if HAVE_LIBCURL
9244 xmlNsPtr isds_ns = NULL;
9245 xmlNodePtr request = NULL, node;
9246 xmlDocPtr response = NULL;
9247 xmlChar *code = NULL, *message = NULL;
9248 xmlXPathContextPtr xpath_ctx = NULL;
9249 xmlXPathObjectPtr result = NULL;
9250 xmlChar *string = NULL;
9251 int count = 0;
9252 #endif
9254 if (!context) return IE_INVALID_CONTEXT;
9255 zfree(context->long_message);
9257 /* Free former message list if any */
9258 if (messages) isds_list_free(messages);
9260 #if HAVE_LIBCURL
9261 /* Check if connection is established
9262 * TODO: This check should be done downstairs. */
9263 if (!context->curl) return IE_CONNECTION_CLOSED;
9265 /* Build GetListOf*Messages request */
9266 request = xmlNewNode(NULL,
9267 (outgoing_direction) ?
9268 BAD_CAST "GetListOfSentMessages" :
9269 BAD_CAST "GetListOfReceivedMessages"
9271 if (!request) {
9272 isds_log_message(context,
9273 (outgoing_direction) ?
9274 _("Could not build GetListOfSentMessages request") :
9275 _("Could not build GetListOfReceivedMessages request")
9277 return IE_ERROR;
9279 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9280 if(!isds_ns) {
9281 isds_log_message(context, _("Could not create ISDS name space"));
9282 xmlFreeNode(request);
9283 return IE_ERROR;
9285 xmlSetNs(request, isds_ns);
9288 if (from_time) {
9289 err = timeval2timestring(from_time, &string);
9290 if (err) goto leave;
9292 INSERT_STRING(request, "dmFromTime", string);
9293 free(string); string = NULL;
9295 if (to_time) {
9296 err = timeval2timestring(to_time, &string);
9297 if (err) goto leave;
9299 INSERT_STRING(request, "dmToTime", string);
9300 free(string); string = NULL;
9302 if (outgoing_direction) {
9303 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9304 organization_unit_number, string);
9305 } else {
9306 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9307 organization_unit_number, string);
9310 if (status_filter > MESSAGESTATE_ANY) {
9311 isds_printf_message(context,
9312 _("Invalid message state filter value: %ld"), status_filter);
9313 err = IE_INVAL;
9314 goto leave;
9316 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9318 if (offset > 0 ) {
9319 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9320 } else {
9321 INSERT_STRING(request, "dmOffset", "1");
9324 /* number 0 means no limit */
9325 if (number && *number == 0) {
9326 INSERT_STRING(request, "dmLimit", NULL);
9327 } else {
9328 INSERT_ULONGINT(request, "dmLimit", number, string);
9332 isds_log(ILF_ISDS, ILL_DEBUG,
9333 (outgoing_direction) ?
9334 _("Sending GetListOfSentMessages request to ISDS\n") :
9335 _("Sending GetListOfReceivedMessages request to ISDS\n")
9338 /* Sent request */
9339 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9340 xmlFreeNode(request); request = NULL;
9342 if (err) {
9343 isds_log(ILF_ISDS, ILL_DEBUG,
9344 (outgoing_direction) ?
9345 _("Processing ISDS response on GetListOfSentMessages "
9346 "request failed\n") :
9347 _("Processing ISDS response on GetListOfReceivedMessages "
9348 "request failed\n")
9350 goto leave;
9353 /* Check for response status */
9354 err = isds_response_status(context, SERVICE_DM_INFO, response,
9355 &code, &message, NULL);
9356 if (err) {
9357 isds_log(ILF_ISDS, ILL_DEBUG,
9358 (outgoing_direction) ?
9359 _("ISDS response on GetListOfSentMessages request "
9360 "is missing status\n") :
9361 _("ISDS response on GetListOfReceivedMessages request "
9362 "is missing status\n")
9364 goto leave;
9367 /* Request processed, but nothing found */
9368 if (xmlStrcmp(code, BAD_CAST "0000")) {
9369 char *code_locale = _isds_utf82locale((char*)code);
9370 char *message_locale = _isds_utf82locale((char*)message);
9371 isds_log(ILF_ISDS, ILL_DEBUG,
9372 (outgoing_direction) ?
9373 _("Server refused GetListOfSentMessages request "
9374 "(code=%s, message=%s)\n") :
9375 _("Server refused GetListOfReceivedMessages request "
9376 "(code=%s, message=%s)\n"),
9377 code_locale, message_locale);
9378 isds_log_message(context, message_locale);
9379 free(code_locale);
9380 free(message_locale);
9381 err = IE_ISDS;
9382 goto leave;
9386 /* Extract data */
9387 xpath_ctx = xmlXPathNewContext(response);
9388 if (!xpath_ctx) {
9389 err = IE_ERROR;
9390 goto leave;
9392 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9393 err = IE_ERROR;
9394 goto leave;
9396 result = xmlXPathEvalExpression(
9397 (outgoing_direction) ?
9398 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9399 "isds:dmRecords/isds:dmRecord" :
9400 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9401 "isds:dmRecords/isds:dmRecord",
9402 xpath_ctx);
9403 if (!result) {
9404 err = IE_ERROR;
9405 goto leave;
9408 /* Fill output arguments in */
9409 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9410 struct isds_envelope *envelope;
9411 struct isds_list *item = NULL, *last_item = NULL;
9413 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9414 /* Create new message */
9415 item = calloc(1, sizeof(*item));
9416 if (!item) {
9417 err = IE_NOMEM;
9418 goto leave;
9420 item->destructor = (void(*)(void**)) &isds_message_free;
9421 item->data = calloc(1, sizeof(struct isds_message));
9422 if (!item->data) {
9423 isds_list_free(&item);
9424 err = IE_NOMEM;
9425 goto leave;
9428 /* Extract envelope data */
9429 xpath_ctx->node = result->nodesetval->nodeTab[count];
9430 envelope = NULL;
9431 err = extract_DmRecord(context, &envelope, xpath_ctx);
9432 if (err) {
9433 isds_list_free(&item);
9434 goto leave;
9437 /* Attach extracted envelope */
9438 ((struct isds_message *) item->data)->envelope = envelope;
9440 /* Append new message into the list */
9441 if (!*messages) {
9442 *messages = last_item = item;
9443 } else {
9444 last_item->next = item;
9445 last_item = item;
9449 if (number) *number = count;
9451 leave:
9452 if (err) {
9453 isds_list_free(messages);
9456 free(string);
9457 xmlXPathFreeObject(result);
9458 xmlXPathFreeContext(xpath_ctx);
9460 free(code);
9461 free(message);
9462 xmlFreeDoc(response);
9463 xmlFreeNode(request);
9465 if (!err)
9466 isds_log(ILF_ISDS, ILL_DEBUG,
9467 (outgoing_direction) ?
9468 _("GetListOfSentMessages request processed by server "
9469 "successfully.\n") :
9470 _("GetListOfReceivedMessages request processed by server "
9471 "successfully.\n")
9473 #else /* not HAVE_LIBCURL */
9474 err = IE_NOTSUP;
9475 #endif
9476 return err;
9480 /* Get list of outgoing (already sent) messages.
9481 * Any criterion argument can be NULL, if you don't care about it.
9482 * @context is session context. Must not be NULL.
9483 * @from_time is minimal time and date of message sending inclusive.
9484 * @to_time is maximal time and date of message sending inclusive
9485 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9486 * @status_filter is bit field of isds_message_status values. Use special
9487 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9488 * all values, you can use bit-wise arithmetic if you want.)
9489 * @offset is index of first message we are interested in. First message is 1.
9490 * Set to 0 (or 1) if you don't care.
9491 * @number is maximal length of list you want to get as input value, outputs
9492 * number of messages matching these criteria. Can be NULL if you don't care
9493 * (applies to output value either).
9494 * @messages is automatically reallocated list of isds_message's. Be ware that
9495 * it returns only brief overview (envelope and some other fields) about each
9496 * message, not the complete message. FIXME: Specify exact fields.
9497 * The list is sorted by delivery time in ascending order.
9498 * Use NULL if you don't care about the meta data (useful if you want to know
9499 * only the @number). If you provide &NULL, list will be allocated on heap,
9500 * if you provide pointer to non-NULL, list will be freed automatically at
9501 * first. Also in case of error the list will be NULLed.
9502 * @return IE_SUCCESS or appropriate error code. */
9503 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9504 const struct timeval *from_time, const struct timeval *to_time,
9505 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9506 const unsigned long int offset, unsigned long int *number,
9507 struct isds_list **messages) {
9509 return isds_get_list_of_messages(
9510 context, 1,
9511 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9512 offset, number,
9513 messages);
9517 /* Get list of incoming (addressed to you) messages.
9518 * Any criterion argument can be NULL, if you don't care about it.
9519 * @context is session context. Must not be NULL.
9520 * @from_time is minimal time and date of message sending inclusive.
9521 * @to_time is maximal time and date of message sending inclusive
9522 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9523 * @status_filter is bit field of isds_message_status values. Use special
9524 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9525 * all values, you can use bit-wise arithmetic if you want.)
9526 * @offset is index of first message we are interested in. First message is 1.
9527 * Set to 0 (or 1) if you don't care.
9528 * @number is maximal length of list you want to get as input value, outputs
9529 * number of messages matching these criteria. Can be NULL if you don't care
9530 * (applies to output value either).
9531 * @messages is automatically reallocated list of isds_message's. Be ware that
9532 * it returns only brief overview (envelope and some other fields) about each
9533 * message, not the complete message. FIXME: Specify exact fields.
9534 * Use NULL if you don't care about the meta data (useful if you want to know
9535 * only the @number). If you provide &NULL, list will be allocated on heap,
9536 * if you provide pointer to non-NULL, list will be freed automatically at
9537 * first. Also in case of error the list will be NULLed.
9538 * @return IE_SUCCESS or appropriate error code. */
9539 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9540 const struct timeval *from_time, const struct timeval *to_time,
9541 const long int *dmRecipientOrgUnitNum,
9542 const unsigned int status_filter,
9543 const unsigned long int offset, unsigned long int *number,
9544 struct isds_list **messages) {
9546 return isds_get_list_of_messages(
9547 context, 0,
9548 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9549 offset, number,
9550 messages);
9554 /* Get list of sent message state changes.
9555 * Any criterion argument can be NULL, if you don't care about it.
9556 * @context is session context. Must not be NULL.
9557 * @from_time is minimal time and date of status changes inclusive
9558 * @to_time is maximal time and date of status changes inclusive
9559 * @changed_states is automatically reallocated list of
9560 * isds_message_status_change's. If you provide &NULL, list will be allocated
9561 * on heap, if you provide pointer to non-NULL, list will be freed
9562 * automatically at first. Also in case of error the list will be NULLed.
9563 * XXX: The list item ordering is not specified.
9564 * XXX: Server provides only `recent' changes.
9565 * @return IE_SUCCESS or appropriate error code. */
9566 isds_error isds_get_list_of_sent_message_state_changes(
9567 struct isds_ctx *context,
9568 const struct timeval *from_time, const struct timeval *to_time,
9569 struct isds_list **changed_states) {
9571 isds_error err = IE_SUCCESS;
9572 #if HAVE_LIBCURL
9573 xmlNsPtr isds_ns = NULL;
9574 xmlNodePtr request = NULL, node;
9575 xmlDocPtr response = NULL;
9576 xmlXPathContextPtr xpath_ctx = NULL;
9577 xmlXPathObjectPtr result = NULL;
9578 xmlChar *string = NULL;
9579 int count = 0;
9580 #endif
9582 if (!context) return IE_INVALID_CONTEXT;
9583 zfree(context->long_message);
9585 /* Free former message list if any */
9586 isds_list_free(changed_states);
9588 #if HAVE_LIBCURL
9589 /* Check if connection is established
9590 * TODO: This check should be done downstairs. */
9591 if (!context->curl) return IE_CONNECTION_CLOSED;
9593 /* Build GetMessageStateChanges request */
9594 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
9595 if (!request) {
9596 isds_log_message(context,
9597 _("Could not build GetMessageStateChanges request"));
9598 return IE_ERROR;
9600 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9601 if(!isds_ns) {
9602 isds_log_message(context, _("Could not create ISDS name space"));
9603 xmlFreeNode(request);
9604 return IE_ERROR;
9606 xmlSetNs(request, isds_ns);
9609 if (from_time) {
9610 err = timeval2timestring(from_time, &string);
9611 if (err) goto leave;
9613 INSERT_STRING(request, "dmFromTime", string);
9614 zfree(string);
9616 if (to_time) {
9617 err = timeval2timestring(to_time, &string);
9618 if (err) goto leave;
9620 INSERT_STRING(request, "dmToTime", string);
9621 zfree(string);
9624 /* Sent request */
9625 err = send_destroy_request_check_response(context,
9626 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9627 &response, NULL, NULL);
9628 if (err) goto leave;
9631 /* Extract data */
9632 xpath_ctx = xmlXPathNewContext(response);
9633 if (!xpath_ctx) {
9634 err = IE_ERROR;
9635 goto leave;
9637 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9638 err = IE_ERROR;
9639 goto leave;
9641 result = xmlXPathEvalExpression(
9642 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9643 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9644 if (!result) {
9645 err = IE_ERROR;
9646 goto leave;
9649 /* Fill output arguments in */
9650 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9651 struct isds_list *item = NULL, *last_item = NULL;
9653 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9654 /* Create new status change */
9655 item = calloc(1, sizeof(*item));
9656 if (!item) {
9657 err = IE_NOMEM;
9658 goto leave;
9660 item->destructor =
9661 (void(*)(void**)) &isds_message_status_change_free;
9663 /* Extract message status change */
9664 xpath_ctx->node = result->nodesetval->nodeTab[count];
9665 err = extract_StateChangesRecord(context,
9666 (struct isds_message_status_change **) &item->data,
9667 xpath_ctx);
9668 if (err) {
9669 isds_list_free(&item);
9670 goto leave;
9673 /* Append new message status change into the list */
9674 if (!*changed_states) {
9675 *changed_states = last_item = item;
9676 } else {
9677 last_item->next = item;
9678 last_item = item;
9683 leave:
9684 if (err) {
9685 isds_list_free(changed_states);
9688 free(string);
9689 xmlXPathFreeObject(result);
9690 xmlXPathFreeContext(xpath_ctx);
9691 xmlFreeDoc(response);
9692 xmlFreeNode(request);
9694 if (!err)
9695 isds_log(ILF_ISDS, ILL_DEBUG,
9696 _("GetMessageStateChanges request processed by server "
9697 "successfully.\n"));
9698 #else /* not HAVE_LIBCURL */
9699 err = IE_NOTSUP;
9700 #endif
9701 return err;
9705 #if HAVE_LIBCURL
9706 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9707 * code
9708 * @context is session context
9709 * @service is ISDS WS service handler
9710 * @service_name is name of SERVICE_DM_OPERATIONS
9711 * @message_id is message ID to send as service argument to ISDS
9712 * @response is reallocated server SOAP body response as XML document
9713 * @raw_response is reallocated bit stream with response body. Use
9714 * NULL if you don't care
9715 * @raw_response_length is size of @raw_response in bytes
9716 * @code is reallocated ISDS status code
9717 * @status_message is reallocated ISDS status message
9718 * @return error coded from lower layer, context message will be set up
9719 * appropriately. */
9720 static isds_error build_send_check_message_request(struct isds_ctx *context,
9721 const isds_service service, const xmlChar *service_name,
9722 const char *message_id,
9723 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9724 xmlChar **code, xmlChar **status_message) {
9726 isds_error err = IE_SUCCESS;
9727 char *service_name_locale = NULL, *message_id_locale = NULL;
9728 xmlNodePtr request = NULL, node;
9729 xmlNsPtr isds_ns = NULL;
9731 if (!context) return IE_INVALID_CONTEXT;
9732 if (!service_name || !message_id) return IE_INVAL;
9733 if (!response || !code || !status_message) return IE_INVAL;
9734 if (!raw_response_length && raw_response) return IE_INVAL;
9736 /* Free output argument */
9737 xmlFreeDoc(*response); *response = NULL;
9738 if (raw_response) zfree(*raw_response);
9739 zfree(*code);
9740 zfree(*status_message);
9743 /* Check if connection is established
9744 * TODO: This check should be done downstairs. */
9745 if (!context->curl) return IE_CONNECTION_CLOSED;
9747 service_name_locale = _isds_utf82locale((char*)service_name);
9748 message_id_locale = _isds_utf82locale(message_id);
9749 if (!service_name_locale || !message_id_locale) {
9750 err = IE_NOMEM;
9751 goto leave;
9754 /* Build request */
9755 request = xmlNewNode(NULL, service_name);
9756 if (!request) {
9757 isds_printf_message(context,
9758 _("Could not build %s request for %s message ID"),
9759 service_name_locale, message_id_locale);
9760 err = IE_ERROR;
9761 goto leave;
9763 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9764 if(!isds_ns) {
9765 isds_log_message(context, _("Could not create ISDS name space"));
9766 err = IE_ERROR;
9767 goto leave;
9769 xmlSetNs(request, isds_ns);
9772 /* Add requested ID */
9773 err = validate_message_id_length(context, (xmlChar *) message_id);
9774 if (err) goto leave;
9775 INSERT_STRING(request, "dmID", message_id);
9778 isds_log(ILF_ISDS, ILL_DEBUG,
9779 _("Sending %s request for %s message ID to ISDS\n"),
9780 service_name_locale, message_id_locale);
9782 /* Send request */
9783 err = _isds(context, service, request, response,
9784 raw_response, raw_response_length);
9785 xmlFreeNode(request); request = NULL;
9787 if (err) {
9788 isds_log(ILF_ISDS, ILL_DEBUG,
9789 _("Processing ISDS response on %s request failed\n"),
9790 service_name_locale);
9791 goto leave;
9794 /* Check for response status */
9795 err = isds_response_status(context, service, *response,
9796 code, status_message, NULL);
9797 if (err) {
9798 isds_log(ILF_ISDS, ILL_DEBUG,
9799 _("ISDS response on %s request is missing status\n"),
9800 service_name_locale);
9801 goto leave;
9804 /* Request processed, but nothing found */
9805 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9806 char *code_locale = _isds_utf82locale((char*) *code);
9807 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9808 isds_log(ILF_ISDS, ILL_DEBUG,
9809 _("Server refused %s request for %s message ID "
9810 "(code=%s, message=%s)\n"),
9811 service_name_locale, message_id_locale,
9812 code_locale, status_message_locale);
9813 isds_log_message(context, status_message_locale);
9814 free(code_locale);
9815 free(status_message_locale);
9816 err = IE_ISDS;
9817 goto leave;
9820 leave:
9821 free(message_id_locale);
9822 free(service_name_locale);
9823 xmlFreeNode(request);
9824 return err;
9828 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9829 * signed data and free ISDS response.
9830 * @context is session context
9831 * @message_id is UTF-8 encoded message ID for logging purpose
9832 * @response is parsed XML document. It will be freed and NULLed in the middle
9833 * of function run to save memory. This is not guaranteed in case of error.
9834 * @request_name is name of ISDS request used to construct response root
9835 * element name and for logging purpose.
9836 * @raw is reallocated output buffer with DER encoded CMS data
9837 * @raw_length is size of @raw buffer in bytes
9838 * @returns standard error codes, in case of error, @raw will be freed and
9839 * NULLed, @response sometimes. */
9840 static isds_error find_extract_signed_data_free_response(
9841 struct isds_ctx *context, const xmlChar *message_id,
9842 xmlDocPtr *response, const xmlChar *request_name,
9843 void **raw, size_t *raw_length) {
9845 isds_error err = IE_SUCCESS;
9846 char *xpath_expression = NULL;
9847 xmlXPathContextPtr xpath_ctx = NULL;
9848 xmlXPathObjectPtr result = NULL;
9849 char *encoded_structure = NULL;
9851 if (!context) return IE_INVALID_CONTEXT;
9852 if (!raw) return IE_INVAL;
9853 zfree(*raw);
9854 if (!message_id || !response || !*response || !request_name || !raw_length)
9855 return IE_INVAL;
9857 /* Build XPath expression */
9858 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9859 "Response/isds:dmSignature");
9860 if (!xpath_expression) return IE_NOMEM;
9862 /* Extract data */
9863 xpath_ctx = xmlXPathNewContext(*response);
9864 if (!xpath_ctx) {
9865 err = IE_ERROR;
9866 goto leave;
9868 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9869 err = IE_ERROR;
9870 goto leave;
9872 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9873 if (!result) {
9874 err = IE_ERROR;
9875 goto leave;
9877 /* Empty response */
9878 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9879 char *message_id_locale = _isds_utf82locale((char*) message_id);
9880 isds_printf_message(context,
9881 _("Server did not return any signed data for message ID `%s' "
9882 "on %s request"),
9883 message_id_locale, request_name);
9884 free(message_id_locale);
9885 err = IE_ISDS;
9886 goto leave;
9888 /* More responses */
9889 if (result->nodesetval->nodeNr > 1) {
9890 char *message_id_locale = _isds_utf82locale((char*) message_id);
9891 isds_printf_message(context,
9892 _("Server did return more signed data for message ID `%s' "
9893 "on %s request"),
9894 message_id_locale, request_name);
9895 free(message_id_locale);
9896 err = IE_ISDS;
9897 goto leave;
9899 /* One response */
9900 xpath_ctx->node = result->nodesetval->nodeTab[0];
9902 /* Extract PKCS#7 structure */
9903 EXTRACT_STRING(".", encoded_structure);
9904 if (!encoded_structure) {
9905 isds_log_message(context, _("dmSignature element is empty"));
9908 /* Here we have delivery info as standalone CMS in encoded_structure.
9909 * We don't need any other data, free them: */
9910 xmlXPathFreeObject(result); result = NULL;
9911 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9912 xmlFreeDoc(*response); *response = NULL;
9915 /* Decode PKCS#7 to DER format */
9916 *raw_length = _isds_b64decode(encoded_structure, raw);
9917 if (*raw_length == (size_t) -1) {
9918 isds_log_message(context,
9919 _("Error while Base64-decoding PKCS#7 structure"));
9920 err = IE_ERROR;
9921 goto leave;
9924 leave:
9925 if (err) {
9926 zfree(*raw);
9927 raw_length = 0;
9930 free(encoded_structure);
9931 xmlXPathFreeObject(result);
9932 xmlXPathFreeContext(xpath_ctx);
9933 free(xpath_expression);
9935 return err;
9937 #endif /* HAVE_LIBCURL */
9940 /* Download incoming message envelope identified by ID.
9941 * @context is session context
9942 * @message_id is message identifier (you can get them from
9943 * isds_get_list_of_received_messages())
9944 * @message is automatically reallocated message retrieved from ISDS.
9945 * It will miss documents per se. Use isds_get_received_message(), if you are
9946 * interested in documents (content) too.
9947 * Returned hash and timestamp require documents to be verifiable. */
9948 isds_error isds_get_received_envelope(struct isds_ctx *context,
9949 const char *message_id, struct isds_message **message) {
9951 isds_error err = IE_SUCCESS;
9952 #if HAVE_LIBCURL
9953 xmlDocPtr response = NULL;
9954 xmlChar *code = NULL, *status_message = NULL;
9955 xmlXPathContextPtr xpath_ctx = NULL;
9956 xmlXPathObjectPtr result = NULL;
9957 #endif
9959 if (!context) return IE_INVALID_CONTEXT;
9960 zfree(context->long_message);
9962 /* Free former message if any */
9963 if (!message) return IE_INVAL;
9964 isds_message_free(message);
9966 #if HAVE_LIBCURL
9967 /* Do request and check for success */
9968 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9969 BAD_CAST "MessageEnvelopeDownload", message_id,
9970 &response, NULL, NULL, &code, &status_message);
9971 if (err) goto leave;
9973 /* Extract data */
9974 xpath_ctx = xmlXPathNewContext(response);
9975 if (!xpath_ctx) {
9976 err = IE_ERROR;
9977 goto leave;
9979 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9980 err = IE_ERROR;
9981 goto leave;
9983 result = xmlXPathEvalExpression(
9984 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9985 "isds:dmReturnedMessageEnvelope",
9986 xpath_ctx);
9987 if (!result) {
9988 err = IE_ERROR;
9989 goto leave;
9991 /* Empty response */
9992 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9993 char *message_id_locale = _isds_utf82locale((char*) message_id);
9994 isds_printf_message(context,
9995 _("Server did not return any envelope for ID `%s' "
9996 "on MessageEnvelopeDownload request"), message_id_locale);
9997 free(message_id_locale);
9998 err = IE_ISDS;
9999 goto leave;
10001 /* More envelops */
10002 if (result->nodesetval->nodeNr > 1) {
10003 char *message_id_locale = _isds_utf82locale((char*) message_id);
10004 isds_printf_message(context,
10005 _("Server did return more envelopes for ID `%s' "
10006 "on MessageEnvelopeDownload request"), message_id_locale);
10007 free(message_id_locale);
10008 err = IE_ISDS;
10009 goto leave;
10011 /* One message */
10012 xpath_ctx->node = result->nodesetval->nodeTab[0];
10014 /* Extract the envelope (= message without documents, hence 0) */
10015 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10016 if (err) goto leave;
10018 /* Save XML blob */
10019 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10020 &(*message)->raw_length);
10022 leave:
10023 if (err) {
10024 isds_message_free(message);
10027 xmlXPathFreeObject(result);
10028 xmlXPathFreeContext(xpath_ctx);
10030 free(code);
10031 free(status_message);
10032 if (!*message || !(*message)->xml) {
10033 xmlFreeDoc(response);
10036 if (!err)
10037 isds_log(ILF_ISDS, ILL_DEBUG,
10038 _("MessageEnvelopeDownload request processed by server "
10039 "successfully.\n")
10041 #else /* not HAVE_LIBCURL */
10042 err = IE_NOTSUP;
10043 #endif
10044 return err;
10048 /* Load delivery info of any format from buffer.
10049 * @context is session context
10050 * @raw_type advertises format of @buffer content. Only delivery info types
10051 * are accepted.
10052 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10053 * retrieve such data from message->raw after calling
10054 * isds_get_signed_delivery_info().
10055 * @length is length of buffer in bytes.
10056 * @message is automatically reallocated message parsed from @buffer.
10057 * @strategy selects how buffer will be attached into raw isds_message member.
10058 * */
10059 isds_error isds_load_delivery_info(struct isds_ctx *context,
10060 const isds_raw_type raw_type,
10061 const void *buffer, const size_t length,
10062 struct isds_message **message, const isds_buffer_strategy strategy) {
10064 isds_error err = IE_SUCCESS;
10065 message_ns_type message_ns;
10066 xmlDocPtr message_doc = NULL;
10067 xmlXPathContextPtr xpath_ctx = NULL;
10068 xmlXPathObjectPtr result = NULL;
10069 void *xml_stream = NULL;
10070 size_t xml_stream_length = 0;
10072 if (!context) return IE_INVALID_CONTEXT;
10073 zfree(context->long_message);
10074 if (!message) return IE_INVAL;
10075 isds_message_free(message);
10076 if (!buffer) return IE_INVAL;
10079 /* Select buffer format and extract XML from CMS*/
10080 switch (raw_type) {
10081 case RAWTYPE_DELIVERYINFO:
10082 message_ns = MESSAGE_NS_UNSIGNED;
10083 xml_stream = (void *) buffer;
10084 xml_stream_length = length;
10085 break;
10087 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
10088 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10089 xml_stream = (void *) buffer;
10090 xml_stream_length = length;
10091 break;
10093 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10094 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10095 err = _isds_extract_cms_data(context, buffer, length,
10096 &xml_stream, &xml_stream_length);
10097 if (err) goto leave;
10098 break;
10100 default:
10101 isds_log_message(context, _("Bad raw delivery representation type"));
10102 return IE_INVAL;
10103 break;
10106 isds_log(ILF_ISDS, ILL_DEBUG,
10107 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10108 xml_stream_length, xml_stream);
10110 /* Convert delivery info XML stream into XPath context */
10111 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10112 if (!message_doc) {
10113 err = IE_XML;
10114 goto leave;
10116 xpath_ctx = xmlXPathNewContext(message_doc);
10117 if (!xpath_ctx) {
10118 err = IE_ERROR;
10119 goto leave;
10121 /* XXX: Name spaces mangled for signed delivery info:
10122 * http://isds.czechpoint.cz/v20/delivery:
10124 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10125 * <q:dmDelivery>
10126 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10127 * <p:dmID>170272</p:dmID>
10128 * ...
10129 * </p:dmDm>
10130 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10131 * ...
10132 * </q:dmEvents>...</q:dmEvents>
10133 * </q:dmDelivery>
10134 * </q:GetDeliveryInfoResponse>
10135 * */
10136 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10137 err = IE_ERROR;
10138 goto leave;
10140 result = xmlXPathEvalExpression(
10141 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10142 xpath_ctx);
10143 if (!result) {
10144 err = IE_ERROR;
10145 goto leave;
10147 /* Empty delivery info */
10148 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10149 isds_printf_message(context,
10150 _("XML document is not sisds:dmDelivery document"));
10151 err = IE_ISDS;
10152 goto leave;
10154 /* More delivery info's */
10155 if (result->nodesetval->nodeNr > 1) {
10156 isds_printf_message(context,
10157 _("XML document has more sisds:dmDelivery elements"));
10158 err = IE_ISDS;
10159 goto leave;
10161 /* One delivery info */
10162 xpath_ctx->node = result->nodesetval->nodeTab[0];
10164 /* Extract the envelope (= message without documents, hence 0).
10165 * XXX: extract_TReturnedMessage() can obtain attachments size,
10166 * but delivery info carries none. It's coded as option elements,
10167 * so it should work. */
10168 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10169 if (err) goto leave;
10171 /* Extract events */
10172 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10173 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10174 if (err) { err = IE_ERROR; goto leave; }
10175 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10176 if (err) goto leave;
10178 /* Append raw CMS structure into message */
10179 (*message)->raw_type = raw_type;
10180 switch (strategy) {
10181 case BUFFER_DONT_STORE:
10182 break;
10183 case BUFFER_COPY:
10184 (*message)->raw = malloc(length);
10185 if (!(*message)->raw) {
10186 err = IE_NOMEM;
10187 goto leave;
10189 memcpy((*message)->raw, buffer, length);
10190 (*message)->raw_length = length;
10191 break;
10192 case BUFFER_MOVE:
10193 (*message)->raw = (void *) buffer;
10194 (*message)->raw_length = length;
10195 break;
10196 default:
10197 err = IE_ENUM;
10198 goto leave;
10201 leave:
10202 if (err) {
10203 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10204 isds_message_free(message);
10207 xmlXPathFreeObject(result);
10208 xmlXPathFreeContext(xpath_ctx);
10209 if (!*message || !(*message)->xml) {
10210 xmlFreeDoc(message_doc);
10212 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10214 if (!err)
10215 isds_log(ILF_ISDS, ILL_DEBUG,
10216 _("Delivery info loaded successfully.\n"));
10217 return err;
10221 /* Download signed delivery info-sheet of given message identified by ID.
10222 * @context is session context
10223 * @message_id is message identifier (you can get them from
10224 * isds_get_list_of_{sent,received}_messages())
10225 * @message is automatically reallocated message retrieved from ISDS.
10226 * It will miss documents per se. Use isds_get_signed_received_message(),
10227 * if you are interested in documents (content). OTOH, only this function
10228 * can get list events message has gone through. */
10229 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10230 const char *message_id, struct isds_message **message) {
10232 isds_error err = IE_SUCCESS;
10233 #if HAVE_LIBCURL
10234 xmlDocPtr response = NULL;
10235 xmlChar *code = NULL, *status_message = NULL;
10236 void *raw = NULL;
10237 size_t raw_length = 0;
10238 #endif
10240 if (!context) return IE_INVALID_CONTEXT;
10241 zfree(context->long_message);
10243 /* Free former message if any */
10244 if (!message) return IE_INVAL;
10245 isds_message_free(message);
10247 #if HAVE_LIBCURL
10248 /* Do request and check for success */
10249 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10250 BAD_CAST "GetSignedDeliveryInfo", message_id,
10251 &response, NULL, NULL, &code, &status_message);
10252 if (err) goto leave;
10254 /* Find signed delivery info, extract it into raw and maybe free
10255 * response */
10256 err = find_extract_signed_data_free_response(context,
10257 (xmlChar *)message_id, &response,
10258 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10259 if (err) goto leave;
10261 /* Parse delivery info */
10262 err = isds_load_delivery_info(context,
10263 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10264 message, BUFFER_MOVE);
10265 if (err) goto leave;
10267 raw = NULL;
10269 leave:
10270 if (err) {
10271 isds_message_free(message);
10274 free(raw);
10275 free(code);
10276 free(status_message);
10277 xmlFreeDoc(response);
10279 if (!err)
10280 isds_log(ILF_ISDS, ILL_DEBUG,
10281 _("GetSignedDeliveryInfo request processed by server "
10282 "successfully.\n")
10284 #else /* not HAVE_LIBCURL */
10285 err = IE_NOTSUP;
10286 #endif
10287 return err;
10291 /* Download delivery info-sheet of given message identified by ID.
10292 * @context is session context
10293 * @message_id is message identifier (you can get them from
10294 * isds_get_list_of_{sent,received}_messages())
10295 * @message is automatically reallocated message retrieved from ISDS.
10296 * It will miss documents per se. Use isds_get_received_message(), if you are
10297 * interested in documents (content). OTOH, only this function can get list
10298 * of events message has gone through. */
10299 isds_error isds_get_delivery_info(struct isds_ctx *context,
10300 const char *message_id, struct isds_message **message) {
10302 isds_error err = IE_SUCCESS;
10303 #if HAVE_LIBCURL
10304 xmlDocPtr response = NULL;
10305 xmlChar *code = NULL, *status_message = NULL;
10306 xmlNodePtr delivery_node = NULL;
10307 void *raw = NULL;
10308 size_t raw_length = 0;
10309 #endif
10311 if (!context) return IE_INVALID_CONTEXT;
10312 zfree(context->long_message);
10314 /* Free former message if any */
10315 if (!message) return IE_INVAL;
10316 isds_message_free(message);
10318 #if HAVE_LIBCURL
10319 /* Do request and check for success */
10320 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10321 BAD_CAST "GetDeliveryInfo", message_id,
10322 &response, NULL, NULL, &code, &status_message);
10323 if (err) goto leave;
10326 /* Serialize delivery info */
10327 delivery_node = xmlDocGetRootElement(response);
10328 if (!delivery_node) {
10329 char *message_id_locale = _isds_utf82locale((char*) message_id);
10330 isds_printf_message(context,
10331 _("Server did not return any delivery info for ID `%s' "
10332 "on GetDeliveryInfo request"), message_id_locale);
10333 free(message_id_locale);
10334 err = IE_ISDS;
10335 goto leave;
10337 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10338 if (err) goto leave;
10340 /* Parse delivery info */
10341 /* TODO: Here we parse the response second time. We could single delivery
10342 * parser from isds_load_delivery_info() to make things faster. */
10343 err = isds_load_delivery_info(context,
10344 RAWTYPE_DELIVERYINFO, raw, raw_length,
10345 message, BUFFER_MOVE);
10346 if (err) goto leave;
10348 raw = NULL;
10351 leave:
10352 if (err) {
10353 isds_message_free(message);
10356 free(raw);
10357 free(code);
10358 free(status_message);
10359 xmlFreeDoc(response);
10361 if (!err)
10362 isds_log(ILF_ISDS, ILL_DEBUG,
10363 _("GetDeliveryInfo request processed by server "
10364 "successfully.\n")
10366 #else /* not HAVE_LIBCURL */
10367 err = IE_NOTSUP;
10368 #endif
10369 return err;
10373 /* Download incoming message identified by ID.
10374 * @context is session context
10375 * @message_id is message identifier (you can get them from
10376 * isds_get_list_of_received_messages())
10377 * @message is automatically reallocated message retrieved from ISDS */
10378 isds_error isds_get_received_message(struct isds_ctx *context,
10379 const char *message_id, struct isds_message **message) {
10381 isds_error err = IE_SUCCESS;
10382 #if HAVE_LIBCURL
10383 xmlDocPtr response = NULL;
10384 void *xml_stream = NULL;
10385 size_t xml_stream_length;
10386 xmlChar *code = NULL, *status_message = NULL;
10387 xmlXPathContextPtr xpath_ctx = NULL;
10388 xmlXPathObjectPtr result = NULL;
10389 char *phys_path = NULL;
10390 size_t phys_start, phys_end;
10391 #endif
10393 if (!context) return IE_INVALID_CONTEXT;
10394 zfree(context->long_message);
10396 /* Free former message if any */
10397 if (NULL == message) return IE_INVAL;
10398 if (message) isds_message_free(message);
10400 #if HAVE_LIBCURL
10401 /* Do request and check for success */
10402 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10403 BAD_CAST "MessageDownload", message_id,
10404 &response, &xml_stream, &xml_stream_length,
10405 &code, &status_message);
10406 if (err) goto leave;
10408 /* Extract data */
10409 xpath_ctx = xmlXPathNewContext(response);
10410 if (!xpath_ctx) {
10411 err = IE_ERROR;
10412 goto leave;
10414 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10415 err = IE_ERROR;
10416 goto leave;
10418 result = xmlXPathEvalExpression(
10419 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10420 xpath_ctx);
10421 if (!result) {
10422 err = IE_ERROR;
10423 goto leave;
10425 /* Empty response */
10426 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10427 char *message_id_locale = _isds_utf82locale((char*) message_id);
10428 isds_printf_message(context,
10429 _("Server did not return any message for ID `%s' "
10430 "on MessageDownload request"), message_id_locale);
10431 free(message_id_locale);
10432 err = IE_ISDS;
10433 goto leave;
10435 /* More messages */
10436 if (result->nodesetval->nodeNr > 1) {
10437 char *message_id_locale = _isds_utf82locale((char*) message_id);
10438 isds_printf_message(context,
10439 _("Server did return more messages for ID `%s' "
10440 "on MessageDownload request"), message_id_locale);
10441 free(message_id_locale);
10442 err = IE_ISDS;
10443 goto leave;
10445 /* One message */
10446 xpath_ctx->node = result->nodesetval->nodeTab[0];
10448 /* Extract the message */
10449 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10450 if (err) goto leave;
10452 /* Locate raw XML blob */
10453 phys_path = strdup(
10454 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10455 PHYSXML_ELEMENT_SEPARATOR
10456 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10457 PHYSXML_ELEMENT_SEPARATOR
10458 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10460 if (!phys_path) {
10461 err = IE_NOMEM;
10462 goto leave;
10464 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10465 phys_path, &phys_start, &phys_end);
10466 zfree(phys_path);
10467 if (err) {
10468 isds_log_message(context,
10469 _("Substring with isds:MessageDownloadResponse element "
10470 "could not be located in raw SOAP message"));
10471 goto leave;
10473 /* Save XML blob */
10474 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10475 &(*message)->raw_length);*/
10476 /* TODO: Store name space declarations from ancestors */
10477 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10478 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10479 (*message)->raw_length = phys_end - phys_start + 1;
10480 (*message)->raw = malloc((*message)->raw_length);
10481 if (!(*message)->raw) {
10482 err = IE_NOMEM;
10483 goto leave;
10485 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10488 leave:
10489 if (err) {
10490 isds_message_free(message);
10493 free(phys_path);
10495 xmlXPathFreeObject(result);
10496 xmlXPathFreeContext(xpath_ctx);
10498 free(code);
10499 free(status_message);
10500 free(xml_stream);
10501 if (!*message || !(*message)->xml) {
10502 xmlFreeDoc(response);
10505 if (!err)
10506 isds_log(ILF_ISDS, ILL_DEBUG,
10507 _("MessageDownload request processed by server "
10508 "successfully.\n")
10510 #else /* not HAVE_LIBCURL */
10511 err = IE_NOTSUP;
10512 #endif
10513 return err;
10517 /* Load message of any type from buffer.
10518 * @context is session context
10519 * @raw_type defines content type of @buffer. Only message types are allowed.
10520 * @buffer is message raw representation. Format (CMS, plain signed,
10521 * message direction) is defined in @raw_type. You can retrieve such data
10522 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10523 * @length is length of buffer in bytes.
10524 * @message is automatically reallocated message parsed from @buffer.
10525 * @strategy selects how buffer will be attached into raw isds_message member.
10526 * */
10527 isds_error isds_load_message(struct isds_ctx *context,
10528 const isds_raw_type raw_type, const void *buffer, const size_t length,
10529 struct isds_message **message, const isds_buffer_strategy strategy) {
10531 isds_error err = IE_SUCCESS;
10532 void *xml_stream = NULL;
10533 size_t xml_stream_length = 0;
10534 message_ns_type message_ns;
10535 xmlDocPtr message_doc = NULL;
10536 xmlXPathContextPtr xpath_ctx = NULL;
10537 xmlXPathObjectPtr result = NULL;
10539 if (!context) return IE_INVALID_CONTEXT;
10540 zfree(context->long_message);
10541 if (!message) return IE_INVAL;
10542 isds_message_free(message);
10543 if (!buffer) return IE_INVAL;
10546 /* Select buffer format and extract XML from CMS*/
10547 switch (raw_type) {
10548 case RAWTYPE_INCOMING_MESSAGE:
10549 message_ns = MESSAGE_NS_UNSIGNED;
10550 xml_stream = (void *) buffer;
10551 xml_stream_length = length;
10552 break;
10554 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10555 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10556 xml_stream = (void *) buffer;
10557 xml_stream_length = length;
10558 break;
10560 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10561 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10562 err = _isds_extract_cms_data(context, buffer, length,
10563 &xml_stream, &xml_stream_length);
10564 if (err) goto leave;
10565 break;
10567 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10568 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10569 xml_stream = (void *) buffer;
10570 xml_stream_length = length;
10571 break;
10573 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10574 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10575 err = _isds_extract_cms_data(context, buffer, length,
10576 &xml_stream, &xml_stream_length);
10577 if (err) goto leave;
10578 break;
10580 default:
10581 isds_log_message(context, _("Bad raw message representation type"));
10582 return IE_INVAL;
10583 break;
10586 isds_log(ILF_ISDS, ILL_DEBUG,
10587 _("Loading message:\n%.*s\nEnd of message\n"),
10588 xml_stream_length, xml_stream);
10590 /* Convert messages XML stream into XPath context */
10591 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10592 if (!message_doc) {
10593 err = IE_XML;
10594 goto leave;
10596 xpath_ctx = xmlXPathNewContext(message_doc);
10597 if (!xpath_ctx) {
10598 err = IE_ERROR;
10599 goto leave;
10601 /* XXX: Standard name space for unsigned incoming direction:
10602 * http://isds.czechpoint.cz/v20/
10604 * XXX: Name spaces mangled for signed outgoing direction:
10605 * http://isds.czechpoint.cz/v20/SentMessage:
10607 * <q:MessageDownloadResponse
10608 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10609 * <q:dmReturnedMessage>
10610 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10611 * <p:dmID>151916</p:dmID>
10612 * ...
10613 * </p:dmDm>
10614 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10615 * ...
10616 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10617 * </q:dmReturnedMessage>
10618 * </q:MessageDownloadResponse>
10620 * XXX: Name spaces mangled for signed incoming direction:
10621 * http://isds.czechpoint.cz/v20/message:
10623 * <q:MessageDownloadResponse
10624 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10625 * <q:dmReturnedMessage>
10626 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10627 * <p:dmID>151916</p:dmID>
10628 * ...
10629 * </p:dmDm>
10630 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10631 * ...
10632 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10633 * </q:dmReturnedMessage>
10634 * </q:MessageDownloadResponse>
10636 * Stupidity of ISDS developers is unlimited */
10637 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10638 err = IE_ERROR;
10639 goto leave;
10641 result = xmlXPathEvalExpression(
10642 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10643 xpath_ctx);
10644 if (!result) {
10645 err = IE_ERROR;
10646 goto leave;
10648 /* Empty message */
10649 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10650 isds_printf_message(context,
10651 _("XML document does not contain "
10652 "sisds:dmReturnedMessage element"));
10653 err = IE_ISDS;
10654 goto leave;
10656 /* More messages */
10657 if (result->nodesetval->nodeNr > 1) {
10658 isds_printf_message(context,
10659 _("XML document has more sisds:dmReturnedMessage elements"));
10660 err = IE_ISDS;
10661 goto leave;
10663 /* One message */
10664 xpath_ctx->node = result->nodesetval->nodeTab[0];
10666 /* Extract the message */
10667 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10668 if (err) goto leave;
10670 /* Append raw buffer into message */
10671 (*message)->raw_type = raw_type;
10672 switch (strategy) {
10673 case BUFFER_DONT_STORE:
10674 break;
10675 case BUFFER_COPY:
10676 (*message)->raw = malloc(length);
10677 if (!(*message)->raw) {
10678 err = IE_NOMEM;
10679 goto leave;
10681 memcpy((*message)->raw, buffer, length);
10682 (*message)->raw_length = length;
10683 break;
10684 case BUFFER_MOVE:
10685 (*message)->raw = (void *) buffer;
10686 (*message)->raw_length = length;
10687 break;
10688 default:
10689 err = IE_ENUM;
10690 goto leave;
10694 leave:
10695 if (err) {
10696 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10697 isds_message_free(message);
10700 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10701 xmlXPathFreeObject(result);
10702 xmlXPathFreeContext(xpath_ctx);
10703 if (!*message || !(*message)->xml) {
10704 xmlFreeDoc(message_doc);
10707 if (!err)
10708 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10709 return err;
10713 /* Determine type of raw message or delivery info according some heuristics.
10714 * It does not validate the raw blob.
10715 * @context is session context
10716 * @raw_type returns content type of @buffer. Valid only if exit code of this
10717 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10718 * reallocated memory.
10719 * @buffer is message raw representation.
10720 * @length is length of buffer in bytes. */
10721 isds_error isds_guess_raw_type(struct isds_ctx *context,
10722 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10723 isds_error err;
10724 void *xml_stream = NULL;
10725 size_t xml_stream_length = 0;
10726 xmlDocPtr document = NULL;
10727 xmlNodePtr root = NULL;
10729 if (!context) return IE_INVALID_CONTEXT;
10730 zfree(context->long_message);
10731 if (length == 0 || !buffer) return IE_INVAL;
10732 if (!raw_type) return IE_INVAL;
10734 /* Try CMS */
10735 err = _isds_extract_cms_data(context, buffer, length,
10736 &xml_stream, &xml_stream_length);
10737 if (err) {
10738 xml_stream = (void *) buffer;
10739 xml_stream_length = (size_t) length;
10740 err = IE_SUCCESS;
10743 /* Try XML */
10744 document = xmlParseMemory(xml_stream, xml_stream_length);
10745 if (!document) {
10746 isds_printf_message(context,
10747 _("Could not parse data as XML document"));
10748 err = IE_NOTSUP;
10749 goto leave;
10752 /* Get root element */
10753 root = xmlDocGetRootElement(document);
10754 if (!root) {
10755 isds_printf_message(context,
10756 _("XML document is missing root element"));
10757 err = IE_XML;
10758 goto leave;
10761 if (!root->ns || !root->ns->href) {
10762 isds_printf_message(context,
10763 _("Root element does not belong to any name space"));
10764 err = IE_NOTSUP;
10765 goto leave;
10768 /* Test name space */
10769 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10770 if (xml_stream == buffer)
10771 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10772 else
10773 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10774 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10775 if (xml_stream == buffer)
10776 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10777 else
10778 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10779 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10780 if (xml_stream == buffer)
10781 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10782 else
10783 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10784 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10785 if (xml_stream != buffer) {
10786 isds_printf_message(context,
10787 _("Document in ISDS name space is encapsulated into CMS" ));
10788 err = IE_NOTSUP;
10789 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10790 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10791 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10792 *raw_type = RAWTYPE_DELIVERYINFO;
10793 else {
10794 isds_printf_message(context,
10795 _("Unknown root element in ISDS name space"));
10796 err = IE_NOTSUP;
10798 } else {
10799 isds_printf_message(context,
10800 _("Unknown name space"));
10801 err = IE_NOTSUP;
10804 leave:
10805 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10806 xmlFreeDoc(document);
10807 return err;
10811 /* Download signed incoming/outgoing message identified by ID.
10812 * @context is session context
10813 * @output is true for outgoing message, false for incoming message
10814 * @message_id is message identifier (you can get them from
10815 * isds_get_list_of_{sent,received}_messages())
10816 * @message is automatically reallocated message retrieved from ISDS. The raw
10817 * member will be filled with PKCS#7 structure in DER format. */
10818 static isds_error isds_get_signed_message(struct isds_ctx *context,
10819 const _Bool outgoing, const char *message_id,
10820 struct isds_message **message) {
10822 isds_error err = IE_SUCCESS;
10823 #if HAVE_LIBCURL
10824 xmlDocPtr response = NULL;
10825 xmlChar *code = NULL, *status_message = NULL;
10826 xmlXPathContextPtr xpath_ctx = NULL;
10827 xmlXPathObjectPtr result = NULL;
10828 char *encoded_structure = NULL;
10829 void *raw = NULL;
10830 size_t raw_length = 0;
10831 #endif
10833 if (!context) return IE_INVALID_CONTEXT;
10834 zfree(context->long_message);
10835 if (!message) return IE_INVAL;
10836 isds_message_free(message);
10838 #if HAVE_LIBCURL
10839 /* Do request and check for success */
10840 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10841 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10842 BAD_CAST "SignedMessageDownload",
10843 message_id, &response, NULL, NULL, &code, &status_message);
10844 if (err) goto leave;
10846 /* Find signed message, extract it into raw and maybe free
10847 * response */
10848 err = find_extract_signed_data_free_response(context,
10849 (xmlChar *)message_id, &response,
10850 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10851 BAD_CAST "SignedMessageDownload",
10852 &raw, &raw_length);
10853 if (err) goto leave;
10855 /* Parse message */
10856 err = isds_load_message(context,
10857 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10858 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10859 raw, raw_length, message, BUFFER_MOVE);
10860 if (err) goto leave;
10862 raw = NULL;
10864 leave:
10865 if (err) {
10866 isds_message_free(message);
10869 free(encoded_structure);
10870 xmlXPathFreeObject(result);
10871 xmlXPathFreeContext(xpath_ctx);
10872 free(raw);
10874 free(code);
10875 free(status_message);
10876 xmlFreeDoc(response);
10878 if (!err)
10879 isds_log(ILF_ISDS, ILL_DEBUG,
10880 (outgoing) ?
10881 _("SignedSentMessageDownload request processed by server "
10882 "successfully.\n") :
10883 _("SignedMessageDownload request processed by server "
10884 "successfully.\n")
10886 #else /* not HAVE_LIBCURL */
10887 err = IE_NOTSUP;
10888 #endif
10889 return err;
10893 /* Download signed incoming message identified by ID.
10894 * @context is session context
10895 * @message_id is message identifier (you can get them from
10896 * isds_get_list_of_received_messages())
10897 * @message is automatically reallocated message retrieved from ISDS. The raw
10898 * member will be filled with PKCS#7 structure in DER format. */
10899 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10900 const char *message_id, struct isds_message **message) {
10901 return isds_get_signed_message(context, 0, message_id, message);
10905 /* Download signed outgoing message identified by ID.
10906 * @context is session context
10907 * @message_id is message identifier (you can get them from
10908 * isds_get_list_of_sent_messages())
10909 * @message is automatically reallocated message retrieved from ISDS. The raw
10910 * member will be filled with PKCS#7 structure in DER format. */
10911 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10912 const char *message_id, struct isds_message **message) {
10913 return isds_get_signed_message(context, 1, message_id, message);
10917 /* Get type and name of user who sent a message identified by ID.
10918 * @context is session context
10919 * @message_id is message identifier
10920 * @sender_type is pointer to automatically allocated type of sender detected
10921 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10922 * library or to the server, NULL will be returned. Pass NULL if you don't
10923 * care about it.
10924 * @raw_sender_type is automatically reallocated UTF-8 string describing
10925 * sender type or NULL if not known to server. Pass NULL if you don't care.
10926 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10927 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10928 isds_error isds_get_message_sender(struct isds_ctx *context,
10929 const char *message_id, isds_sender_type **sender_type,
10930 char **raw_sender_type, char **sender_name) {
10931 isds_error err = IE_SUCCESS;
10932 #if HAVE_LIBCURL
10933 xmlDocPtr response = NULL;
10934 xmlChar *code = NULL, *status_message = NULL;
10935 xmlXPathContextPtr xpath_ctx = NULL;
10936 xmlXPathObjectPtr result = NULL;
10937 char *type_string = NULL;
10938 #endif
10940 if (!context) return IE_INVALID_CONTEXT;
10941 zfree(context->long_message);
10942 if (sender_type) zfree(*sender_type);
10943 if (raw_sender_type) zfree(*raw_sender_type);
10944 if (sender_name) zfree(*sender_name);
10945 if (!message_id) return IE_INVAL;
10947 #if HAVE_LIBCURL
10948 /* Do request and check for success */
10949 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10950 BAD_CAST "GetMessageAuthor",
10951 message_id, &response, NULL, NULL, &code, &status_message);
10952 if (err) goto leave;
10954 /* Extract data */
10955 xpath_ctx = xmlXPathNewContext(response);
10956 if (!xpath_ctx) {
10957 err = IE_ERROR;
10958 goto leave;
10960 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10961 err = IE_ERROR;
10962 goto leave;
10964 result = xmlXPathEvalExpression(
10965 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10966 if (!result) {
10967 err = IE_ERROR;
10968 goto leave;
10970 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10971 isds_log_message(context,
10972 _("Missing GetMessageAuthorResponse element"));
10973 err = IE_ISDS;
10974 goto leave;
10976 if (result->nodesetval->nodeNr > 1) {
10977 isds_log_message(context,
10978 _("Multiple GetMessageAuthorResponse element"));
10979 err = IE_ISDS;
10980 goto leave;
10982 xpath_ctx->node = result->nodesetval->nodeTab[0];
10983 xmlXPathFreeObject(result); result = NULL;
10985 /* Fill output arguments in */
10986 EXTRACT_STRING("isds:userType", type_string);
10987 if (NULL != type_string) {
10988 if (NULL != sender_type) {
10989 *sender_type = calloc(1, sizeof(**sender_type));
10990 if (NULL == *sender_type) {
10991 err = IE_NOMEM;
10992 goto leave;
10995 err = string2isds_sender_type((xmlChar *)type_string,
10996 *sender_type);
10997 if (err) {
10998 zfree(*sender_type);
10999 if (err == IE_ENUM) {
11000 err = IE_SUCCESS;
11001 char *type_string_locale = _isds_utf82locale(type_string);
11002 isds_log(ILF_ISDS, ILL_WARNING,
11003 _("Unknown isds:userType value: %s"),
11004 type_string_locale);
11005 free(type_string_locale);
11010 if (NULL != sender_name)
11011 EXTRACT_STRING("isds:authorName", *sender_name);
11013 leave:
11014 if (err) {
11015 if (NULL != sender_type) zfree(*sender_type);
11016 zfree(type_string);
11017 if (NULL != sender_name) zfree(*sender_name);
11019 if (NULL != raw_sender_type) *raw_sender_type = type_string;
11021 xmlXPathFreeObject(result);
11022 xmlXPathFreeContext(xpath_ctx);
11024 free(code);
11025 free(status_message);
11026 xmlFreeDoc(response);
11028 if (!err)
11029 isds_log(ILF_ISDS, ILL_DEBUG,
11030 _("GetMessageAuthor request processed by server "
11031 "successfully.\n"));
11032 #else /* not HAVE_LIBCURL */
11033 err = IE_NOTSUP;
11034 #endif
11035 return err;
11039 /* Retrieve hash of message identified by ID stored in ISDS.
11040 * @context is session context
11041 * @message_id is message identifier
11042 * @hash is automatically reallocated message hash downloaded from ISDS.
11043 * Message must exist in system and must not be deleted. */
11044 isds_error isds_download_message_hash(struct isds_ctx *context,
11045 const char *message_id, struct isds_hash **hash) {
11047 isds_error err = IE_SUCCESS;
11048 #if HAVE_LIBCURL
11049 xmlDocPtr response = NULL;
11050 xmlChar *code = NULL, *status_message = NULL;
11051 xmlXPathContextPtr xpath_ctx = NULL;
11052 xmlXPathObjectPtr result = NULL;
11053 #endif
11055 if (!context) return IE_INVALID_CONTEXT;
11056 zfree(context->long_message);
11058 isds_hash_free(hash);
11060 #if HAVE_LIBCURL
11061 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11062 BAD_CAST "VerifyMessage", message_id,
11063 &response, NULL, NULL, &code, &status_message);
11064 if (err) goto leave;
11067 /* Extract data */
11068 xpath_ctx = xmlXPathNewContext(response);
11069 if (!xpath_ctx) {
11070 err = IE_ERROR;
11071 goto leave;
11073 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11074 err = IE_ERROR;
11075 goto leave;
11077 result = xmlXPathEvalExpression(
11078 BAD_CAST "/isds:VerifyMessageResponse",
11079 xpath_ctx);
11080 if (!result) {
11081 err = IE_ERROR;
11082 goto leave;
11084 /* Empty response */
11085 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11086 char *message_id_locale = _isds_utf82locale((char*) message_id);
11087 isds_printf_message(context,
11088 _("Server did not return any response for ID `%s' "
11089 "on VerifyMessage request"), message_id_locale);
11090 free(message_id_locale);
11091 err = IE_ISDS;
11092 goto leave;
11094 /* More responses */
11095 if (result->nodesetval->nodeNr > 1) {
11096 char *message_id_locale = _isds_utf82locale((char*) message_id);
11097 isds_printf_message(context,
11098 _("Server did return more responses for ID `%s' "
11099 "on VerifyMessage request"), message_id_locale);
11100 free(message_id_locale);
11101 err = IE_ISDS;
11102 goto leave;
11104 /* One response */
11105 xpath_ctx->node = result->nodesetval->nodeTab[0];
11107 /* Extract the hash */
11108 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11110 leave:
11111 if (err) {
11112 isds_hash_free(hash);
11115 xmlXPathFreeObject(result);
11116 xmlXPathFreeContext(xpath_ctx);
11118 free(code);
11119 free(status_message);
11120 xmlFreeDoc(response);
11122 if (!err)
11123 isds_log(ILF_ISDS, ILL_DEBUG,
11124 _("VerifyMessage request processed by server "
11125 "successfully.\n")
11127 #else /* not HAVE_LIBCURL */
11128 err = IE_NOTSUP;
11129 #endif
11130 return err;
11134 /* Erase message specified by @message_id from long term storage. Other
11135 * message cannot be erased on user request.
11136 * @context is session context
11137 * @message_id is message identifier.
11138 * @incoming is true for incoming message, false for outgoing message.
11139 * @return
11140 * IE_SUCCESS if message has ben removed
11141 * IE_INVAL if message does not exist in long term storage or message
11142 * belongs to different box
11143 * TODO: IE_NOEPRM if user has no permission to erase a message */
11144 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11145 const char *message_id, _Bool incoming) {
11146 isds_error err = IE_SUCCESS;
11147 #if HAVE_LIBCURL
11148 xmlNodePtr request = NULL, node;
11149 xmlNsPtr isds_ns = NULL;
11150 xmlDocPtr response = NULL;
11151 xmlChar *code = NULL, *status_message = NULL;
11152 #endif
11154 if (!context) return IE_INVALID_CONTEXT;
11155 zfree(context->long_message);
11156 if (NULL == message_id) return IE_INVAL;
11158 /* Check if connection is established
11159 * TODO: This check should be done downstairs. */
11160 if (!context->curl) return IE_CONNECTION_CLOSED;
11162 #if HAVE_LIBCURL
11163 /* Build request */
11164 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11165 if (!request) {
11166 isds_log_message(context,
11167 _("Could build EraseMessage request"));
11168 return IE_ERROR;
11170 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11171 if(!isds_ns) {
11172 isds_log_message(context, _("Could not create ISDS name space"));
11173 xmlFreeNode(request);
11174 return IE_ERROR;
11176 xmlSetNs(request, isds_ns);
11178 err = validate_message_id_length(context, (xmlChar *) message_id);
11179 if (err) goto leave;
11180 INSERT_STRING(request, "dmID", message_id);
11182 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11185 /* Send request */
11186 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11187 "message ID %s to ISDS\n"), message_id);
11188 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11189 xmlFreeNode(request); request = NULL;
11191 if (err) {
11192 isds_log(ILF_ISDS, ILL_DEBUG,
11193 _("Processing ISDS response on EraseMessage request "
11194 "failed\n"));
11195 goto leave;
11198 /* Check for response status */
11199 err = isds_response_status(context, SERVICE_DM_INFO, response,
11200 &code, &status_message, NULL);
11201 if (err) {
11202 isds_log(ILF_ISDS, ILL_DEBUG,
11203 _("ISDS response on EraseMessage request is missing "
11204 "status\n"));
11205 goto leave;
11208 /* Check server status code */
11209 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11210 isds_log_message(context, _("Message to erase belongs to other box"));
11211 err = IE_INVAL;
11212 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11213 isds_log_message(context, _("Message to erase is not saved in "
11214 "long term storage or the direction does not match"));
11215 err = IE_INVAL;
11216 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11217 char *code_locale = _isds_utf82locale((char*) code);
11218 char *message_locale = _isds_utf82locale((char*) status_message);
11219 isds_log(ILF_ISDS, ILL_DEBUG,
11220 _("Server refused EraseMessage request "
11221 "(code=%s, message=%s)\n"),
11222 code_locale, message_locale);
11223 isds_log_message(context, message_locale);
11224 free(code_locale);
11225 free(message_locale);
11226 err = IE_ISDS;
11227 goto leave;
11230 leave:
11231 free(code);
11232 free(status_message);
11233 xmlFreeDoc(response);
11234 xmlFreeNode(request);
11236 if (!err)
11237 isds_log(ILF_ISDS, ILL_DEBUG,
11238 _("EraseMessage request processed by server "
11239 "successfully.\n")
11241 #else /* not HAVE_LIBCURL */
11242 err = IE_NOTSUP;
11243 #endif
11244 return err;
11248 /* Mark message as read. This is a transactional commit function to acknowledge
11249 * to ISDS the message has been downloaded and processed by client properly.
11250 * @context is session context
11251 * @message_id is message identifier. */
11252 isds_error isds_mark_message_read(struct isds_ctx *context,
11253 const char *message_id) {
11255 isds_error err = IE_SUCCESS;
11256 #if HAVE_LIBCURL
11257 xmlDocPtr response = NULL;
11258 xmlChar *code = NULL, *status_message = NULL;
11259 #endif
11261 if (!context) return IE_INVALID_CONTEXT;
11262 zfree(context->long_message);
11264 #if HAVE_LIBCURL
11265 /* Do request and check for success */
11266 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11267 BAD_CAST "MarkMessageAsDownloaded", message_id,
11268 &response, NULL, NULL, &code, &status_message);
11270 free(code);
11271 free(status_message);
11272 xmlFreeDoc(response);
11274 if (!err)
11275 isds_log(ILF_ISDS, ILL_DEBUG,
11276 _("MarkMessageAsDownloaded request processed by server "
11277 "successfully.\n")
11279 #else /* not HAVE_LIBCURL */
11280 err = IE_NOTSUP;
11281 #endif
11282 return err;
11286 /* Mark message as received by recipient. This is applicable only to
11287 * commercial message. Use envelope->dmType message member to distinguish
11288 * commercial message from government message. Government message is
11289 * received automatically (by law), commercial message on recipient request.
11290 * @context is session context
11291 * @message_id is message identifier. */
11292 isds_error isds_mark_message_received(struct isds_ctx *context,
11293 const char *message_id) {
11295 isds_error err = IE_SUCCESS;
11296 #if HAVE_LIBCURL
11297 xmlDocPtr response = NULL;
11298 xmlChar *code = NULL, *status_message = NULL;
11299 #endif
11301 if (!context) return IE_INVALID_CONTEXT;
11302 zfree(context->long_message);
11304 #if HAVE_LIBCURL
11305 /* Do request and check for success */
11306 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11307 BAD_CAST "ConfirmDelivery", message_id,
11308 &response, NULL, NULL, &code, &status_message);
11310 free(code);
11311 free(status_message);
11312 xmlFreeDoc(response);
11314 if (!err)
11315 isds_log(ILF_ISDS, ILL_DEBUG,
11316 _("ConfirmDelivery request processed by server "
11317 "successfully.\n")
11319 #else /* not HAVE_LIBCURL */
11320 err = IE_NOTSUP;
11321 #endif
11322 return err;
11326 /* Send document for authorized conversion into Czech POINT system.
11327 * This is public anonymous service, no log-in necessary. Special context is
11328 * used to reuse keep-a-live HTTPS connection.
11329 * @context is Czech POINT session context. DO NOT use context connected to
11330 * ISDS server. Use new context or context used by this function previously.
11331 * @document is document to convert. Only data, data_length, dmFileDescr and
11332 * is_xml members are significant. Be ware that not all document formats can be
11333 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11334 * @id is reallocated identifier assigned by Czech POINT system to
11335 * your document on submit. Use is to tell it to Czech POINT officer.
11336 * @date is reallocated document submit date (submitted documents
11337 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11338 * value. */
11339 isds_error czp_convert_document(struct isds_ctx *context,
11340 const struct isds_document *document,
11341 char **id, struct tm **date) {
11342 isds_error err = IE_SUCCESS;
11343 #if HAVE_LIBCURL
11344 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11345 xmlNodePtr request = NULL, node;
11346 xmlDocPtr response = NULL;
11348 xmlXPathContextPtr xpath_ctx = NULL;
11349 xmlXPathObjectPtr result = NULL;
11350 long int status = -1;
11351 long int *status_ptr = &status;
11352 char *string = NULL;
11353 #endif
11356 if (!context) return IE_INVALID_CONTEXT;
11357 zfree(context->long_message);
11358 if (!document || !id || !date) return IE_INVAL;
11360 if (document->is_xml) {
11361 isds_log_message(context,
11362 _("XML documents cannot be submitted to conversion"));
11363 return IE_NOTSUP;
11366 /* Free output arguments */
11367 zfree(*id);
11368 zfree(*date);
11370 #if HAVE_LIBCURL
11371 /* Store configuration */
11372 context->type = CTX_TYPE_CZP;
11373 free(context->url);
11374 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11375 if (!(context->url))
11376 return IE_NOMEM;
11378 /* Prepare CURL handle if not yet connected */
11379 if (!context->curl) {
11380 context->curl = curl_easy_init();
11381 if (!(context->curl))
11382 return IE_ERROR;
11385 /* Build conversion request */
11386 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11387 if (!request) {
11388 isds_log_message(context,
11389 _("Could not build Czech POINT conversion request"));
11390 return IE_ERROR;
11392 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11393 if(!deposit_ns) {
11394 isds_log_message(context,
11395 _("Could not create Czech POINT deposit name space"));
11396 xmlFreeNode(request);
11397 return IE_ERROR;
11399 xmlSetNs(request, deposit_ns);
11401 /* Insert children. They are in empty namespace! */
11402 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11403 if(!empty_ns) {
11404 isds_log_message(context, _("Could not create empty name space"));
11405 err = IE_ERROR;
11406 goto leave;
11408 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11409 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11410 document->dmFileDescr);
11412 /* Document encoded in Base64 */
11413 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11414 document->data, document->data_length);
11415 if (err) goto leave;
11417 isds_log(ILF_ISDS, ILL_DEBUG,
11418 _("Submitting document for conversion into Czech POINT deposit"));
11420 /* Send conversion request */
11421 err = _czp_czpdeposit(context, request, &response);
11422 xmlFreeNode(request); request = NULL;
11424 if (err) {
11425 czp_do_close_connection(context);
11426 goto leave;
11430 /* Extract response */
11431 xpath_ctx = xmlXPathNewContext(response);
11432 if (!xpath_ctx) {
11433 err = IE_ERROR;
11434 goto leave;
11436 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11437 err = IE_ERROR;
11438 goto leave;
11440 result = xmlXPathEvalExpression(
11441 BAD_CAST "/deposit:saveDocumentResponse/return",
11442 xpath_ctx);
11443 if (!result) {
11444 err = IE_ERROR;
11445 goto leave;
11447 /* Empty response */
11448 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11449 isds_printf_message(context,
11450 _("Missing `return' element in Czech POINT deposit response"));
11451 err = IE_ISDS;
11452 goto leave;
11454 /* More responses */
11455 if (result->nodesetval->nodeNr > 1) {
11456 isds_printf_message(context,
11457 _("Multiple `return' element in Czech POINT deposit response"));
11458 err = IE_ISDS;
11459 goto leave;
11461 /* One response */
11462 xpath_ctx->node = result->nodesetval->nodeTab[0];
11464 /* Get status */
11465 EXTRACT_LONGINT("status", status_ptr, 1);
11466 if (status) {
11467 EXTRACT_STRING("statusMsg", string);
11468 char *string_locale = _isds_utf82locale(string);
11469 isds_printf_message(context,
11470 _("Czech POINT deposit refused document for conversion "
11471 "(code=%ld, message=%s)"),
11472 status, string_locale);
11473 free(string_locale);
11474 err = IE_ISDS;
11475 goto leave;
11478 /* Get document ID */
11479 EXTRACT_STRING("documentID", *id);
11481 /* Get submit date */
11482 EXTRACT_STRING("dateInserted", string);
11483 if (string) {
11484 *date = calloc(1, sizeof(**date));
11485 if (!*date) {
11486 err = IE_NOMEM;
11487 goto leave;
11489 err = _isds_datestring2tm((xmlChar *)string, *date);
11490 if (err) {
11491 if (err == IE_NOTSUP) {
11492 err = IE_ISDS;
11493 char *string_locale = _isds_utf82locale(string);
11494 isds_printf_message(context,
11495 _("Invalid dateInserted value: %s"), string_locale);
11496 free(string_locale);
11498 goto leave;
11502 leave:
11503 free(string);
11504 xmlXPathFreeObject(result);
11505 xmlXPathFreeContext(xpath_ctx);
11507 xmlFreeDoc(response);
11508 xmlFreeNode(request);
11510 if (!err) {
11511 char *id_locale = _isds_utf82locale((char *) *id);
11512 isds_log(ILF_ISDS, ILL_DEBUG,
11513 _("Document %s has been submitted for conversion "
11514 "to server successfully\n"), id_locale);
11515 free(id_locale);
11517 #else /* not HAVE_LIBCURL */
11518 err = IE_NOTSUP;
11519 #endif
11520 return err;
11524 /* Close possibly opened connection to Czech POINT document deposit.
11525 * @context is Czech POINT session context. */
11526 isds_error czp_close_connection(struct isds_ctx *context) {
11527 if (!context) return IE_INVALID_CONTEXT;
11528 zfree(context->long_message);
11529 #if HAVE_LIBCURL
11530 return czp_do_close_connection(context);
11531 #else
11532 return IE_NOTSUP;
11533 #endif
11537 /* Send request for new box creation in testing ISDS instance.
11538 * It's not possible to request for a production box currently, as it
11539 * communicates via e-mail.
11540 * XXX: This function does not work either. Server complains about invalid
11541 * e-mail address.
11542 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11543 * this function
11544 * @context is special session context for box creation request. DO NOT use
11545 * standard context as it could reveal your password. Use fresh new context or
11546 * context previously used by this function.
11547 * @box is box description to create including single primary user (in case of
11548 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
11549 * ignored. It outputs box ID assigned by ISDS in dbID element.
11550 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11551 * box, or contact address of PFO box owner). The email member is mandatory as
11552 * it will be used to deliver credentials.
11553 * @former_names is former name of box owner. Pass NULL if you don't care.
11554 * @approval is optional external approval of box manipulation
11555 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11556 * NULL, if you don't care.*/
11557 isds_error isds_request_new_testing_box(struct isds_ctx *context,
11558 struct isds_DbOwnerInfo *box, const struct isds_list *users,
11559 const char *former_names, const struct isds_approval *approval,
11560 char **refnumber) {
11561 isds_error err = IE_SUCCESS;
11562 #if HAVE_LIBCURL
11563 xmlNodePtr request = NULL;
11564 xmlDocPtr response = NULL;
11565 xmlXPathContextPtr xpath_ctx = NULL;
11566 xmlXPathObjectPtr result = NULL;
11567 #endif
11570 if (!context) return IE_INVALID_CONTEXT;
11571 zfree(context->long_message);
11572 if (!box) return IE_INVAL;
11574 #if HAVE_LIBCURL
11575 if (!box->email || box->email[0] == '\0') {
11576 isds_log_message(context, _("E-mail field is mandatory"));
11577 return IE_INVAL;
11580 /* Scratch box ID */
11581 zfree(box->dbID);
11583 /* Store configuration */
11584 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
11585 free(context->url);
11586 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
11587 if (!(context->url))
11588 return IE_NOMEM;
11590 /* Prepare CURL handle if not yet connected */
11591 if (!context->curl) {
11592 context->curl = curl_easy_init();
11593 if (!(context->curl))
11594 return IE_ERROR;
11597 /* Build CreateDataBox request */
11598 err = build_CreateDBInput_request(context,
11599 &request, BAD_CAST "CreateDataBox",
11600 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
11601 if (err) goto leave;
11603 /* Send it to server and process response */
11604 err = send_destroy_request_check_response(context,
11605 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
11606 &response, (xmlChar **) refnumber, NULL);
11607 if (err) goto leave;
11609 /* Extract box ID */
11610 xpath_ctx = xmlXPathNewContext(response);
11611 if (!xpath_ctx) {
11612 err = IE_ERROR;
11613 goto leave;
11615 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11616 err = IE_ERROR;
11617 goto leave;
11619 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
11621 leave:
11622 xmlXPathFreeObject(result);
11623 xmlXPathFreeContext(xpath_ctx);
11624 xmlFreeDoc(response);
11625 xmlFreeNode(request);
11627 if (!err) {
11628 isds_log(ILF_ISDS, ILL_DEBUG,
11629 _("CreateDataBox request processed by server successfully.\n"));
11631 #else /* not HAVE_LIBCURL */
11632 err = IE_NOTSUP;
11633 #endif
11635 return err;
11639 /* Submit CMS signed message to ISDS to verify its originality. This is
11640 * stronger form of isds_verify_message_hash() because ISDS does more checks
11641 * than simple one (potentialy old weak) hash comparison.
11642 * @context is session context
11643 * @message is memory with raw CMS signed message bit stream
11644 * @length is @message size in bytes
11645 * @return
11646 * IE_SUCCESS if message originates in ISDS
11647 * IE_NOTEQUAL if message is unknown to ISDS
11648 * other code for other errors */
11649 isds_error isds_authenticate_message(struct isds_ctx *context,
11650 const void *message, size_t length) {
11651 isds_error err = IE_SUCCESS;
11652 #if HAVE_LIBCURL
11653 xmlNsPtr isds_ns = NULL;
11654 xmlNodePtr request = NULL;
11655 xmlDocPtr response = NULL;
11656 xmlXPathContextPtr xpath_ctx = NULL;
11657 xmlXPathObjectPtr result = NULL;
11658 _Bool *authentic = NULL;
11659 #endif
11661 if (!context) return IE_INVALID_CONTEXT;
11662 zfree(context->long_message);
11663 if (!message || length == 0) return IE_INVAL;
11665 #if HAVE_LIBCURL
11666 /* Check if connection is established
11667 * TODO: This check should be done downstairs. */
11668 if (!context->curl) return IE_CONNECTION_CLOSED;
11671 /* Build AuthenticateMessage request */
11672 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11673 if (!request) {
11674 isds_log_message(context,
11675 _("Could not build AuthenticateMessage request"));
11676 return IE_ERROR;
11678 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11679 if(!isds_ns) {
11680 isds_log_message(context, _("Could not create ISDS name space"));
11681 xmlFreeNode(request);
11682 return IE_ERROR;
11684 xmlSetNs(request, isds_ns);
11686 /* Insert Base64 encoded message */
11687 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11688 message, length);
11689 if (err) goto leave;
11691 /* Send request to server and process response */
11692 err = send_destroy_request_check_response(context,
11693 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11694 &response, NULL, NULL);
11695 if (err) goto leave;
11698 /* ISDS has decided */
11699 xpath_ctx = xmlXPathNewContext(response);
11700 if (!xpath_ctx) {
11701 err = IE_ERROR;
11702 goto leave;
11704 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11705 err = IE_ERROR;
11706 goto leave;
11709 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11711 if (!authentic) {
11712 isds_log_message(context,
11713 _("Server did not return any response on "
11714 "AuthenticateMessage request"));
11715 err = IE_ISDS;
11716 goto leave;
11718 if (*authentic) {
11719 isds_log(ILF_ISDS, ILL_DEBUG,
11720 _("ISDS authenticated the message successfully\n"));
11721 } else {
11722 isds_log_message(context, _("ISDS does not know the message"));
11723 err = IE_NOTEQUAL;
11727 leave:
11728 free(authentic);
11729 xmlXPathFreeObject(result);
11730 xmlXPathFreeContext(xpath_ctx);
11732 xmlFreeDoc(response);
11733 xmlFreeNode(request);
11734 #else /* not HAVE_LIBCURL */
11735 err = IE_NOTSUP;
11736 #endif
11738 return err;
11742 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11743 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11744 * be re-signed.
11745 * @context is session context
11746 * @input_data is memory with raw CMS signed message or delivery info bit
11747 * stream to re-sign
11748 * @input_length is @input_data size in bytes
11749 * @output_data is pointer to auto-allocated memory where to store re-signed
11750 * input data blob. Caller must free it.
11751 * @output_data is pointer where to store @output_data size in bytes
11752 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11753 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11754 * @return
11755 * IE_SUCCESS if CMS blob has been re-signed successfully
11756 * other code for other errors */
11757 isds_error isds_resign_message(struct isds_ctx *context,
11758 const void *input_data, size_t input_length,
11759 void **output_data, size_t *output_length, struct tm **valid_to) {
11760 isds_error err = IE_SUCCESS;
11761 #if HAVE_LIBCURL
11762 xmlNsPtr isds_ns = NULL;
11763 xmlNodePtr request = NULL;
11764 xmlDocPtr response = NULL;
11765 xmlXPathContextPtr xpath_ctx = NULL;
11766 xmlXPathObjectPtr result = NULL;
11767 char *string = NULL;
11768 const xmlChar *codes[] = {
11769 BAD_CAST "2200",
11770 BAD_CAST "2201",
11771 BAD_CAST "2204",
11772 BAD_CAST "2207",
11773 NULL
11775 const char *meanings[] = {
11776 "Message is bad",
11777 "Message is not original",
11778 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11779 "Time stamp could not been generated in time"
11781 const isds_error errors[] = {
11782 IE_INVAL,
11783 IE_NOTUNIQ,
11784 IE_INVAL,
11785 IE_ISDS,
11787 struct code_map_isds_error map = {
11788 .codes = codes,
11789 .meanings = meanings,
11790 .errors = errors
11792 #endif
11794 if (NULL != output_data) *output_data = NULL;
11795 if (NULL != output_length) *output_length = 0;
11796 if (NULL != valid_to) *valid_to = NULL;
11798 if (NULL == context) return IE_INVALID_CONTEXT;
11799 zfree(context->long_message);
11800 if (NULL == input_data || 0 == input_length) {
11801 isds_log_message(context, _("Empty CMS blob on input"));
11802 return IE_INVAL;
11804 if (NULL == output_data || NULL == output_length) {
11805 isds_log_message(context,
11806 _("NULL pointer provided for output CMS blob"));
11807 return IE_INVAL;
11810 #if HAVE_LIBCURL
11811 /* Check if connection is established
11812 * TODO: This check should be done downstairs. */
11813 if (!context->curl) return IE_CONNECTION_CLOSED;
11816 /* Build Re-signISDSDocument request */
11817 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11818 if (!request) {
11819 isds_log_message(context,
11820 _("Could not build Re-signISDSDocument request"));
11821 return IE_ERROR;
11823 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11824 if(!isds_ns) {
11825 isds_log_message(context, _("Could not create ISDS name space"));
11826 xmlFreeNode(request);
11827 return IE_ERROR;
11829 xmlSetNs(request, isds_ns);
11831 /* Insert Base64 encoded CMS blob */
11832 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11833 input_data, input_length);
11834 if (err) goto leave;
11836 /* Send request to server and process response */
11837 err = send_destroy_request_check_response(context,
11838 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11839 &response, NULL, &map);
11840 if (err) goto leave;
11843 /* Extract re-signed data */
11844 xpath_ctx = xmlXPathNewContext(response);
11845 if (!xpath_ctx) {
11846 err = IE_ERROR;
11847 goto leave;
11849 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11850 err = IE_ERROR;
11851 goto leave;
11853 result = xmlXPathEvalExpression(
11854 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11855 if (!result) {
11856 err = IE_ERROR;
11857 goto leave;
11859 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11860 isds_log_message(context,
11861 _("Missing Re-signISDSDocumentResponse element"));
11862 err = IE_ISDS;
11863 goto leave;
11865 if (result->nodesetval->nodeNr > 1) {
11866 isds_log_message(context,
11867 _("Multiple Re-signISDSDocumentResponse element"));
11868 err = IE_ISDS;
11869 goto leave;
11871 xpath_ctx->node = result->nodesetval->nodeTab[0];
11872 xmlXPathFreeObject(result); result = NULL;
11874 EXTRACT_STRING("isds:dmResultDoc", string);
11875 /* Decode non-empty data */
11876 if (NULL != string && string[0] != '\0') {
11877 *output_length = _isds_b64decode(string, output_data);
11878 if (*output_length == (size_t) -1) {
11879 isds_log_message(context,
11880 _("Error while Base64-decoding re-signed data"));
11881 err = IE_ERROR;
11882 goto leave;
11884 } else {
11885 isds_log_message(context, _("Server did not send re-signed data"));
11886 err = IE_ISDS;
11887 goto leave;
11889 zfree(string);
11891 if (NULL != valid_to) {
11892 /* Get time stamp expiration date */
11893 EXTRACT_STRING("isds:dmValidTo", string);
11894 if (NULL != string) {
11895 *valid_to = calloc(1, sizeof(**valid_to));
11896 if (!*valid_to) {
11897 err = IE_NOMEM;
11898 goto leave;
11900 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11901 if (err) {
11902 if (err == IE_NOTSUP) {
11903 err = IE_ISDS;
11904 char *string_locale = _isds_utf82locale(string);
11905 isds_printf_message(context,
11906 _("Invalid dmValidTo value: %s"), string_locale);
11907 free(string_locale);
11909 goto leave;
11914 leave:
11915 free(string);
11917 xmlXPathFreeObject(result);
11918 xmlXPathFreeContext(xpath_ctx);
11920 xmlFreeDoc(response);
11921 xmlFreeNode(request);
11922 #else /* not HAVE_LIBCURL */
11923 err = IE_NOTSUP;
11924 #endif
11926 return err;
11929 #undef INSERT_ELEMENT
11930 #undef CHECK_FOR_STRING_LENGTH
11931 #undef INSERT_STRING_ATTRIBUTE
11932 #undef INSERT_ULONGINTNOPTR
11933 #undef INSERT_ULONGINT
11934 #undef INSERT_LONGINT
11935 #undef INSERT_BOOLEAN
11936 #undef INSERT_SCALAR_BOOLEAN
11937 #undef INSERT_STRING
11938 #undef INSERT_STRING_WITH_NS
11939 #undef EXTRACT_STRING_ATTRIBUTE
11940 #undef EXTRACT_ULONGINT
11941 #undef EXTRACT_LONGINT
11942 #undef EXTRACT_BOOLEAN
11943 #undef EXTRACT_STRING
11946 /* Compute hash of message from raw representation and store it into envelope.
11947 * Original hash structure will be destroyed in envelope.
11948 * @context is session context
11949 * @message is message carrying raw XML message blob
11950 * @algorithm is desired hash algorithm to use */
11951 isds_error isds_compute_message_hash(struct isds_ctx *context,
11952 struct isds_message *message, const isds_hash_algorithm algorithm) {
11953 isds_error err = IE_SUCCESS;
11954 const char *nsuri;
11955 void *xml_stream = NULL;
11956 size_t xml_stream_length;
11957 size_t phys_start, phys_end;
11958 char *phys_path = NULL;
11959 struct isds_hash *new_hash = NULL;
11962 if (!context) return IE_INVALID_CONTEXT;
11963 zfree(context->long_message);
11964 if (!message) return IE_INVAL;
11966 if (!message->raw) {
11967 isds_log_message(context,
11968 _("Message does not carry raw representation"));
11969 return IE_INVAL;
11972 switch (message->raw_type) {
11973 case RAWTYPE_INCOMING_MESSAGE:
11974 nsuri = ISDS_NS;
11975 xml_stream = message->raw;
11976 xml_stream_length = message->raw_length;
11977 break;
11979 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11980 nsuri = SISDS_INCOMING_NS;
11981 xml_stream = message->raw;
11982 xml_stream_length = message->raw_length;
11983 break;
11985 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11986 nsuri = SISDS_INCOMING_NS;
11987 err = _isds_extract_cms_data(context,
11988 message->raw, message->raw_length,
11989 &xml_stream, &xml_stream_length);
11990 if (err) goto leave;
11991 break;
11993 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11994 nsuri = SISDS_OUTGOING_NS;
11995 xml_stream = message->raw;
11996 xml_stream_length = message->raw_length;
11997 break;
11999 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
12000 nsuri = SISDS_OUTGOING_NS;
12001 err = _isds_extract_cms_data(context,
12002 message->raw, message->raw_length,
12003 &xml_stream, &xml_stream_length);
12004 if (err) goto leave;
12005 break;
12007 default:
12008 isds_log_message(context, _("Bad raw representation type"));
12009 return IE_INVAL;
12010 break;
12014 /* XXX: Hash is computed from original string representing isds:dmDm
12015 * subtree. That means no encoding, white space, xmlns attributes changes.
12016 * In other words, input for hash can be invalid XML stream. */
12017 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
12018 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
12019 PHYSXML_ELEMENT_SEPARATOR,
12020 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
12021 PHYSXML_ELEMENT_SEPARATOR
12022 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
12023 err = IE_NOMEM;
12024 goto leave;
12026 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
12027 phys_path, &phys_start, &phys_end);
12028 zfree(phys_path);
12029 if (err) {
12030 isds_log_message(context,
12031 _("Substring with isds:dmDM element could not be located "
12032 "in raw message"));
12033 goto leave;
12037 /* Compute hash */
12038 new_hash = calloc(1, sizeof(*new_hash));
12039 if (!new_hash) {
12040 err = IE_NOMEM;
12041 goto leave;
12043 new_hash->algorithm = algorithm;
12044 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
12045 new_hash);
12046 if (err) {
12047 isds_log_message(context, _("Could not compute message hash"));
12048 goto leave;
12051 /* Save computed hash */
12052 if (!message->envelope) {
12053 message->envelope = calloc(1, sizeof(*message->envelope));
12054 if (!message->envelope) {
12055 err = IE_NOMEM;
12056 goto leave;
12059 isds_hash_free(&message->envelope->hash);
12060 message->envelope->hash = new_hash;
12062 leave:
12063 if (err) {
12064 isds_hash_free(&new_hash);
12067 free(phys_path);
12068 if (xml_stream != message->raw) free(xml_stream);
12069 return err;
12073 /* Compare two hashes.
12074 * @h1 is first hash
12075 * @h2 is another hash
12076 * @return
12077 * IE_SUCCESS if hashes equal
12078 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12079 * IE_ENUM if not comparable, but both structures defined
12080 * IE_INVAL if some of the structures are undefined (NULL)
12081 * IE_ERROR if internal error occurs */
12082 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
12083 if (h1 == NULL || h2 == NULL) return IE_INVAL;
12084 if (h1->algorithm != h2->algorithm) return IE_ENUM;
12085 if (h1->length != h2->length) return IE_ERROR;
12086 if (h1->length > 0 && !h1->value) return IE_ERROR;
12087 if (h2->length > 0 && !h2->value) return IE_ERROR;
12089 for (size_t i = 0; i < h1->length; i++) {
12090 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12091 return IE_NOTEQUAL;
12093 return IE_SUCCESS;
12097 /* Check message has gone through ISDS by comparing message hash stored in
12098 * ISDS and locally computed hash. You must provide message with valid raw
12099 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12100 * This is convenient wrapper for isds_download_message_hash(),
12101 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12102 * @context is session context
12103 * @message is message with valid raw and envelope member; envelope->hash
12104 * member will be changed during function run. Use envelope on heap only.
12105 * @return
12106 * IE_SUCCESS if message originates in ISDS
12107 * IE_NOTEQUAL if message is unknown to ISDS
12108 * other code for other errors */
12109 isds_error isds_verify_message_hash(struct isds_ctx *context,
12110 struct isds_message *message) {
12111 isds_error err = IE_SUCCESS;
12112 struct isds_hash *downloaded_hash = NULL;
12114 if (!context) return IE_INVALID_CONTEXT;
12115 zfree(context->long_message);
12116 if (!message) return IE_INVAL;
12118 if (!message->envelope) {
12119 isds_log_message(context,
12120 _("Given message structure is missing envelope"));
12121 return IE_INVAL;
12123 if (!message->raw) {
12124 isds_log_message(context,
12125 _("Given message structure is missing raw representation"));
12126 return IE_INVAL;
12129 err = isds_download_message_hash(context, message->envelope->dmID,
12130 &downloaded_hash);
12131 if (err) goto leave;
12133 err = isds_compute_message_hash(context, message,
12134 downloaded_hash->algorithm);
12135 if (err) goto leave;
12137 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12139 leave:
12140 isds_hash_free(&downloaded_hash);
12141 return err;
12145 /* Search for document by document ID in list of documents. IDs are compared
12146 * as UTF-8 string.
12147 * @documents is list of isds_documents
12148 * @id is document identifier
12149 * @return first matching document or NULL. */
12150 const struct isds_document *isds_find_document_by_id(
12151 const struct isds_list *documents, const char *id) {
12152 const struct isds_list *item;
12153 const struct isds_document *document;
12155 for (item = documents; item; item = item->next) {
12156 document = (struct isds_document *) item->data;
12157 if (!document) continue;
12159 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12160 return document;
12163 return NULL;
12167 /* Normalize @mime_type to be proper MIME type.
12168 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12169 * guess regular MIME type (e.g. "application/pdf").
12170 * @mime_type is UTF-8 encoded MIME type to fix
12171 * @return original @mime_type if no better interpretation exists, or
12172 * constant static UTF-8 encoded string with proper MIME type. */
12173 const char *isds_normalize_mime_type(const char *mime_type) {
12174 if (!mime_type) return NULL;
12176 for (size_t offset = 0;
12177 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12178 offset += 2) {
12179 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12180 extension_map_mime[offset]))
12181 return (const char *) extension_map_mime[offset + 1];
12184 return mime_type;
12188 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12189 struct isds_message **message);
12190 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12191 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12192 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12193 struct isds_address **address);
12195 int isds_message_free(struct isds_message **message);
12196 int isds_address_free(struct isds_address **address);
12200 /* Makes known all relevant namespaces to given XPath context
12201 * @xpath_ctx is XPath context
12202 * @message_ns selects proper message name space. Unsigned and signed
12203 * messages and delivery info's differ in prefix and URI. */
12204 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12205 const message_ns_type message_ns) {
12206 const xmlChar *message_namespace = NULL;
12208 if (!xpath_ctx) return IE_ERROR;
12210 switch(message_ns) {
12211 case MESSAGE_NS_1:
12212 message_namespace = BAD_CAST ISDS1_NS; break;
12213 case MESSAGE_NS_UNSIGNED:
12214 message_namespace = BAD_CAST ISDS_NS; break;
12215 case MESSAGE_NS_SIGNED_INCOMING:
12216 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12217 case MESSAGE_NS_SIGNED_OUTGOING:
12218 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12219 case MESSAGE_NS_SIGNED_DELIVERY:
12220 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12221 default:
12222 return IE_ENUM;
12225 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12226 return IE_ERROR;
12227 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12228 return IE_ERROR;
12229 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12230 return IE_ERROR;
12231 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12232 return IE_ERROR;
12233 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12234 return IE_ERROR;
12235 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12236 return IE_ERROR;
12237 return IE_SUCCESS;