Do not export _isds_delete_box_common()_ function
[libisds.git] / src / isds.c
blobb53116c28fdb1f3a9e58a0ea0491db33418b28f3
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;
3159 if (!context) return IE_INVALID_CONTEXT;
3160 if (!owner || !db_owner_info) return IE_INVAL;
3163 /* Build XSD:tDbOwnerInfo */
3164 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3165 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3167 /* dbType */
3168 if (owner->dbType) {
3169 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
3170 if (!type_string) {
3171 isds_printf_message(context, _("Invalid dbType value: %d"),
3172 *(owner->dbType));
3173 err = IE_ENUM;
3174 goto leave;
3176 INSERT_STRING(db_owner_info, "dbType", type_string);
3178 INSERT_STRING(db_owner_info, "ic", owner->ic);
3179 if (owner->personName) {
3180 INSERT_STRING(db_owner_info, "pnFirstName",
3181 owner->personName->pnFirstName);
3182 INSERT_STRING(db_owner_info, "pnMiddleName",
3183 owner->personName->pnMiddleName);
3184 INSERT_STRING(db_owner_info, "pnLastName",
3185 owner->personName->pnLastName);
3186 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3187 owner->personName->pnLastNameAtBirth);
3189 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3190 if (owner->birthInfo) {
3191 if (owner->birthInfo->biDate) {
3192 if (!tm2datestring(owner->birthInfo->biDate, &string))
3193 INSERT_STRING(db_owner_info, "biDate", string);
3194 free(string); string = NULL;
3196 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
3197 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
3198 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
3200 if (owner->address) {
3201 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
3202 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
3203 INSERT_STRING(db_owner_info, "adNumberInStreet",
3204 owner->address->adNumberInStreet);
3205 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3206 owner->address->adNumberInMunicipality);
3207 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
3208 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
3210 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3211 INSERT_STRING(db_owner_info, "email", owner->email);
3212 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3214 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3215 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3217 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3218 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3220 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3222 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3223 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3224 owner->dbOpenAddressing);
3226 leave:
3227 free(string);
3228 return err;
3232 /* Convert XSD:tDbUserInfo XML tree into structure
3233 * @context is ISDS context
3234 * @db_user_info is automatically reallocated user info structure
3235 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3236 * In case of error @db_user_info will be freed. */
3237 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3238 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3239 isds_error err = IE_SUCCESS;
3240 xmlXPathObjectPtr result = NULL;
3241 char *string = NULL;
3243 if (!context) return IE_INVALID_CONTEXT;
3244 if (!db_user_info) return IE_INVAL;
3245 isds_DbUserInfo_free(db_user_info);
3246 if (!xpath_ctx) return IE_INVAL;
3249 *db_user_info = calloc(1, sizeof(**db_user_info));
3250 if (!*db_user_info) {
3251 err = IE_NOMEM;
3252 goto leave;
3255 EXTRACT_STRING_ATTRIBUTE("AIFOTicket", (*db_user_info)->aifo_ticket, 0);
3257 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3259 EXTRACT_STRING("isds:userType", string);
3260 if (string) {
3261 (*db_user_info)->userType =
3262 calloc(1, sizeof(*((*db_user_info)->userType)));
3263 if (!(*db_user_info)->userType) {
3264 err = IE_NOMEM;
3265 goto leave;
3267 err = string2isds_UserType((xmlChar *)string,
3268 (*db_user_info)->userType);
3269 if (err) {
3270 zfree((*db_user_info)->userType);
3271 if (err == IE_ENUM) {
3272 err = IE_ISDS;
3273 char *string_locale = _isds_utf82locale(string);
3274 isds_printf_message(context,
3275 _("Unknown isds:userType value: %s"), string_locale);
3276 free(string_locale);
3278 goto leave;
3280 zfree(string);
3283 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3285 (*db_user_info)->personName =
3286 calloc(1, sizeof(*((*db_user_info)->personName)));
3287 if (!(*db_user_info)->personName) {
3288 err = IE_NOMEM;
3289 goto leave;
3292 err = extract_gPersonName(context, &(*db_user_info)->personName,
3293 xpath_ctx);
3294 if (err) goto leave;
3296 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3297 if (err) goto leave;
3299 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3300 if (err) goto leave;
3302 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3303 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3305 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3306 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3307 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3309 /* ???: Default value is "CZ" according specification. Should we provide
3310 * it? */
3311 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3313 leave:
3314 if (err) isds_DbUserInfo_free(db_user_info);
3315 free(string);
3316 xmlXPathFreeObject(result);
3317 return err;
3321 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3322 * @context is session context
3323 * @user is libisds structure with user description
3324 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
3325 * @db_user_info is XML element of XSD:tDbUserInfo */
3326 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3327 const struct isds_DbUserInfo *user, _Bool honor_aifo_ticket,
3328 xmlNodePtr db_user_info) {
3330 isds_error err = IE_SUCCESS;
3331 xmlNodePtr node;
3332 xmlAttrPtr attribute_node;
3333 xmlChar *string = NULL;
3335 if (!context) return IE_INVALID_CONTEXT;
3336 if (!user || !db_user_info) return IE_INVAL;
3338 /* Build XSD:tDbUserInfo */
3340 /* XXX: Insert AIFOTicket attribute only when allowed. XML schema does not
3341 * allow it everywhere. */
3342 if (honor_aifo_ticket && user->aifo_ticket) {
3343 INSERT_STRING_ATTRIBUTE(db_user_info, "AIFOTicket", user->aifo_ticket);
3346 if (user->personName) {
3347 INSERT_STRING(db_user_info, "pnFirstName",
3348 user->personName->pnFirstName);
3349 INSERT_STRING(db_user_info, "pnMiddleName",
3350 user->personName->pnMiddleName);
3351 INSERT_STRING(db_user_info, "pnLastName",
3352 user->personName->pnLastName);
3353 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3354 user->personName->pnLastNameAtBirth);
3356 if (user->address) {
3357 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3358 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3359 INSERT_STRING(db_user_info, "adNumberInStreet",
3360 user->address->adNumberInStreet);
3361 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3362 user->address->adNumberInMunicipality);
3363 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3364 INSERT_STRING(db_user_info, "adState", user->address->adState);
3366 if (user->biDate) {
3367 if (!tm2datestring(user->biDate, &string))
3368 INSERT_STRING(db_user_info, "biDate", string);
3369 zfree(string);
3371 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3372 INSERT_STRING(db_user_info, "userID", user->userID);
3374 /* userType */
3375 if (user->userType) {
3376 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3377 if (!type_string) {
3378 isds_printf_message(context, _("Invalid userType value: %d"),
3379 *(user->userType));
3380 err = IE_ENUM;
3381 goto leave;
3383 INSERT_STRING(db_user_info, "userType", type_string);
3386 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3387 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3388 INSERT_STRING(db_user_info, "ic", user->ic);
3389 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3390 INSERT_STRING(db_user_info, "firmName", user->firmName);
3391 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3392 INSERT_STRING(db_user_info, "caCity", user->caCity);
3393 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3394 INSERT_STRING(db_user_info, "caState", user->caState);
3396 leave:
3397 free(string);
3398 return err;
3402 /* Convert XSD:tPDZRec XML tree into structure
3403 * @context is ISDS context
3404 * @permission is automatically reallocated commercial permission structure
3405 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3406 * In case of error @permission will be freed. */
3407 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3408 struct isds_commercial_permission **permission,
3409 xmlXPathContextPtr xpath_ctx) {
3410 isds_error err = IE_SUCCESS;
3411 xmlXPathObjectPtr result = NULL;
3412 char *string = NULL;
3414 if (!context) return IE_INVALID_CONTEXT;
3415 if (!permission) return IE_INVAL;
3416 isds_commercial_permission_free(permission);
3417 if (!xpath_ctx) return IE_INVAL;
3420 *permission = calloc(1, sizeof(**permission));
3421 if (!*permission) {
3422 err = IE_NOMEM;
3423 goto leave;
3426 EXTRACT_STRING("isds:PDZType", string);
3427 if (string) {
3428 err = string2isds_payment_type((xmlChar *)string,
3429 &(*permission)->type);
3430 if (err) {
3431 if (err == IE_ENUM) {
3432 err = IE_ISDS;
3433 char *string_locale = _isds_utf82locale(string);
3434 isds_printf_message(context,
3435 _("Unknown isds:PDZType value: %s"), string_locale);
3436 free(string_locale);
3438 goto leave;
3440 zfree(string);
3443 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3444 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3446 EXTRACT_STRING("isds:PDZExpire", string);
3447 if (string) {
3448 err = timestring2timeval((xmlChar *) string,
3449 &((*permission)->expiration));
3450 if (err) {
3451 char *string_locale = _isds_utf82locale(string);
3452 if (err == IE_DATE) err = IE_ISDS;
3453 isds_printf_message(context,
3454 _("Could not convert PDZExpire as ISO time: %s"),
3455 string_locale);
3456 free(string_locale);
3457 goto leave;
3459 zfree(string);
3462 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3463 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3465 leave:
3466 if (err) isds_commercial_permission_free(permission);
3467 free(string);
3468 xmlXPathFreeObject(result);
3469 return err;
3473 /* Convert XSD:tCiRecord XML tree into structure
3474 * @context is ISDS context
3475 * @event is automatically reallocated commercial credit event structure
3476 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3477 * In case of error @event will be freed. */
3478 static isds_error extract_CiRecord(struct isds_ctx *context,
3479 struct isds_credit_event **event,
3480 xmlXPathContextPtr xpath_ctx) {
3481 isds_error err = IE_SUCCESS;
3482 xmlXPathObjectPtr result = NULL;
3483 char *string = NULL;
3484 long int *number_ptr;
3486 if (!context) return IE_INVALID_CONTEXT;
3487 if (!event) return IE_INVAL;
3488 isds_credit_event_free(event);
3489 if (!xpath_ctx) return IE_INVAL;
3492 *event = calloc(1, sizeof(**event));
3493 if (!*event) {
3494 err = IE_NOMEM;
3495 goto leave;
3498 EXTRACT_STRING("isds:ciEventTime", string);
3499 if (string) {
3500 err = timestring2timeval((xmlChar *) string,
3501 &(*event)->time);
3502 if (err) {
3503 char *string_locale = _isds_utf82locale(string);
3504 if (err == IE_DATE) err = IE_ISDS;
3505 isds_printf_message(context,
3506 _("Could not convert ciEventTime as ISO time: %s"),
3507 string_locale);
3508 free(string_locale);
3509 goto leave;
3511 zfree(string);
3514 EXTRACT_STRING("isds:ciEventType", string);
3515 if (string) {
3516 err = string2isds_credit_event_type((xmlChar *)string,
3517 &(*event)->type);
3518 if (err) {
3519 if (err == IE_ENUM) {
3520 err = IE_ISDS;
3521 char *string_locale = _isds_utf82locale(string);
3522 isds_printf_message(context,
3523 _("Unknown isds:ciEventType value: %s"), string_locale);
3524 free(string_locale);
3526 goto leave;
3528 zfree(string);
3531 number_ptr = &((*event)->credit_change);
3532 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3533 number_ptr = &(*event)->new_credit;
3534 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3536 switch((*event)->type) {
3537 case ISDS_CREDIT_CHARGED:
3538 EXTRACT_STRING("isds:ciTransID",
3539 (*event)->details.charged.transaction);
3540 break;
3541 case ISDS_CREDIT_DISCHARGED:
3542 EXTRACT_STRING("isds:ciTransID",
3543 (*event)->details.discharged.transaction);
3544 break;
3545 case ISDS_CREDIT_MESSAGE_SENT:
3546 EXTRACT_STRING("isds:ciRecipientID",
3547 (*event)->details.message_sent.recipient);
3548 EXTRACT_STRING("isds:ciPDZID",
3549 (*event)->details.message_sent.message_id);
3550 break;
3551 case ISDS_CREDIT_STORAGE_SET:
3552 number_ptr = &((*event)->details.storage_set.new_capacity);
3553 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3554 EXTRACT_DATE("isds:ciNewFrom",
3555 (*event)->details.storage_set.new_valid_from);
3556 EXTRACT_DATE("isds:ciNewTo",
3557 (*event)->details.storage_set.new_valid_to);
3558 EXTRACT_LONGINT("isds:ciOldCapacity",
3559 (*event)->details.storage_set.old_capacity, 0);
3560 EXTRACT_DATE("isds:ciOldFrom",
3561 (*event)->details.storage_set.old_valid_from);
3562 EXTRACT_DATE("isds:ciOldTo",
3563 (*event)->details.storage_set.old_valid_to);
3564 EXTRACT_STRING("isds:ciDoneBy",
3565 (*event)->details.storage_set.initiator);
3566 break;
3567 case ISDS_CREDIT_EXPIRED:
3568 break;
3571 leave:
3572 if (err) isds_credit_event_free(event);
3573 free(string);
3574 xmlXPathFreeObject(result);
3575 return err;
3579 #endif /* HAVE_LIBCURL */
3582 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3583 * isds_envelope structure. The envelope is automatically allocated but not
3584 * reallocated. The date are just appended into envelope structure.
3585 * @context is ISDS context
3586 * @envelope is automatically allocated message envelope structure
3587 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3588 * In case of error @envelope will be freed. */
3589 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3590 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3591 isds_error err = IE_SUCCESS;
3592 xmlXPathObjectPtr result = NULL;
3594 if (!context) return IE_INVALID_CONTEXT;
3595 if (!envelope) return IE_INVAL;
3596 if (!xpath_ctx) return IE_INVAL;
3599 if (!*envelope) {
3600 /* Allocate envelope */
3601 *envelope = calloc(1, sizeof(**envelope));
3602 if (!*envelope) {
3603 err = IE_NOMEM;
3604 goto leave;
3606 } else {
3607 /* Else free former data */
3608 zfree((*envelope)->dmSenderOrgUnit);
3609 zfree((*envelope)->dmSenderOrgUnitNum);
3610 zfree((*envelope)->dbIDRecipient);
3611 zfree((*envelope)->dmRecipientOrgUnit);
3612 zfree((*envelope)->dmRecipientOrgUnitNum);
3613 zfree((*envelope)->dmToHands);
3614 zfree((*envelope)->dmAnnotation);
3615 zfree((*envelope)->dmRecipientRefNumber);
3616 zfree((*envelope)->dmSenderRefNumber);
3617 zfree((*envelope)->dmRecipientIdent);
3618 zfree((*envelope)->dmSenderIdent);
3619 zfree((*envelope)->dmLegalTitleLaw);
3620 zfree((*envelope)->dmLegalTitleYear);
3621 zfree((*envelope)->dmLegalTitleSect);
3622 zfree((*envelope)->dmLegalTitlePar);
3623 zfree((*envelope)->dmLegalTitlePoint);
3624 zfree((*envelope)->dmPersonalDelivery);
3625 zfree((*envelope)->dmAllowSubstDelivery);
3628 /* Extract envelope elements added by sender or ISDS
3629 * (XSD: gMessageEnvelopeSub type) */
3630 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3631 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3632 (*envelope)->dmSenderOrgUnitNum, 0);
3633 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3634 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3635 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3636 (*envelope)->dmRecipientOrgUnitNum, 0);
3637 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3638 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3639 EXTRACT_STRING("isds:dmRecipientRefNumber",
3640 (*envelope)->dmRecipientRefNumber);
3641 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3642 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3643 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3645 /* Extract envelope elements regarding law reference */
3646 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3647 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3648 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3649 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3650 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3652 /* Extract envelope other elements */
3653 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3654 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3655 (*envelope)->dmAllowSubstDelivery);
3657 leave:
3658 if (err) isds_envelope_free(envelope);
3659 xmlXPathFreeObject(result);
3660 return err;
3665 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3666 * isds_envelope structure. The envelope is automatically allocated but not
3667 * reallocated. The date are just appended into envelope structure.
3668 * @context is ISDS context
3669 * @envelope is automatically allocated message envelope structure
3670 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3671 * In case of error @envelope will be freed. */
3672 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3673 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3674 isds_error err = IE_SUCCESS;
3675 xmlXPathObjectPtr result = NULL;
3677 if (!context) return IE_INVALID_CONTEXT;
3678 if (!envelope) return IE_INVAL;
3679 if (!xpath_ctx) return IE_INVAL;
3682 if (!*envelope) {
3683 /* Allocate envelope */
3684 *envelope = calloc(1, sizeof(**envelope));
3685 if (!*envelope) {
3686 err = IE_NOMEM;
3687 goto leave;
3689 } else {
3690 /* Else free former data */
3691 zfree((*envelope)->dmID);
3692 zfree((*envelope)->dbIDSender);
3693 zfree((*envelope)->dmSender);
3694 zfree((*envelope)->dmSenderAddress);
3695 zfree((*envelope)->dmSenderType);
3696 zfree((*envelope)->dmRecipient);
3697 zfree((*envelope)->dmRecipientAddress);
3698 zfree((*envelope)->dmAmbiguousRecipient);
3701 /* Extract envelope elements added by ISDS
3702 * (XSD: gMessageEnvelope type) */
3703 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3704 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3705 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3706 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3707 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3708 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3709 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3710 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3711 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3712 (*envelope)->dmAmbiguousRecipient);
3714 /* Extract envelope elements added by sender and ISDS
3715 * (XSD: gMessageEnvelope type) */
3716 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3717 if (err) goto leave;
3719 leave:
3720 if (err) isds_envelope_free(envelope);
3721 xmlXPathFreeObject(result);
3722 return err;
3726 /* Convert other envelope elements from XML tree into isds_envelope structure:
3727 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3728 * The envelope is automatically allocated but not reallocated.
3729 * The data are just appended into envelope structure.
3730 * @context is ISDS context
3731 * @envelope is automatically allocated message envelope structure
3732 * @xpath_ctx is XPath context with current node as parent desired elements
3733 * In case of error @envelope will be freed. */
3734 static isds_error append_status_size_times(struct isds_ctx *context,
3735 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3736 isds_error err = IE_SUCCESS;
3737 xmlXPathObjectPtr result = NULL;
3738 char *string = NULL;
3739 unsigned long int *unumber = NULL;
3741 if (!context) return IE_INVALID_CONTEXT;
3742 if (!envelope) return IE_INVAL;
3743 if (!xpath_ctx) return IE_INVAL;
3746 if (!*envelope) {
3747 /* Allocate new */
3748 *envelope = calloc(1, sizeof(**envelope));
3749 if (!*envelope) {
3750 err = IE_NOMEM;
3751 goto leave;
3753 } else {
3754 /* Free old data */
3755 zfree((*envelope)->dmMessageStatus);
3756 zfree((*envelope)->dmAttachmentSize);
3757 zfree((*envelope)->dmDeliveryTime);
3758 zfree((*envelope)->dmAcceptanceTime);
3762 /* dmMessageStatus element is mandatory */
3763 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3764 if (!unumber) {
3765 isds_log_message(context,
3766 _("Missing mandatory sisds:dmMessageStatus integer"));
3767 err = IE_ISDS;
3768 goto leave;
3770 err = uint2isds_message_status(context, unumber,
3771 &((*envelope)->dmMessageStatus));
3772 if (err) {
3773 if (err == IE_ENUM) err = IE_ISDS;
3774 goto leave;
3776 free(unumber); unumber = NULL;
3778 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3781 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3782 if (string) {
3783 err = timestring2timeval((xmlChar *) string,
3784 &((*envelope)->dmDeliveryTime));
3785 if (err) {
3786 char *string_locale = _isds_utf82locale(string);
3787 if (err == IE_DATE) err = IE_ISDS;
3788 isds_printf_message(context,
3789 _("Could not convert dmDeliveryTime as ISO time: %s"),
3790 string_locale);
3791 free(string_locale);
3792 goto leave;
3794 zfree(string);
3797 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3798 if (string) {
3799 err = timestring2timeval((xmlChar *) string,
3800 &((*envelope)->dmAcceptanceTime));
3801 if (err) {
3802 char *string_locale = _isds_utf82locale(string);
3803 if (err == IE_DATE) err = IE_ISDS;
3804 isds_printf_message(context,
3805 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3806 string_locale);
3807 free(string_locale);
3808 goto leave;
3810 zfree(string);
3813 leave:
3814 if (err) isds_envelope_free(envelope);
3815 free(unumber);
3816 free(string);
3817 xmlXPathFreeObject(result);
3818 return err;
3822 /* Convert message type attribute of current element into isds_envelope
3823 * structure.
3824 * TODO: This function can be incorporated into append_status_size_times() as
3825 * they are called always together.
3826 * The envelope is automatically allocated but not reallocated.
3827 * The data are just appended into envelope structure.
3828 * @context is ISDS context
3829 * @envelope is automatically allocated message envelope structure
3830 * @xpath_ctx is XPath context with current node as parent of attribute
3831 * carrying message type
3832 * In case of error @envelope will be freed. */
3833 static isds_error append_message_type(struct isds_ctx *context,
3834 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3835 isds_error err = IE_SUCCESS;
3837 if (!context) return IE_INVALID_CONTEXT;
3838 if (!envelope) return IE_INVAL;
3839 if (!xpath_ctx) return IE_INVAL;
3842 if (!*envelope) {
3843 /* Allocate new */
3844 *envelope = calloc(1, sizeof(**envelope));
3845 if (!*envelope) {
3846 err = IE_NOMEM;
3847 goto leave;
3849 } else {
3850 /* Free old data */
3851 zfree((*envelope)->dmType);
3855 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3857 if (!(*envelope)->dmType) {
3858 /* Use default value */
3859 (*envelope)->dmType = strdup("V");
3860 if (!(*envelope)->dmType) {
3861 err = IE_NOMEM;
3862 goto leave;
3864 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3865 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3866 isds_printf_message(context,
3867 _("Message type in dmType attribute is not 1 character long: "
3868 "%s"),
3869 type_locale);
3870 free(type_locale);
3871 err = IE_ISDS;
3872 goto leave;
3875 leave:
3876 if (err) isds_envelope_free(envelope);
3877 return err;
3881 #if HAVE_LIBCURL
3882 /* Convert dmType isds_envelope member into XML attribute and append it to
3883 * current node.
3884 * @context is ISDS context
3885 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3886 * @dm_envelope is XML element the resulting attribute will be appended to.
3887 * @return error code, in case of error context' message is filled. */
3888 static isds_error insert_message_type(struct isds_ctx *context,
3889 const char *type, xmlNodePtr dm_envelope) {
3890 isds_error err = IE_SUCCESS;
3891 xmlAttrPtr attribute_node;
3893 if (!context) return IE_INVALID_CONTEXT;
3894 if (!dm_envelope) return IE_INVAL;
3896 /* Insert optional message type */
3897 if (type) {
3898 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3899 char *type_locale = _isds_utf82locale(type);
3900 isds_printf_message(context,
3901 _("Message type in envelope is not 1 character long: %s"),
3902 type_locale);
3903 free(type_locale);
3904 err = IE_INVAL;
3905 goto leave;
3907 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3910 leave:
3911 return err;
3913 #endif /* HAVE_LIBCURL */
3916 /* Extract message document into reallocated document structure
3917 * @context is ISDS context
3918 * @document is automatically reallocated message documents structure
3919 * @xpath_ctx is XPath context with current node as isds:dmFile
3920 * In case of error @document will be freed. */
3921 static isds_error extract_document(struct isds_ctx *context,
3922 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3923 isds_error err = IE_SUCCESS;
3924 xmlXPathObjectPtr result = NULL;
3925 xmlNodePtr file_node;
3926 char *string = NULL;
3928 if (!context) return IE_INVALID_CONTEXT;
3929 if (!document) return IE_INVAL;
3930 isds_document_free(document);
3931 if (!xpath_ctx) return IE_INVAL;
3932 file_node = xpath_ctx->node;
3934 *document = calloc(1, sizeof(**document));
3935 if (!*document) {
3936 err = IE_NOMEM;
3937 goto leave;
3940 /* Extract document meta data */
3941 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3942 if (context->normalize_mime_type) {
3943 const char *normalized_type =
3944 isds_normalize_mime_type((*document)->dmMimeType);
3945 if (NULL != normalized_type &&
3946 normalized_type != (*document)->dmMimeType) {
3947 char *new_type = strdup(normalized_type);
3948 if (NULL == new_type) {
3949 isds_printf_message(context,
3950 _("Not enough memory to normalize document MIME type"));
3951 err = IE_NOMEM;
3952 goto leave;
3954 free((*document)->dmMimeType);
3955 (*document)->dmMimeType = new_type;
3959 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3960 err = string2isds_FileMetaType((xmlChar*)string,
3961 &((*document)->dmFileMetaType));
3962 if (err) {
3963 char *meta_type_locale = _isds_utf82locale(string);
3964 isds_printf_message(context,
3965 _("Document has invalid dmFileMetaType attribute value: %s"),
3966 meta_type_locale);
3967 free(meta_type_locale);
3968 err = IE_ISDS;
3969 goto leave;
3971 zfree(string);
3973 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3974 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3975 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3976 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3979 /* Extract document data.
3980 * Base64 encoded blob or XML subtree must be presented. */
3982 /* Check for dmEncodedContent */
3983 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3984 xpath_ctx);
3985 if (!result) {
3986 err = IE_XML;
3987 goto leave;
3990 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3991 /* Here we have Base64 blob */
3992 (*document)->is_xml = 0;
3994 if (result->nodesetval->nodeNr > 1) {
3995 isds_printf_message(context,
3996 _("Document has more dmEncodedContent elements"));
3997 err = IE_ISDS;
3998 goto leave;
4001 xmlXPathFreeObject(result); result = NULL;
4002 EXTRACT_STRING("isds:dmEncodedContent", string);
4004 /* Decode non-empty document */
4005 if (string && string[0] != '\0') {
4006 (*document)->data_length =
4007 _isds_b64decode(string, &((*document)->data));
4008 if ((*document)->data_length == (size_t) -1) {
4009 isds_printf_message(context,
4010 _("Error while Base64-decoding document content"));
4011 err = IE_ERROR;
4012 goto leave;
4015 } else {
4016 /* No Base64 blob, try XML document */
4017 xmlXPathFreeObject(result); result = NULL;
4018 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
4019 xpath_ctx);
4020 if (!result) {
4021 err = IE_XML;
4022 goto leave;
4025 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4026 /* Here we have XML document */
4027 (*document)->is_xml = 1;
4029 if (result->nodesetval->nodeNr > 1) {
4030 isds_printf_message(context,
4031 _("Document has more dmXMLContent elements"));
4032 err = IE_ISDS;
4033 goto leave;
4036 /* XXX: We cannot serialize the content simply because:
4037 * - XML document may point out of its scope (e.g. to message
4038 * envelope)
4039 * - isds:dmXMLContent can contain more elements, no element,
4040 * a text node only
4041 * - it's not the XML way
4042 * Thus we provide the only right solution: XML DOM. Let's
4043 * application to cope with this hot potato :) */
4044 (*document)->xml_node_list =
4045 result->nodesetval->nodeTab[0]->children;
4046 } else {
4047 /* No base64 blob, nor XML document */
4048 isds_printf_message(context,
4049 _("Document has no dmEncodedContent, nor dmXMLContent "
4050 "element"));
4051 err = IE_ISDS;
4052 goto leave;
4057 leave:
4058 if (err) isds_document_free(document);
4059 free(string);
4060 xmlXPathFreeObject(result);
4061 xpath_ctx->node = file_node;
4062 return err;
4067 /* Extract message documents into reallocated list of documents
4068 * @context is ISDS context
4069 * @documents is automatically reallocated message documents list structure
4070 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4071 * In case of error @documents will be freed. */
4072 static isds_error extract_documents(struct isds_ctx *context,
4073 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
4074 isds_error err = IE_SUCCESS;
4075 xmlXPathObjectPtr result = NULL;
4076 xmlNodePtr files_node;
4077 struct isds_list *document, *prev_document = NULL;
4079 if (!context) return IE_INVALID_CONTEXT;
4080 if (!documents) return IE_INVAL;
4081 isds_list_free(documents);
4082 if (!xpath_ctx) return IE_INVAL;
4083 files_node = xpath_ctx->node;
4085 /* Find documents */
4086 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
4087 if (!result) {
4088 err = IE_XML;
4089 goto leave;
4092 /* No match */
4093 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4094 isds_printf_message(context,
4095 _("Message does not contain any document"));
4096 err = IE_ISDS;
4097 goto leave;
4101 /* Iterate over documents */
4102 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4104 /* Allocate and append list item */
4105 document = calloc(1, sizeof(*document));
4106 if (!document) {
4107 err = IE_NOMEM;
4108 goto leave;
4110 document->destructor = (void (*)(void **))isds_document_free;
4111 if (i == 0) *documents = document;
4112 else prev_document->next = document;
4113 prev_document = document;
4115 /* Extract document */
4116 xpath_ctx->node = result->nodesetval->nodeTab[i];
4117 err = extract_document(context,
4118 (struct isds_document **) &(document->data), xpath_ctx);
4119 if (err) goto leave;
4123 leave:
4124 if (err) isds_list_free(documents);
4125 xmlXPathFreeObject(result);
4126 xpath_ctx->node = files_node;
4127 return err;
4131 #if HAVE_LIBCURL
4132 /* Convert isds:dmRecord XML tree into structure
4133 * @context is ISDS context
4134 * @envelope is automatically reallocated message envelope structure
4135 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4136 * In case of error @envelope will be freed. */
4137 static isds_error extract_DmRecord(struct isds_ctx *context,
4138 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4139 isds_error err = IE_SUCCESS;
4140 xmlXPathObjectPtr result = NULL;
4142 if (!context) return IE_INVALID_CONTEXT;
4143 if (!envelope) return IE_INVAL;
4144 isds_envelope_free(envelope);
4145 if (!xpath_ctx) return IE_INVAL;
4148 *envelope = calloc(1, sizeof(**envelope));
4149 if (!*envelope) {
4150 err = IE_NOMEM;
4151 goto leave;
4155 /* Extract tRecord data */
4156 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4158 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4159 * dmAcceptanceTime. */
4160 err = append_status_size_times(context, envelope, xpath_ctx);
4161 if (err) goto leave;
4163 /* Extract envelope elements added by sender and ISDS
4164 * (XSD: gMessageEnvelope type) */
4165 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4166 if (err) goto leave;
4168 /* Get message type */
4169 err = append_message_type(context, envelope, xpath_ctx);
4170 if (err) goto leave;
4173 leave:
4174 if (err) isds_envelope_free(envelope);
4175 xmlXPathFreeObject(result);
4176 return err;
4180 /* Convert XSD:tStateChangesRecord type XML tree into structure
4181 * @context is ISDS context
4182 * @changed_status is automatically reallocated message state change structure
4183 * @xpath_ctx is XPath context with current node as element of
4184 * XSD:tStateChangesRecord type
4185 * In case of error @changed_status will be freed. */
4186 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4187 struct isds_message_status_change **changed_status,
4188 xmlXPathContextPtr xpath_ctx) {
4189 isds_error err = IE_SUCCESS;
4190 xmlXPathObjectPtr result = NULL;
4191 unsigned long int *unumber = NULL;
4192 char *string = NULL;
4194 if (!context) return IE_INVALID_CONTEXT;
4195 if (!changed_status) return IE_INVAL;
4196 isds_message_status_change_free(changed_status);
4197 if (!xpath_ctx) return IE_INVAL;
4200 *changed_status = calloc(1, sizeof(**changed_status));
4201 if (!*changed_status) {
4202 err = IE_NOMEM;
4203 goto leave;
4207 /* Extract tGetStateChangesInput data */
4208 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4210 /* dmEventTime is mandatory */
4211 EXTRACT_STRING("isds:dmEventTime", string);
4212 if (string) {
4213 err = timestring2timeval((xmlChar *) string,
4214 &((*changed_status)->time));
4215 if (err) {
4216 char *string_locale = _isds_utf82locale(string);
4217 if (err == IE_DATE) err = IE_ISDS;
4218 isds_printf_message(context,
4219 _("Could not convert dmEventTime as ISO time: %s"),
4220 string_locale);
4221 free(string_locale);
4222 goto leave;
4224 zfree(string);
4227 /* dmMessageStatus element is mandatory */
4228 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4229 if (!unumber) {
4230 isds_log_message(context,
4231 _("Missing mandatory isds:dmMessageStatus integer"));
4232 err = IE_ISDS;
4233 goto leave;
4235 err = uint2isds_message_status(context, unumber,
4236 &((*changed_status)->dmMessageStatus));
4237 if (err) {
4238 if (err == IE_ENUM) err = IE_ISDS;
4239 goto leave;
4241 zfree(unumber);
4244 leave:
4245 free(unumber);
4246 free(string);
4247 if (err) isds_message_status_change_free(changed_status);
4248 xmlXPathFreeObject(result);
4249 return err;
4251 #endif /* HAVE_LIBCURL */
4254 /* Find and convert isds:dmHash XML tree into structure
4255 * @context is ISDS context
4256 * @envelope is automatically reallocated message hash structure
4257 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4258 * In case of error @hash will be freed. */
4259 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4260 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4261 isds_error err = IE_SUCCESS;
4262 xmlNodePtr old_ctx_node;
4263 xmlXPathObjectPtr result = NULL;
4264 char *string = NULL;
4266 if (!context) return IE_INVALID_CONTEXT;
4267 if (!hash) return IE_INVAL;
4268 isds_hash_free(hash);
4269 if (!xpath_ctx) return IE_INVAL;
4271 old_ctx_node = xpath_ctx->node;
4273 *hash = calloc(1, sizeof(**hash));
4274 if (!*hash) {
4275 err = IE_NOMEM;
4276 goto leave;
4279 /* Locate dmHash */
4280 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4281 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4282 err = IE_ISDS;
4283 goto leave;
4285 if (err) {
4286 err = IE_ERROR;
4287 goto leave;
4290 /* Get hash algorithm */
4291 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4292 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4293 if (err) {
4294 if (err == IE_ENUM) {
4295 char *string_locale = _isds_utf82locale(string);
4296 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4297 string_locale);
4298 free(string_locale);
4300 goto leave;
4302 zfree(string);
4304 /* Get hash value */
4305 EXTRACT_STRING(".", string);
4306 if (!string) {
4307 isds_printf_message(context,
4308 _("sisds:dmHash element is missing hash value"));
4309 err = IE_ISDS;
4310 goto leave;
4312 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4313 if ((*hash)->length == (size_t) -1) {
4314 isds_printf_message(context,
4315 _("Error while Base64-decoding hash value"));
4316 err = IE_ERROR;
4317 goto leave;
4320 leave:
4321 if (err) isds_hash_free(hash);
4322 free(string);
4323 xmlXPathFreeObject(result);
4324 xpath_ctx->node = old_ctx_node;
4325 return err;
4329 /* Find and append isds:dmQTimestamp XML tree into envelope.
4330 * Because one service is allowed to miss time-stamp content, and we think
4331 * other could too (flaw in specification), this function is deliberated and
4332 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4333 * @context is ISDS context
4334 * @envelope is automatically allocated envelope structure
4335 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4336 * child
4337 * In case of error @envelope will be freed. */
4338 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4339 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4340 isds_error err = IE_SUCCESS;
4341 xmlXPathObjectPtr result = NULL;
4342 char *string = NULL;
4344 if (!context) return IE_INVALID_CONTEXT;
4345 if (!envelope) return IE_INVAL;
4346 if (!xpath_ctx) {
4347 isds_envelope_free(envelope);
4348 return IE_INVAL;
4351 if (!*envelope) {
4352 *envelope = calloc(1, sizeof(**envelope));
4353 if (!*envelope) {
4354 err = IE_NOMEM;
4355 goto leave;
4357 } else {
4358 zfree((*envelope)->timestamp);
4359 (*envelope)->timestamp_length = 0;
4362 /* Get dmQTimestamp */
4363 EXTRACT_STRING("sisds:dmQTimestamp", string);
4364 if (!string) {
4365 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4366 goto leave;
4368 (*envelope)->timestamp_length =
4369 _isds_b64decode(string, &((*envelope)->timestamp));
4370 if ((*envelope)->timestamp_length == (size_t) -1) {
4371 isds_printf_message(context,
4372 _("Error while Base64-decoding time stamp value"));
4373 err = IE_ERROR;
4374 goto leave;
4377 leave:
4378 if (err) isds_envelope_free(envelope);
4379 free(string);
4380 xmlXPathFreeObject(result);
4381 return err;
4385 /* Convert XSD tReturnedMessage XML tree into message structure.
4386 * It does not store serialized XML tree into message->raw.
4387 * It does store (pointer to) parsed XML tree into message->xml if needed.
4388 * @context is ISDS context
4389 * @include_documents Use true if documents must be extracted
4390 * (tReturnedMessage XSD type), use false if documents shall be omitted
4391 * (tReturnedMessageEnvelope).
4392 * @message is automatically reallocated message structure
4393 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4394 * type
4395 * In case of error @message will be freed. */
4396 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4397 const _Bool include_documents, struct isds_message **message,
4398 xmlXPathContextPtr xpath_ctx) {
4399 isds_error err = IE_SUCCESS;
4400 xmlNodePtr message_node;
4402 if (!context) return IE_INVALID_CONTEXT;
4403 if (!message) return IE_INVAL;
4404 isds_message_free(message);
4405 if (!xpath_ctx) return IE_INVAL;
4408 *message = calloc(1, sizeof(**message));
4409 if (!*message) {
4410 err = IE_NOMEM;
4411 goto leave;
4414 /* Save message XPATH context node */
4415 message_node = xpath_ctx->node;
4418 /* Extract dmDM */
4419 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4420 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4421 if (err) { err = IE_ERROR; goto leave; }
4422 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4423 if (err) goto leave;
4425 if (include_documents) {
4426 struct isds_list *item;
4428 /* Extract dmFiles */
4429 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4430 xpath_ctx);
4431 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4432 err = IE_ISDS; goto leave;
4434 if (err) { err = IE_ERROR; goto leave; }
4435 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4436 if (err) goto leave;
4438 /* Store xmlDoc of this message if needed */
4439 /* Only if we got a XML document in all the documents. */
4440 for (item = (*message)->documents; item; item = item->next) {
4441 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4442 (*message)->xml = xpath_ctx->doc;
4443 break;
4449 /* Restore context to message */
4450 xpath_ctx->node = message_node;
4452 /* Extract dmHash */
4453 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4454 xpath_ctx);
4455 if (err) goto leave;
4457 /* Extract dmQTimestamp, */
4458 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4459 xpath_ctx);
4460 if (err) goto leave;
4462 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4463 * dmAcceptanceTime. */
4464 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4465 if (err) goto leave;
4467 /* Get message type */
4468 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4469 if (err) goto leave;
4471 leave:
4472 if (err) isds_message_free(message);
4473 return err;
4477 /* Extract message event into reallocated isds_event structure
4478 * @context is ISDS context
4479 * @event is automatically reallocated message event structure
4480 * @xpath_ctx is XPath context with current node as isds:dmEvent
4481 * In case of error @event will be freed. */
4482 static isds_error extract_event(struct isds_ctx *context,
4483 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4484 isds_error err = IE_SUCCESS;
4485 xmlXPathObjectPtr result = NULL;
4486 xmlNodePtr event_node;
4487 char *string = NULL;
4489 if (!context) return IE_INVALID_CONTEXT;
4490 if (!event) return IE_INVAL;
4491 isds_event_free(event);
4492 if (!xpath_ctx) return IE_INVAL;
4493 event_node = xpath_ctx->node;
4495 *event = calloc(1, sizeof(**event));
4496 if (!*event) {
4497 err = IE_NOMEM;
4498 goto leave;
4501 /* Extract event data.
4502 * All elements are optional according XSD. That's funny. */
4503 EXTRACT_STRING("sisds:dmEventTime", string);
4504 if (string) {
4505 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4506 if (err) {
4507 char *string_locale = _isds_utf82locale(string);
4508 if (err == IE_DATE) err = IE_ISDS;
4509 isds_printf_message(context,
4510 _("Could not convert dmEventTime as ISO time: %s"),
4511 string_locale);
4512 free(string_locale);
4513 goto leave;
4515 zfree(string);
4518 /* dmEventDescr element has prefix and the rest */
4519 EXTRACT_STRING("sisds:dmEventDescr", string);
4520 if (string) {
4521 err = eventstring2event((xmlChar *) string, *event);
4522 if (err) goto leave;
4523 zfree(string);
4526 leave:
4527 if (err) isds_event_free(event);
4528 free(string);
4529 xmlXPathFreeObject(result);
4530 xpath_ctx->node = event_node;
4531 return err;
4535 /* Convert element of XSD tEventsArray type from XML tree into
4536 * isds_list of isds_event's structure. The list is automatically reallocated.
4537 * @context is ISDS context
4538 * @events is automatically reallocated list of event structures
4539 * @xpath_ctx is XPath context with current node as tEventsArray
4540 * In case of error @events will be freed. */
4541 static isds_error extract_events(struct isds_ctx *context,
4542 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4543 isds_error err = IE_SUCCESS;
4544 xmlXPathObjectPtr result = NULL;
4545 xmlNodePtr events_node;
4546 struct isds_list *event, *prev_event = NULL;
4548 if (!context) return IE_INVALID_CONTEXT;
4549 if (!events) return IE_INVAL;
4550 if (!xpath_ctx) return IE_INVAL;
4551 events_node = xpath_ctx->node;
4553 /* Free old list */
4554 isds_list_free(events);
4556 /* Find events */
4557 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4558 if (!result) {
4559 err = IE_XML;
4560 goto leave;
4563 /* No match */
4564 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4565 isds_printf_message(context,
4566 _("Delivery info does not contain any event"));
4567 err = IE_ISDS;
4568 goto leave;
4572 /* Iterate over events */
4573 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4575 /* Allocate and append list item */
4576 event = calloc(1, sizeof(*event));
4577 if (!event) {
4578 err = IE_NOMEM;
4579 goto leave;
4581 event->destructor = (void (*)(void **))isds_event_free;
4582 if (i == 0) *events = event;
4583 else prev_event->next = event;
4584 prev_event = event;
4586 /* Extract event */
4587 xpath_ctx->node = result->nodesetval->nodeTab[i];
4588 err = extract_event(context,
4589 (struct isds_event **) &(event->data), xpath_ctx);
4590 if (err) goto leave;
4594 leave:
4595 if (err) isds_list_free(events);
4596 xmlXPathFreeObject(result);
4597 xpath_ctx->node = events_node;
4598 return err;
4602 #if HAVE_LIBCURL
4603 /* Insert Base64 encoded data as element with text child.
4604 * @context is session context
4605 * @parent is XML node to append @element with @data as child
4606 * @ns is XML namespace of @element, use NULL to inherit from @parent
4607 * @element is UTF-8 encoded name of new element
4608 * @data is bit stream to encode into @element
4609 * @length is size of @data in bytes
4610 * @return standard error code and fill long error message if needed */
4611 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4612 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4613 const void *data, size_t length) {
4614 isds_error err = IE_SUCCESS;
4615 xmlNodePtr node;
4617 if (!context) return IE_INVALID_CONTEXT;
4618 if (!data && length > 0) return IE_INVAL;
4619 if (!parent || !element) return IE_INVAL;
4621 xmlChar *base64data = NULL;
4622 base64data = (xmlChar *) _isds_b64encode(data, length);
4623 if (!base64data) {
4624 isds_printf_message(context,
4625 ngettext("Not enough memory to encode %zd byte into Base64",
4626 "Not enough memory to encode %zd bytes into Base64",
4627 length),
4628 length);
4629 err = IE_NOMEM;
4630 goto leave;
4632 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4634 leave:
4635 free(base64data);
4636 return err;
4640 /* Convert isds_document structure into XML tree and append to dmFiles node.
4641 * @context is session context
4642 * @document is ISDS document
4643 * @dm_files is XML element the resulting tree will be appended to as a child.
4644 * @return error code, in case of error context' message is filled. */
4645 static isds_error insert_document(struct isds_ctx *context,
4646 struct isds_document *document, xmlNodePtr dm_files) {
4647 isds_error err = IE_SUCCESS;
4648 xmlNodePtr new_file = NULL, file = NULL, node;
4649 xmlAttrPtr attribute_node;
4651 if (!context) return IE_INVALID_CONTEXT;
4652 if (!document || !dm_files) return IE_INVAL;
4654 /* Allocate new dmFile */
4655 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4656 if (!new_file) {
4657 isds_printf_message(context, _("Could not allocate main dmFile"));
4658 err = IE_ERROR;
4659 goto leave;
4661 /* Append the new dmFile.
4662 * XXX: Main document must go first */
4663 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4664 file = xmlAddPrevSibling(dm_files->children, new_file);
4665 else
4666 file = xmlAddChild(dm_files, new_file);
4668 if (!file) {
4669 xmlFreeNode(new_file); new_file = NULL;
4670 isds_printf_message(context, _("Could not add dmFile child to "
4671 "%s element"), dm_files->name);
4672 err = IE_ERROR;
4673 goto leave;
4676 /* @dmMimeType is required */
4677 if (!document->dmMimeType) {
4678 isds_log_message(context,
4679 _("Document is missing mandatory MIME type definition"));
4680 err = IE_INVAL;
4681 goto leave;
4683 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4685 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4686 if (!string) {
4687 isds_printf_message(context,
4688 _("Document has unknown dmFileMetaType: %ld"),
4689 document->dmFileMetaType);
4690 err = IE_ENUM;
4691 goto leave;
4693 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4695 if (document->dmFileGuid) {
4696 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4698 if (document->dmUpFileGuid) {
4699 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4702 /* @dmFileDescr is required */
4703 if (!document->dmFileDescr) {
4704 isds_log_message(context,
4705 _("Document is missing mandatory description (title)"));
4706 err = IE_INVAL;
4707 goto leave;
4709 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4711 if (document->dmFormat) {
4712 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4716 /* Insert content (body) of the document. */
4717 if (document->is_xml) {
4718 /* XML document requested */
4720 /* Allocate new dmXMLContent */
4721 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4722 if (!xmlcontent) {
4723 isds_printf_message(context,
4724 _("Could not allocate dmXMLContent element"));
4725 err = IE_ERROR;
4726 goto leave;
4728 /* Append it */
4729 node = xmlAddChild(file, xmlcontent);
4730 if (!node) {
4731 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4732 isds_printf_message(context,
4733 _("Could not add dmXMLContent child to %s element"),
4734 file->name);
4735 err = IE_ERROR;
4736 goto leave;
4739 /* Copy non-empty node list */
4740 if (document->xml_node_list) {
4741 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4742 document->xml_node_list);
4743 if (!content) {
4744 isds_printf_message(context,
4745 _("Not enough memory to copy XML document"));
4746 err = IE_NOMEM;
4747 goto leave;
4750 if (!xmlAddChildList(node, content)) {
4751 xmlFreeNodeList(content);
4752 isds_printf_message(context,
4753 _("Error while adding XML document into dmXMLContent"));
4754 err = IE_XML;
4755 goto leave;
4757 /* XXX: We cannot free the content here because it's part of node's
4758 * document since now. It will be freed with it automatically. */
4760 } else {
4761 /* Binary document requested */
4762 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4763 document->data, document->data_length);
4764 if (err) goto leave;
4767 leave:
4768 return err;
4772 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4773 * The copy must be preallocated, the date are just appended into structure.
4774 * @context is ISDS context
4775 * @copy is message copy structure
4776 * @xpath_ctx is XPath context with current node as tMStatus */
4777 static isds_error append_TMStatus(struct isds_ctx *context,
4778 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4779 isds_error err = IE_SUCCESS;
4780 xmlXPathObjectPtr result = NULL;
4781 char *code = NULL, *message = NULL;
4783 if (!context) return IE_INVALID_CONTEXT;
4784 if (!copy || !xpath_ctx) return IE_INVAL;
4786 /* Free old values */
4787 zfree(copy->dmStatus);
4788 zfree(copy->dmID);
4790 /* Get error specific to this copy */
4791 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4792 if (!code) {
4793 isds_log_message(context,
4794 _("Missing isds:dmStatusCode under "
4795 "XSD:tMStatus type element"));
4796 err = IE_ISDS;
4797 goto leave;
4800 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4801 /* This copy failed */
4802 copy->error = IE_ISDS;
4803 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4804 if (message) {
4805 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4806 if (!copy->dmStatus) {
4807 copy->dmStatus = code;
4808 code = NULL;
4810 } else {
4811 copy->dmStatus = code;
4812 code = NULL;
4814 } else {
4815 /* This copy succeeded. In this case only, message ID is valid */
4816 copy->error = IE_SUCCESS;
4818 EXTRACT_STRING("isds:dmID", copy->dmID);
4819 if (!copy->dmID) {
4820 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4821 "but did not returned assigned message ID\n"));
4822 err = IE_ISDS;
4826 leave:
4827 free(code);
4828 free(message);
4829 xmlXPathFreeObject(result);
4830 return err;
4834 /* Insert struct isds_approval data (box approval) into XML tree
4835 * @context is session context
4836 * @approval is libisds structure with approval description. NULL is
4837 * acceptable.
4838 * @parent is XML element to append @approval to */
4839 static isds_error insert_GExtApproval(struct isds_ctx *context,
4840 const struct isds_approval *approval, xmlNodePtr parent) {
4842 isds_error err = IE_SUCCESS;
4843 xmlNodePtr node;
4845 if (!context) return IE_INVALID_CONTEXT;
4846 if (!parent) return IE_INVAL;
4848 if (!approval) return IE_SUCCESS;
4850 /* Build XSD:gExtApproval */
4851 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4852 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4854 leave:
4855 return err;
4859 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4860 * code
4861 * @context is session context
4862 * @service_name is name of SERVICE_DB_ACCESS
4863 * @response is reallocated server SOAP body response as XML document
4864 * @raw_response is reallocated bit stream with response body. Use
4865 * NULL if you don't care
4866 * @raw_response_length is size of @raw_response in bytes
4867 * @code is reallocated ISDS status code
4868 * @status_message is reallocated ISDS status message
4869 * @return error coded from lower layer, context message will be set up
4870 * appropriately. */
4871 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4872 const xmlChar *service_name,
4873 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4874 xmlChar **code, xmlChar **status_message) {
4876 isds_error err = IE_SUCCESS;
4877 char *service_name_locale = NULL;
4878 xmlNodePtr request = NULL, node;
4879 xmlNsPtr isds_ns = NULL;
4881 if (!context) return IE_INVALID_CONTEXT;
4882 if (!service_name) return IE_INVAL;
4883 if (!response || !code || !status_message) return IE_INVAL;
4884 if (!raw_response_length && raw_response) return IE_INVAL;
4886 /* Free output argument */
4887 xmlFreeDoc(*response); *response = NULL;
4888 if (raw_response) zfree(*raw_response);
4889 zfree(*code);
4890 zfree(*status_message);
4893 /* Check if connection is established
4894 * TODO: This check should be done downstairs. */
4895 if (!context->curl) return IE_CONNECTION_CLOSED;
4897 service_name_locale = _isds_utf82locale((char*)service_name);
4898 if (!service_name_locale) {
4899 err = IE_NOMEM;
4900 goto leave;
4903 /* Build request */
4904 request = xmlNewNode(NULL, service_name);
4905 if (!request) {
4906 isds_printf_message(context,
4907 _("Could not build %s request"), service_name_locale);
4908 err = IE_ERROR;
4909 goto leave;
4911 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4912 if(!isds_ns) {
4913 isds_log_message(context, _("Could not create ISDS name space"));
4914 err = IE_ERROR;
4915 goto leave;
4917 xmlSetNs(request, isds_ns);
4920 /* Add XSD:tDummyInput child */
4921 INSERT_STRING(request, "dbDummy", NULL);
4924 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4925 service_name_locale);
4927 /* Send request */
4928 err = _isds(context, SERVICE_DB_ACCESS, request, response,
4929 raw_response, raw_response_length);
4930 xmlFreeNode(request); request = NULL;
4932 if (err) {
4933 isds_log(ILF_ISDS, ILL_DEBUG,
4934 _("Processing ISDS response on %s request failed\n"),
4935 service_name_locale);
4936 goto leave;
4939 /* Check for response status */
4940 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4941 code, status_message, NULL);
4942 if (err) {
4943 isds_log(ILF_ISDS, ILL_DEBUG,
4944 _("ISDS response on %s request is missing status\n"),
4945 service_name_locale);
4946 goto leave;
4949 /* Request processed, but nothing found */
4950 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4951 char *code_locale = _isds_utf82locale((char*) *code);
4952 char *status_message_locale =
4953 _isds_utf82locale((char*) *status_message);
4954 isds_log(ILF_ISDS, ILL_DEBUG,
4955 _("Server refused %s request (code=%s, message=%s)\n"),
4956 service_name_locale, code_locale, status_message_locale);
4957 isds_log_message(context, status_message_locale);
4958 free(code_locale);
4959 free(status_message_locale);
4960 err = IE_ISDS;
4961 goto leave;
4964 leave:
4965 free(service_name_locale);
4966 xmlFreeNode(request);
4967 return err;
4969 #endif
4972 /* Get data about logged in user and his box.
4973 * @context is session context
4974 * @db_owner_info is reallocated box owner description. It will be freed on
4975 * error.
4976 * @return error code from lower layer, context message will be set up
4977 * appropriately. */
4978 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4979 struct isds_DbOwnerInfo **db_owner_info) {
4980 isds_error err = IE_SUCCESS;
4981 #if HAVE_LIBCURL
4982 xmlDocPtr response = NULL;
4983 xmlChar *code = NULL, *message = NULL;
4984 xmlXPathContextPtr xpath_ctx = NULL;
4985 xmlXPathObjectPtr result = NULL;
4986 char *string = NULL;
4987 #endif
4989 if (!context) return IE_INVALID_CONTEXT;
4990 zfree(context->long_message);
4991 if (!db_owner_info) return IE_INVAL;
4992 isds_DbOwnerInfo_free(db_owner_info);
4994 #if HAVE_LIBCURL
4995 /* Check if connection is established */
4996 if (!context->curl) return IE_CONNECTION_CLOSED;
4999 /* Do request and check for success */
5000 err = build_send_check_dbdummy_request(context,
5001 BAD_CAST "GetOwnerInfoFromLogin",
5002 &response, NULL, NULL, &code, &message);
5003 if (err) goto leave;
5006 /* Extract data */
5007 /* Prepare structure */
5008 *db_owner_info = calloc(1, sizeof(**db_owner_info));
5009 if (!*db_owner_info) {
5010 err = IE_NOMEM;
5011 goto leave;
5013 xpath_ctx = xmlXPathNewContext(response);
5014 if (!xpath_ctx) {
5015 err = IE_ERROR;
5016 goto leave;
5018 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5019 err = IE_ERROR;
5020 goto leave;
5023 /* Set context node */
5024 result = xmlXPathEvalExpression(BAD_CAST
5025 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
5026 if (!result) {
5027 err = IE_ERROR;
5028 goto leave;
5030 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5031 isds_log_message(context, _("Missing dbOwnerInfo element"));
5032 err = IE_ISDS;
5033 goto leave;
5035 if (result->nodesetval->nodeNr > 1) {
5036 isds_log_message(context, _("Multiple dbOwnerInfo element"));
5037 err = IE_ISDS;
5038 goto leave;
5040 xpath_ctx->node = result->nodesetval->nodeTab[0];
5041 xmlXPathFreeObject(result); result = NULL;
5043 /* Extract it */
5044 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
5047 leave:
5048 if (err) {
5049 isds_DbOwnerInfo_free(db_owner_info);
5052 free(string);
5053 xmlXPathFreeObject(result);
5054 xmlXPathFreeContext(xpath_ctx);
5056 free(code);
5057 free(message);
5058 xmlFreeDoc(response);
5060 if (!err)
5061 isds_log(ILF_ISDS, ILL_DEBUG,
5062 _("GetOwnerInfoFromLogin request processed by server "
5063 "successfully.\n"));
5064 #else /* not HAVE_LIBCURL */
5065 err = IE_NOTSUP;
5066 #endif
5068 return err;
5072 /* Get data about logged in user. */
5073 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
5074 struct isds_DbUserInfo **db_user_info) {
5075 isds_error err = IE_SUCCESS;
5076 #if HAVE_LIBCURL
5077 xmlDocPtr response = NULL;
5078 xmlChar *code = NULL, *message = NULL;
5079 xmlXPathContextPtr xpath_ctx = NULL;
5080 xmlXPathObjectPtr result = NULL;
5081 #endif
5083 if (!context) return IE_INVALID_CONTEXT;
5084 zfree(context->long_message);
5085 if (!db_user_info) return IE_INVAL;
5086 isds_DbUserInfo_free(db_user_info);
5088 #if HAVE_LIBCURL
5089 /* Check if connection is established */
5090 if (!context->curl) return IE_CONNECTION_CLOSED;
5093 /* Do request and check for success */
5094 err = build_send_check_dbdummy_request(context,
5095 BAD_CAST "GetUserInfoFromLogin",
5096 &response, NULL, NULL, &code, &message);
5097 if (err) goto leave;
5100 /* Extract data */
5101 /* Prepare structure */
5102 *db_user_info = calloc(1, sizeof(**db_user_info));
5103 if (!*db_user_info) {
5104 err = IE_NOMEM;
5105 goto leave;
5107 xpath_ctx = xmlXPathNewContext(response);
5108 if (!xpath_ctx) {
5109 err = IE_ERROR;
5110 goto leave;
5112 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5113 err = IE_ERROR;
5114 goto leave;
5117 /* Set context node */
5118 result = xmlXPathEvalExpression(BAD_CAST
5119 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
5120 if (!result) {
5121 err = IE_ERROR;
5122 goto leave;
5124 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5125 isds_log_message(context, _("Missing dbUserInfo element"));
5126 err = IE_ISDS;
5127 goto leave;
5129 if (result->nodesetval->nodeNr > 1) {
5130 isds_log_message(context, _("Multiple dbUserInfo element"));
5131 err = IE_ISDS;
5132 goto leave;
5134 xpath_ctx->node = result->nodesetval->nodeTab[0];
5135 xmlXPathFreeObject(result); result = NULL;
5137 /* Extract it */
5138 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5140 leave:
5141 if (err) {
5142 isds_DbUserInfo_free(db_user_info);
5145 xmlXPathFreeObject(result);
5146 xmlXPathFreeContext(xpath_ctx);
5148 free(code);
5149 free(message);
5150 xmlFreeDoc(response);
5152 if (!err)
5153 isds_log(ILF_ISDS, ILL_DEBUG,
5154 _("GetUserInfoFromLogin request processed by server "
5155 "successfully.\n"));
5156 #else /* not HAVE_LIBCURL */
5157 err = IE_NOTSUP;
5158 #endif
5160 return err;
5164 /* Get expiration time of current password
5165 * @context is session context
5166 * @expiration is automatically reallocated time when password expires. If
5167 * password expiration is disabled, NULL will be returned. In case of error
5168 * it will be nulled too. */
5169 isds_error isds_get_password_expiration(struct isds_ctx *context,
5170 struct timeval **expiration) {
5171 isds_error err = IE_SUCCESS;
5172 #if HAVE_LIBCURL
5173 xmlDocPtr response = NULL;
5174 xmlChar *code = NULL, *message = NULL;
5175 xmlXPathContextPtr xpath_ctx = NULL;
5176 xmlXPathObjectPtr result = NULL;
5177 char *string = NULL;
5178 #endif
5180 if (!context) return IE_INVALID_CONTEXT;
5181 zfree(context->long_message);
5182 if (!expiration) return IE_INVAL;
5183 zfree(*expiration);
5185 #if HAVE_LIBCURL
5186 /* Check if connection is established */
5187 if (!context->curl) return IE_CONNECTION_CLOSED;
5190 /* Do request and check for success */
5191 err = build_send_check_dbdummy_request(context,
5192 BAD_CAST "GetPasswordInfo",
5193 &response, NULL, NULL, &code, &message);
5194 if (err) goto leave;
5197 /* Extract data */
5198 xpath_ctx = xmlXPathNewContext(response);
5199 if (!xpath_ctx) {
5200 err = IE_ERROR;
5201 goto leave;
5203 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5204 err = IE_ERROR;
5205 goto leave;
5208 /* Set context node */
5209 result = xmlXPathEvalExpression(BAD_CAST
5210 "/isds:GetPasswordInfoResponse", xpath_ctx);
5211 if (!result) {
5212 err = IE_ERROR;
5213 goto leave;
5215 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5216 isds_log_message(context,
5217 _("Missing GetPasswordInfoResponse element"));
5218 err = IE_ISDS;
5219 goto leave;
5221 if (result->nodesetval->nodeNr > 1) {
5222 isds_log_message(context,
5223 _("Multiple GetPasswordInfoResponse element"));
5224 err = IE_ISDS;
5225 goto leave;
5227 xpath_ctx->node = result->nodesetval->nodeTab[0];
5228 xmlXPathFreeObject(result); result = NULL;
5230 /* Extract expiration date */
5231 EXTRACT_STRING("isds:pswExpDate", string);
5232 if (string) {
5233 /* And convert it if any returned. Otherwise expiration is disabled. */
5234 err = timestring2timeval((xmlChar *) string, expiration);
5235 if (err) {
5236 char *string_locale = _isds_utf82locale(string);
5237 if (err == IE_DATE) err = IE_ISDS;
5238 isds_printf_message(context,
5239 _("Could not convert pswExpDate as ISO time: %s"),
5240 string_locale);
5241 free(string_locale);
5242 goto leave;
5246 leave:
5247 if (err) {
5248 if (*expiration) {
5249 zfree(*expiration);
5253 free(string);
5254 xmlXPathFreeObject(result);
5255 xmlXPathFreeContext(xpath_ctx);
5257 free(code);
5258 free(message);
5259 xmlFreeDoc(response);
5261 if (!err)
5262 isds_log(ILF_ISDS, ILL_DEBUG,
5263 _("GetPasswordInfo request processed by server "
5264 "successfully.\n"));
5265 #else /* not HAVE_LIBCURL */
5266 err = IE_NOTSUP;
5267 #endif
5269 return err;
5273 #if HAVE_LIBCURL
5274 /* Request delivering new TOTP code from ISDS through side channel before
5275 * changing password.
5276 * @context is session context
5277 * @password is current password.
5278 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5279 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5280 * function for more details.
5281 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5282 * NULL, if you don't care.
5283 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5284 * error code. */
5285 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5286 const char *password, struct isds_otp *otp, char **refnumber) {
5287 isds_error err = IE_SUCCESS;
5288 char *saved_url = NULL; /* No copy */
5289 #if HAVE_CURL_REAUTHORIZATION_BUG
5290 CURL *saved_curl = NULL; /* No copy */
5291 #endif
5292 xmlNsPtr isds_ns = NULL;
5293 xmlNodePtr request = NULL;
5294 xmlDocPtr response = NULL;
5295 xmlChar *code = NULL, *message = NULL;
5296 const xmlChar *codes[] = {
5297 BAD_CAST "2300",
5298 BAD_CAST "2301",
5299 BAD_CAST "2302"
5301 const char *meanings[] = {
5302 N_("Unexpected error"),
5303 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5304 N_("One-time code could not been sent. Try later again.")
5306 const isds_otp_resolution resolutions[] = {
5307 OTP_RESOLUTION_UNKNOWN,
5308 OTP_RESOLUTION_TO_FAST,
5309 OTP_RESOLUTION_TOTP_NOT_SENT
5312 if (NULL == context) return IE_INVALID_CONTEXT;
5313 zfree(context->long_message);
5314 if (NULL == password) {
5315 isds_log_message(context,
5316 _("Second argument (password) of isds_change_password() "
5317 "is NULL"));
5318 return IE_INVAL;
5321 /* Check if connection is established
5322 * TODO: This check should be done downstairs. */
5323 if (!context->curl) return IE_CONNECTION_CLOSED;
5325 if (!context->otp) {
5326 isds_log_message(context, _("This function requires OTP-authenticated "
5327 "context"));
5328 return IE_INVALID_CONTEXT;
5330 if (NULL == otp) {
5331 isds_log_message(context, _("If one-time password authentication "
5332 "method is in use, requesting new OTP code requires "
5333 "one-time credentials argument either"));
5334 return IE_INVAL;
5336 if (otp->method != OTP_TIME) {
5337 isds_log_message(context, _("Requesting new time-based OTP code from "
5338 "server requires one-time password authentication "
5339 "method"));
5340 return IE_INVAL;
5342 if (otp->otp_code != NULL) {
5343 isds_log_message(context, _("Requesting new time-based OTP code from "
5344 "server requires undefined OTP code member in "
5345 "one-time credentials argument"));
5346 return IE_INVAL;
5350 /* Build request */
5351 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5352 if (!request) {
5353 isds_log_message(context, _("Could not build SendSMSCode request"));
5354 return IE_ERROR;
5356 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5357 if(!isds_ns) {
5358 isds_log_message(context, _("Could not create ISDS name space"));
5359 xmlFreeNode(request);
5360 return IE_ERROR;
5362 xmlSetNs(request, isds_ns);
5364 /* Change URL temporarily for sending this request only */
5366 char *new_url = NULL;
5367 if ((err = _isds_build_url_from_context(context,
5368 "%1$.*2$sasws/changePassword", &new_url))) {
5369 goto leave;
5371 saved_url = context->url;
5372 context->url = new_url;
5375 /* Store credentials for sending this request only */
5376 context->otp_credentials = otp;
5377 _isds_discard_credentials(context, 0);
5378 if ((err = _isds_store_credentials(context, context->saved_username,
5379 password, NULL))) {
5380 _isds_discard_credentials(context, 0);
5381 goto leave;
5383 #if HAVE_CURL_REAUTHORIZATION_BUG
5384 saved_curl = context->curl;
5385 context->curl = curl_easy_init();
5386 if (NULL == context->curl) {
5387 err = IE_ERROR;
5388 goto leave;
5390 if (context->timeout) {
5391 err = isds_set_timeout(context, context->timeout);
5392 if (err) goto leave;
5394 #endif
5396 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5398 /* Sent request */
5399 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5401 /* Remove temporal credentials */
5402 _isds_discard_credentials(context, 0);
5403 /* Detach pointer to OTP credentials from context */
5404 context->otp_credentials = NULL;
5405 /* Keep context->otp true to keep signaling this is OTP session */
5407 /* Destroy request */
5408 xmlFreeNode(request); request = NULL;
5410 if (err) {
5411 isds_log(ILF_ISDS, ILL_DEBUG,
5412 _("Processing ISDS response on SendSMSCode request failed\n"));
5413 goto leave;
5416 /* Check for response status */
5417 err = isds_response_status(context, SERVICE_ASWS, response,
5418 &code, &message, (xmlChar **)refnumber);
5419 if (err) {
5420 isds_log(ILF_ISDS, ILL_DEBUG,
5421 _("ISDS response on SendSMSCode request is missing "
5422 "status\n"));
5423 goto leave;
5426 /* Check for error */
5427 if (xmlStrcmp(code, BAD_CAST "0000")) {
5428 char *code_locale = _isds_utf82locale((char*)code);
5429 char *message_locale = _isds_utf82locale((char*)message);
5430 size_t i;
5431 isds_log(ILF_ISDS, ILL_DEBUG,
5432 _("Server refused to send new code on SendSMSCode "
5433 "request (code=%s, message=%s)\n"),
5434 code_locale, message_locale);
5436 /* Check for known error codes */
5437 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5438 if (!xmlStrcmp(code, codes[i])) break;
5440 if (i < sizeof(codes)/sizeof(*codes)) {
5441 isds_log_message(context, _(meanings[i]));
5442 /* Mimic otp->resolution according to the code, specification does
5443 * prescribe OTP header to be available. */
5444 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5445 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5446 otp->resolution = resolutions[i];
5447 } else
5448 isds_log_message(context, message_locale);
5450 free(code_locale);
5451 free(message_locale);
5453 err = IE_ISDS;
5454 goto leave;
5457 /* Otherwise new code sent successfully */
5458 /* Mimic otp->resolution according to the code, specification does
5459 * prescribe OTP header to be available. */
5460 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5461 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5463 leave:
5464 if (NULL != saved_url) {
5465 /* Revert URL to original one */
5466 zfree(context->url);
5467 context->url = saved_url;
5469 #if HAVE_CURL_REAUTHORIZATION_BUG
5470 if (NULL != saved_curl) {
5471 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5472 context->curl = saved_curl;
5474 #endif
5476 free(code);
5477 free(message);
5478 xmlFreeDoc(response);
5479 xmlFreeNode(request);
5481 if (!err)
5482 isds_log(ILF_ISDS, ILL_DEBUG,
5483 _("New OTP code has been sent successfully on SendSMSCode "
5484 "request.\n"));
5485 return err;
5489 /* Convert response status code to isds_error code and set long message
5490 * @context is context to save long message to
5491 * @map is mapping from codes to errors and messages. Pass NULL for generic
5492 * handling.
5493 * @code is status code to translate
5494 * @message is non-localized status message to put into long message in case
5495 * of uknown error. It can be NULL if server did not provide any.
5496 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5497 * invalid invocation. */
5498 static isds_error statuscode2isds_error(struct isds_ctx *context,
5499 const struct code_map_isds_error *map,
5500 const xmlChar *code, const xmlChar *message) {
5501 if (NULL == code) {
5502 isds_log_message(context,
5503 _("NULL status code passed to statuscode2isds_error()"));
5504 return IE_INVAL;
5507 if (NULL != map) {
5508 /* Check for known error codes */
5509 for (int i=0; map->codes[i] != NULL; i++) {
5510 if (!xmlStrcmp(code, map->codes[i])) {
5511 isds_log_message(context, _(map->meanings[i]));
5512 return map->errors[i];
5517 /* Other error */
5518 if (xmlStrcmp(code, BAD_CAST "0000")) {
5519 char *message_locale = _isds_utf82locale((char*)message);
5520 if (NULL == message_locale)
5521 isds_log_message(context, _("ISDS server returned unknown error"));
5522 else
5523 isds_log_message(context, message_locale);
5524 free(message_locale);
5525 return IE_ISDS;
5528 return IE_SUCCESS;
5530 #endif
5533 /* Change user password in ISDS.
5534 * User must supply old password, new password will takes effect after some
5535 * time, current session can continue. Password must fulfill some constraints.
5536 * @context is session context
5537 * @old_password is current password.
5538 * @new_password is requested new password
5539 * @otp auxiliary data required if one-time password authentication is in use,
5540 * defines OTP code (if known) and returns fine grade resolution of OTP
5541 * procedure. Pass NULL, if one-time password authentication is not needed.
5542 * Please note the @otp argument must match OTP method used at log-in time. See
5543 * isds_login() function for more details.
5544 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5545 * NULL, if you don't care.
5546 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5547 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5548 * awaiting OTP code that has been delivered by side channel to the user. */
5549 isds_error isds_change_password(struct isds_ctx *context,
5550 const char *old_password, const char *new_password,
5551 struct isds_otp *otp, char **refnumber) {
5552 isds_error err = IE_SUCCESS;
5553 #if HAVE_LIBCURL
5554 char *saved_url = NULL; /* No copy */
5555 #if HAVE_CURL_REAUTHORIZATION_BUG
5556 CURL *saved_curl = NULL; /* No copy */
5557 #endif
5558 xmlNsPtr isds_ns = NULL;
5559 xmlNodePtr request = NULL, node;
5560 xmlDocPtr response = NULL;
5561 xmlChar *code = NULL, *message = NULL;
5562 const xmlChar *codes[] = {
5563 BAD_CAST "1066",
5564 BAD_CAST "1067",
5565 BAD_CAST "1079",
5566 BAD_CAST "1080",
5567 BAD_CAST "1081",
5568 BAD_CAST "1082",
5569 BAD_CAST "1083",
5570 BAD_CAST "1090",
5571 BAD_CAST "1091",
5572 BAD_CAST "2300",
5573 BAD_CAST "9204"
5575 const char *meanings[] = {
5576 N_("Password length must be between 8 and 32 characters"),
5577 N_("Password cannot be reused"), /* Server does not distinguish 1067
5578 and 1091 on ChangePasswordOTP */
5579 N_("Password contains forbidden character"),
5580 N_("Password must contain at least one upper-case letter, "
5581 "one lower-case, and one digit"),
5582 N_("Password cannot contain sequence of three identical characters"),
5583 N_("Password cannot contain user identifier"),
5584 N_("Password is too simmple"),
5585 N_("Old password is not valid"),
5586 N_("Password cannot be reused"),
5587 N_("Unexpected error"),
5588 N_("LDAP update error")
5590 #endif
5592 if (!context) return IE_INVALID_CONTEXT;
5593 zfree(context->long_message);
5594 if (NULL != refnumber)
5595 zfree(*refnumber);
5596 if (NULL == old_password) {
5597 isds_log_message(context,
5598 _("Second argument (old password) of isds_change_password() "
5599 "is NULL"));
5600 return IE_INVAL;
5602 if (NULL == otp && NULL == new_password) {
5603 isds_log_message(context,
5604 _("Third argument (new password) of isds_change_password() "
5605 "is NULL"));
5606 return IE_INVAL;
5609 #if HAVE_LIBCURL
5610 /* Check if connection is established
5611 * TODO: This check should be done downstairs. */
5612 if (!context->curl) return IE_CONNECTION_CLOSED;
5614 if (context->otp && NULL == otp) {
5615 isds_log_message(context, _("If one-time password authentication "
5616 "method is in use, changing password requires one-time "
5617 "credentials either"));
5618 return IE_INVAL;
5621 /* Build ChangeISDSPassword request */
5622 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5623 BAD_CAST "ChangePasswordOTP");
5624 if (!request) {
5625 isds_log_message(context, (NULL == otp) ?
5626 _("Could not build ChangeISDSPassword request") :
5627 _("Could not build ChangePasswordOTP request"));
5628 return IE_ERROR;
5630 isds_ns = xmlNewNs(request,
5631 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5632 NULL);
5633 if(!isds_ns) {
5634 isds_log_message(context, _("Could not create ISDS name space"));
5635 xmlFreeNode(request);
5636 return IE_ERROR;
5638 xmlSetNs(request, isds_ns);
5640 INSERT_STRING(request, "dbOldPassword", old_password);
5641 INSERT_STRING(request, "dbNewPassword", new_password);
5643 if (NULL != otp) {
5644 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5645 switch (otp->method) {
5646 case OTP_HMAC:
5647 isds_log(ILF_SEC, ILL_INFO,
5648 _("Selected authentication method: "
5649 "HMAC-based one-time password\n"));
5650 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5651 break;
5652 case OTP_TIME:
5653 isds_log(ILF_SEC, ILL_INFO,
5654 _("Selected authentication method: "
5655 "Time-based one-time password\n"));
5656 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5657 if (otp->otp_code == NULL) {
5658 isds_log(ILF_SEC, ILL_INFO,
5659 _("OTP code has not been provided by "
5660 "application, requesting server for "
5661 "new one.\n"));
5662 err = _isds_request_totp_code(context, old_password, otp,
5663 refnumber);
5664 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5665 goto leave;
5667 } else {
5668 isds_log(ILF_SEC, ILL_INFO,
5669 _("OTP code has been provided by "
5670 "application, not requesting server "
5671 "for new one.\n"));
5673 break;
5674 default:
5675 isds_log_message(context,
5676 _("Unknown one-time password authentication "
5677 "method requested by application"));
5678 err = IE_ENUM;
5679 goto leave;
5682 /* Change URL temporarily for sending this request only */
5684 char *new_url = NULL;
5685 if ((err = _isds_build_url_from_context(context,
5686 "%1$.*2$sasws/changePassword", &new_url))) {
5687 goto leave;
5689 saved_url = context->url;
5690 context->url = new_url;
5693 /* Store credentials for sending this request only */
5694 context->otp_credentials = otp;
5695 _isds_discard_credentials(context, 0);
5696 if ((err = _isds_store_credentials(context, context->saved_username,
5697 old_password, NULL))) {
5698 _isds_discard_credentials(context, 0);
5699 goto leave;
5701 #if HAVE_CURL_REAUTHORIZATION_BUG
5702 saved_curl = context->curl;
5703 context->curl = curl_easy_init();
5704 if (NULL == context->curl) {
5705 err = IE_ERROR;
5706 goto leave;
5708 if (context->timeout) {
5709 err = isds_set_timeout(context, context->timeout);
5710 if (err) goto leave;
5712 #endif
5715 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5716 _("Sending ChangeISDSPassword request to ISDS\n") :
5717 _("Sending ChangePasswordOTP request to ISDS\n"));
5719 /* Sent request */
5720 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5721 request, &response, NULL, NULL);
5723 if (otp) {
5724 /* Remove temporal credentials */
5725 _isds_discard_credentials(context, 0);
5726 /* Detach pointer to OTP credentials from context */
5727 context->otp_credentials = NULL;
5728 /* Keep context->otp true to keep signaling this is OTP session */
5731 /* Destroy request */
5732 xmlFreeNode(request); request = NULL;
5734 if (err) {
5735 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5736 _("Processing ISDS response on ChangeISDSPassword "
5737 "request failed\n") :
5738 _("Processing ISDS response on ChangePasswordOTP "
5739 "request failed\n"));
5740 goto leave;
5743 /* Check for response status */
5744 err = isds_response_status(context,
5745 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5746 &code, &message, (xmlChar **)refnumber);
5747 if (err) {
5748 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5749 _("ISDS response on ChangeISDSPassword request is missing "
5750 "status\n") :
5751 _("ISDS response on ChangePasswordOTP request is missing "
5752 "status\n"));
5753 goto leave;
5756 /* Check for known error codes */
5757 for (size_t i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5758 if (!xmlStrcmp(code, codes[i])) {
5759 char *code_locale = _isds_utf82locale((char*)code);
5760 char *message_locale = _isds_utf82locale((char*)message);
5761 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5762 _("Server refused to change password on ChangeISDSPassword "
5763 "request (code=%s, message=%s)\n") :
5764 _("Server refused to change password on ChangePasswordOTP "
5765 "request (code=%s, message=%s)\n"),
5766 code_locale, message_locale);
5767 free(code_locale);
5768 free(message_locale);
5769 isds_log_message(context, _(meanings[i]));
5770 err = IE_INVAL;
5771 goto leave;
5775 /* Other error */
5776 if (xmlStrcmp(code, BAD_CAST "0000")) {
5777 char *code_locale = _isds_utf82locale((char*)code);
5778 char *message_locale = _isds_utf82locale((char*)message);
5779 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5780 _("Server refused to change password on ChangeISDSPassword "
5781 "request (code=%s, message=%s)\n") :
5782 _("Server refused to change password on ChangePasswordOTP "
5783 "request (code=%s, message=%s)\n"),
5784 code_locale, message_locale);
5785 isds_log_message(context, message_locale);
5786 free(code_locale);
5787 free(message_locale);
5788 err = IE_ISDS;
5789 goto leave;
5792 /* Otherwise password changed successfully */
5794 leave:
5795 if (NULL != saved_url) {
5796 /* Revert URL to original one */
5797 zfree(context->url);
5798 context->url = saved_url;
5800 #if HAVE_CURL_REAUTHORIZATION_BUG
5801 if (NULL != saved_curl) {
5802 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5803 context->curl = saved_curl;
5805 #endif
5807 free(code);
5808 free(message);
5809 xmlFreeDoc(response);
5810 xmlFreeNode(request);
5812 if (!err)
5813 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5814 _("Password changed successfully on ChangeISDSPassword "
5815 "request.\n") :
5816 _("Password changed successfully on ChangePasswordOTP "
5817 "request.\n"));
5818 #else /* not HAVE_LIBCURL */
5819 err = IE_NOTSUP;
5820 #endif
5822 return err;
5826 #if HAVE_LIBCURL
5827 /* Generic middle part with request sending and response check.
5828 * It sends prepared request and checks for error code.
5829 * @context is ISDS session context.
5830 * @service is ISDS service handler
5831 * @service_name is name in scope of given @service
5832 * @request is XML tree with request. Will be freed to save memory.
5833 * @response is XML document outputting ISDS response.
5834 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5835 * @map is mapping from status code to library error. Pass NULL if no special
5836 * handling is requested.
5837 * NULL, if you don't care. */
5838 static isds_error send_destroy_request_check_response(
5839 struct isds_ctx *context,
5840 const isds_service service, const xmlChar *service_name,
5841 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5842 const struct code_map_isds_error *map) {
5843 isds_error err = IE_SUCCESS;
5844 char *service_name_locale = NULL;
5845 xmlChar *code = NULL, *message = NULL;
5848 if (!context) return IE_INVALID_CONTEXT;
5849 if (!service_name || *service_name == '\0' || !request || !*request ||
5850 !response)
5851 return IE_INVAL;
5853 /* Check if connection is established
5854 * TODO: This check should be done downstairs. */
5855 if (!context->curl) return IE_CONNECTION_CLOSED;
5857 service_name_locale = _isds_utf82locale((char*) service_name);
5858 if (!service_name_locale) {
5859 err = IE_NOMEM;
5860 goto leave;
5863 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5864 service_name_locale);
5866 /* Send request */
5867 err = _isds(context, service, *request, response, NULL, NULL);
5868 xmlFreeNode(*request); *request = NULL;
5870 if (err) {
5871 isds_log(ILF_ISDS, ILL_DEBUG,
5872 _("Processing ISDS response on %s request failed\n"),
5873 service_name_locale);
5874 goto leave;
5877 /* Check for response status */
5878 err = isds_response_status(context, service, *response,
5879 &code, &message, refnumber);
5880 if (err) {
5881 isds_log(ILF_ISDS, ILL_DEBUG,
5882 _("ISDS response on %s request is missing status\n"),
5883 service_name_locale);
5884 goto leave;
5887 err = statuscode2isds_error(context, map, code, message);
5889 /* Request processed, but server failed */
5890 if (xmlStrcmp(code, BAD_CAST "0000")) {
5891 char *code_locale = _isds_utf82locale((char*) code);
5892 char *message_locale = _isds_utf82locale((char*) message);
5893 isds_log(ILF_ISDS, ILL_DEBUG,
5894 _("Server refused %s request (code=%s, message=%s)\n"),
5895 service_name_locale, code_locale, message_locale);
5896 free(code_locale);
5897 free(message_locale);
5898 goto leave;
5902 leave:
5903 free(code);
5904 free(message);
5905 if (err && *response) {
5906 xmlFreeDoc(*response);
5907 *response = NULL;
5909 if (*request) {
5910 xmlFreeNode(*request);
5911 *request = NULL;
5913 free(service_name_locale);
5915 return err;
5919 /* Generic bottom half with request sending.
5920 * It sends prepared request, checks for error code, destroys response and
5921 * request and log success or failure.
5922 * @context is ISDS session context.
5923 * @service is ISDS service handler
5924 * @service_name is name in scope of given @service
5925 * @request is XML tree with request. Will be freed to save memory.
5926 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5927 * NULL, if you don't care. */
5928 static isds_error send_request_check_drop_response(
5929 struct isds_ctx *context,
5930 const isds_service service, const xmlChar *service_name,
5931 xmlNodePtr *request, xmlChar **refnumber) {
5932 isds_error err = IE_SUCCESS;
5933 xmlDocPtr response = NULL;
5936 if (!context) return IE_INVALID_CONTEXT;
5937 if (!service_name || *service_name == '\0' || !request || !*request)
5938 return IE_INVAL;
5940 /* Send request and check response*/
5941 err = send_destroy_request_check_response(context,
5942 service, service_name, request, &response, refnumber, NULL);
5944 xmlFreeDoc(response);
5946 if (*request) {
5947 xmlFreeNode(*request);
5948 *request = NULL;
5951 if (!err) {
5952 char *service_name_locale = _isds_utf82locale((char *) service_name);
5953 isds_log(ILF_ISDS, ILL_DEBUG,
5954 _("%s request processed by server successfully.\n"),
5955 service_name_locale);
5956 free(service_name_locale);
5959 return err;
5963 /* Insert isds_credentials_delivery structure into XML request if not NULL
5964 * @context is session context
5965 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5966 * credentials delivery. The email field is passed.
5967 * @parent is XML element where to insert */
5968 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5969 const struct isds_credentials_delivery *credentials_delivery,
5970 xmlNodePtr parent) {
5971 isds_error err = IE_SUCCESS;
5972 xmlNodePtr node;
5974 if (!context) return IE_INVALID_CONTEXT;
5975 if (!parent) return IE_INVAL;
5977 if (credentials_delivery) {
5978 /* Following elements are valid only for services:
5979 * NewAccessData, AddDataBoxUser, CreateDataBox */
5980 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5981 INSERT_STRING(parent, "email", credentials_delivery->email);
5984 leave:
5985 return err;
5989 /* Extract credentials delivery from ISDS response.
5990 * @context is session context
5991 * @credentials_delivery is pointer to valid structure to fill in returned
5992 * user's password (and new log-in name). If NULL, do not extract the data.
5993 * @response is pointer to XML document with ISDS response
5994 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5995 * @return IE_SUCCESS even if new user name has not been found because it's not
5996 * clear whether it's returned always. */
5997 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5998 struct isds_credentials_delivery *credentials_delivery,
5999 xmlDocPtr response, const char *request_name) {
6000 isds_error err = IE_SUCCESS;
6001 xmlXPathContextPtr xpath_ctx = NULL;
6002 xmlXPathObjectPtr result = NULL;
6003 char *xpath_query = NULL;
6005 if (!context) return IE_INVALID_CONTEXT;
6006 if (credentials_delivery) {
6007 zfree(credentials_delivery->token);
6008 zfree(credentials_delivery->new_user_name);
6010 if (!response || !request_name || !*request_name) return IE_INVAL;
6013 /* Extract optional token */
6014 if (credentials_delivery) {
6015 xpath_ctx = xmlXPathNewContext(response);
6016 if (!xpath_ctx) {
6017 err = IE_ERROR;
6018 goto leave;
6020 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6021 err = IE_ERROR;
6022 goto leave;
6025 /* Verify root element */
6026 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
6027 request_name)) {
6028 err = IE_NOMEM;
6029 goto leave;
6031 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
6032 if (!result) {
6033 err = IE_ERROR;
6034 goto leave;
6036 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6037 char *request_name_locale = _isds_utf82locale(request_name);
6038 isds_log(ILF_ISDS, ILL_WARNING,
6039 _("Wrong element in ISDS response for %s request "
6040 "while extracting credentials delivery details\n"),
6041 request_name_locale);
6042 free(request_name_locale);
6043 err = IE_ERROR;
6044 goto leave;
6046 xpath_ctx->node = result->nodesetval->nodeTab[0];
6049 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6050 * optional. */
6051 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
6053 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
6054 if (!credentials_delivery->token) {
6055 char *request_name_locale = _isds_utf82locale(request_name);
6056 isds_log(ILF_ISDS, ILL_ERR,
6057 _("ISDS did not return token on %s request "
6058 "even if requested\n"), request_name_locale);
6059 free(request_name_locale);
6060 err = IE_ERROR;
6064 leave:
6065 free(xpath_query);
6066 xmlXPathFreeObject(result);
6067 xmlXPathFreeContext(xpath_ctx);
6069 return err;
6073 /* Build XSD:tCreateDBInput request type for box creating.
6074 * @context is session context
6075 * @request outputs built XML tree
6076 * @service_name is request name of SERVICE_DB_MANIPULATION service
6077 * @box is box description to create including single primary user (in case of
6078 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6079 * ignored.
6080 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6081 * box, or contact address of PFO box owner)
6082 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6083 * @upper_box_id is optional ID of supper box if currently created box is
6084 * subordinated.
6085 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6086 * don't care.
6087 * @credentials_delivery is valid pointer if ISDS should return token that box
6088 * owner can use to obtain his new credentials in on-line way. Then valid email
6089 * member value should be supplied.
6090 * @approval is optional external approval of box manipulation */
6091 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
6092 xmlNodePtr *request, const xmlChar *service_name,
6093 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6094 const xmlChar *former_names, const xmlChar *upper_box_id,
6095 const xmlChar *ceo_label,
6096 const struct isds_credentials_delivery *credentials_delivery,
6097 const struct isds_approval *approval) {
6098 isds_error err = IE_SUCCESS;
6099 xmlNsPtr isds_ns = NULL;
6100 xmlNodePtr node, dbPrimaryUsers;
6101 xmlChar *string = NULL;
6102 const struct isds_list *item;
6105 if (!context) return IE_INVALID_CONTEXT;
6106 if (!request || !service_name || service_name[0] == '\0' || !box)
6107 return IE_INVAL;
6110 /* Build CreateDataBox-similar request */
6111 *request = xmlNewNode(NULL, service_name);
6112 if (!*request) {
6113 char *service_name_locale = _isds_utf82locale((char*) service_name);
6114 isds_printf_message(context, _("Could build %s request"),
6115 service_name_locale);
6116 free(service_name_locale);
6117 return IE_ERROR;
6119 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
6120 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
6121 if (!isds_ns) {
6122 isds_log_message(context, _("Could not create ISDS1 name space"));
6123 xmlFreeNode(*request);
6124 return IE_ERROR;
6126 } else {
6127 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6128 if (!isds_ns) {
6129 isds_log_message(context, _("Could not create ISDS name space"));
6130 xmlFreeNode(*request);
6131 return IE_ERROR;
6134 xmlSetNs(*request, isds_ns);
6136 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6137 err = insert_DbOwnerInfo(context, box, node);
6138 if (err) goto leave;
6140 /* Insert users */
6141 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6142 * verbose documentation allows none dbUserInfo */
6143 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6144 for (item = users; item; item = item->next) {
6145 if (item->data) {
6146 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6147 err = insert_DbUserInfo(context,
6148 (struct isds_DbUserInfo *) item->data, 1, node);
6149 if (err) goto leave;
6153 INSERT_STRING(*request, "dbFormerNames", former_names);
6154 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6155 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6157 err = insert_credentials_delivery(context, credentials_delivery, *request);
6158 if (err) goto leave;
6160 err = insert_GExtApproval(context, approval, *request);
6161 if (err) goto leave;
6163 leave:
6164 if (err) {
6165 xmlFreeNode(*request);
6166 *request = NULL;
6168 free(string);
6169 return err;
6171 #endif /* HAVE_LIBCURL */
6174 /* Create new box.
6175 * @context is session context
6176 * @box is box description to create including single primary user (in case of
6177 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6178 * ignored. It outputs box ID assigned by ISDS in dbID element.
6179 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6180 * box, or contact address of PFO box owner)
6181 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6182 * @upper_box_id is optional ID of supper box if currently created box is
6183 * subordinated.
6184 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6185 * @credentials_delivery is NULL if new password should be delivered off-line
6186 * to box owner. It is valid pointer if owner should obtain new password on-line
6187 * on dedicated web server. Then input @credentials_delivery.email value is
6188 * his e-mail address he must provide to dedicated web server together
6189 * with output reallocated @credentials_delivery.token member. Output
6190 * member @credentials_delivery.new_user_name is unused up on this call.
6191 * @approval is optional external approval of box manipulation
6192 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6193 * NULL, if you don't care.*/
6194 isds_error isds_add_box(struct isds_ctx *context,
6195 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6196 const char *former_names, const char *upper_box_id,
6197 const char *ceo_label,
6198 struct isds_credentials_delivery *credentials_delivery,
6199 const struct isds_approval *approval, char **refnumber) {
6200 isds_error err = IE_SUCCESS;
6201 #if HAVE_LIBCURL
6202 xmlNodePtr request = NULL;
6203 xmlDocPtr response = NULL;
6204 xmlXPathContextPtr xpath_ctx = NULL;
6205 xmlXPathObjectPtr result = NULL;
6206 #endif
6209 if (!context) return IE_INVALID_CONTEXT;
6210 zfree(context->long_message);
6211 if (credentials_delivery) {
6212 zfree(credentials_delivery->token);
6213 zfree(credentials_delivery->new_user_name);
6215 if (!box) return IE_INVAL;
6217 #if HAVE_LIBCURL
6218 /* Scratch box ID */
6219 zfree(box->dbID);
6221 /* Build CreateDataBox request */
6222 err = build_CreateDBInput_request(context,
6223 &request, BAD_CAST "CreateDataBox",
6224 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6225 (xmlChar *) ceo_label, credentials_delivery, approval);
6226 if (err) goto leave;
6228 /* Send it to server and process response */
6229 err = send_destroy_request_check_response(context,
6230 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6231 &response, (xmlChar **) refnumber, NULL);
6233 /* Extract box ID */
6234 xpath_ctx = xmlXPathNewContext(response);
6235 if (!xpath_ctx) {
6236 err = IE_ERROR;
6237 goto leave;
6239 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6240 err = IE_ERROR;
6241 goto leave;
6243 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6245 /* Extract optional token */
6246 err = extract_credentials_delivery(context, credentials_delivery, response,
6247 "CreateDataBox");
6249 leave:
6250 xmlXPathFreeObject(result);
6251 xmlXPathFreeContext(xpath_ctx);
6252 xmlFreeDoc(response);
6253 xmlFreeNode(request);
6255 if (!err) {
6256 isds_log(ILF_ISDS, ILL_DEBUG,
6257 _("CreateDataBox request processed by server successfully.\n"));
6259 #else /* not HAVE_LIBCURL */
6260 err = IE_NOTSUP;
6261 #endif
6263 return err;
6267 /* Notify ISDS about new PFO entity.
6268 * This function has no real effect.
6269 * @context is session context
6270 * @box is PFO description including single primary user. aifoIsds,
6271 * address->adCode, address->adDistrict members are ignored.
6272 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6273 * @former_names is optional undocumented string. Pass NULL if you don't care.
6274 * @upper_box_id is optional ID of supper box if currently created box is
6275 * subordinated.
6276 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6277 * @approval is optional external approval of box manipulation
6278 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6279 * NULL, if you don't care.*/
6280 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6281 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6282 const char *former_names, const char *upper_box_id,
6283 const char *ceo_label, const struct isds_approval *approval,
6284 char **refnumber) {
6285 isds_error err = IE_SUCCESS;
6286 #if HAVE_LIBCURL
6287 xmlNodePtr request = NULL;
6288 #endif
6290 if (!context) return IE_INVALID_CONTEXT;
6291 zfree(context->long_message);
6292 if (!box) return IE_INVAL;
6294 #if HAVE_LIBCURL
6295 /* Build CreateDataBoxPFOInfo request */
6296 err = build_CreateDBInput_request(context,
6297 &request, BAD_CAST "CreateDataBoxPFOInfo",
6298 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6299 (xmlChar *) ceo_label, NULL, approval);
6300 if (err) goto leave;
6302 /* Send it to server and process response */
6303 err = send_request_check_drop_response(context,
6304 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6305 (xmlChar **) refnumber);
6306 /* XXX: XML Schema names output dbID element but textual documentation
6307 * states no box identifier is returned. */
6308 leave:
6309 xmlFreeNode(request);
6310 #else /* not HAVE_LIBCURL */
6311 err = IE_NOTSUP;
6312 #endif
6313 return err;
6317 /* Common implementation for removing given box.
6318 * @context is session context
6319 * @service_name is UTF-8 encoded name fo ISDS service
6320 * @box is box description to delete. aifoIsds, address->adCode,
6321 * address->adDistrict members are ignored.
6322 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6323 * carry sane value. If NULL, do not inject this information into request.
6324 * @approval is optional external approval of box manipulation
6325 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6326 * NULL, if you don't care.*/
6327 static isds_error _isds_delete_box_common(struct isds_ctx *context,
6328 const xmlChar *service_name,
6329 const struct isds_DbOwnerInfo *box, const struct tm *since,
6330 const struct isds_approval *approval, char **refnumber) {
6331 isds_error err = IE_SUCCESS;
6332 #if HAVE_LIBCURL
6333 xmlNsPtr isds_ns = NULL;
6334 xmlNodePtr request = NULL;
6335 xmlNodePtr node;
6336 xmlChar *string = NULL;
6337 #endif
6340 if (!context) return IE_INVALID_CONTEXT;
6341 zfree(context->long_message);
6342 if (!service_name || !*service_name || !box) return IE_INVAL;
6345 #if HAVE_LIBCURL
6346 /* Build DeleteDataBox(Promptly) request */
6347 request = xmlNewNode(NULL, service_name);
6348 if (!request) {
6349 char *service_name_locale = _isds_utf82locale((char*)service_name);
6350 isds_printf_message(context,
6351 _("Could build %s request"), service_name_locale);
6352 free(service_name_locale);
6353 return IE_ERROR;
6355 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6356 if(!isds_ns) {
6357 isds_log_message(context, _("Could not create ISDS name space"));
6358 xmlFreeNode(request);
6359 return IE_ERROR;
6361 xmlSetNs(request, isds_ns);
6363 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6364 err = insert_DbOwnerInfo(context, box, node);
6365 if (err) goto leave;
6367 if (since) {
6368 err = tm2datestring(since, &string);
6369 if (err) {
6370 isds_log_message(context,
6371 _("Could not convert `since' argument to ISO date string"));
6372 goto leave;
6374 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6375 zfree(string);
6378 err = insert_GExtApproval(context, approval, request);
6379 if (err) goto leave;
6382 /* Send it to server and process response */
6383 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6384 service_name, &request, (xmlChar **) refnumber);
6386 leave:
6387 xmlFreeNode(request);
6388 free(string);
6389 #else /* not HAVE_LIBCURL */
6390 err = IE_NOTSUP;
6391 #endif
6392 return err;
6396 /* Remove given box permanently.
6397 * @context is session context
6398 * @box is box description to delete. aifoIsds, address->adCode,
6399 * address->adDistrict members are ignored.
6400 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6401 * carry sane value.
6402 * @approval is optional external approval of box manipulation
6403 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6404 * NULL, if you don't care.*/
6405 isds_error isds_delete_box(struct isds_ctx *context,
6406 const struct isds_DbOwnerInfo *box, const struct tm *since,
6407 const struct isds_approval *approval, char **refnumber) {
6408 if (!context) return IE_INVALID_CONTEXT;
6409 zfree(context->long_message);
6410 if (!box || !since) return IE_INVAL;
6412 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6413 box, since, approval, refnumber);
6417 /* Undocumented function.
6418 * @context is session context
6419 * @box is box description to delete. aifoIsds, address->adCode,
6420 * address->adDistrict members are ignored.
6421 * @approval is optional external approval of box manipulation
6422 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6423 * NULL, if you don't care.*/
6424 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6425 const struct isds_DbOwnerInfo *box,
6426 const struct isds_approval *approval, char **refnumber) {
6427 if (!context) return IE_INVALID_CONTEXT;
6428 zfree(context->long_message);
6429 if (!box) return IE_INVAL;
6431 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6432 box, NULL, approval, refnumber);
6436 /* Update data about given box.
6437 * @context is session context
6438 * @old_box current box description. aifoIsds, address->adCode,
6439 * address->adDistrict members are ignored.
6440 * @new_box are updated data about @old_box. aifoIsds, address->adCode,
6441 * address->adDistrict members are ignored.
6442 * @approval is optional external approval of box manipulation
6443 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6444 * NULL, if you don't care.*/
6445 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6446 const struct isds_DbOwnerInfo *old_box,
6447 const struct isds_DbOwnerInfo *new_box,
6448 const struct isds_approval *approval, char **refnumber) {
6449 isds_error err = IE_SUCCESS;
6450 #if HAVE_LIBCURL
6451 xmlNsPtr isds_ns = NULL;
6452 xmlNodePtr request = NULL;
6453 xmlNodePtr node;
6454 #endif
6457 if (!context) return IE_INVALID_CONTEXT;
6458 zfree(context->long_message);
6459 if (!old_box || !new_box) return IE_INVAL;
6462 #if HAVE_LIBCURL
6463 /* Build UpdateDataBoxDescr request */
6464 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6465 if (!request) {
6466 isds_log_message(context,
6467 _("Could build UpdateDataBoxDescr request"));
6468 return IE_ERROR;
6470 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6471 if(!isds_ns) {
6472 isds_log_message(context, _("Could not create ISDS name space"));
6473 xmlFreeNode(request);
6474 return IE_ERROR;
6476 xmlSetNs(request, isds_ns);
6478 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6479 err = insert_DbOwnerInfo(context, old_box, node);
6480 if (err) goto leave;
6482 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6483 err = insert_DbOwnerInfo(context, new_box, node);
6484 if (err) goto leave;
6486 err = insert_GExtApproval(context, approval, request);
6487 if (err) goto leave;
6490 /* Send it to server and process response */
6491 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6492 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6494 leave:
6495 xmlFreeNode(request);
6496 #else /* not HAVE_LIBCURL */
6497 err = IE_NOTSUP;
6498 #endif
6500 return err;
6504 #if HAVE_LIBCURL
6505 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6506 * code
6507 * @context is session context
6508 * @service is SOAP service
6509 * @service_name is name of request in @service
6510 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6511 * @box_id is box ID of interest
6512 * @approval is optional external approval of box manipulation
6513 * @response is server SOAP body response as XML document
6514 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6515 * NULL, if you don't care.
6516 * @return error coded from lower layer, context message will be set up
6517 * appropriately. */
6518 static isds_error build_send_dbid_request_check_response(
6519 struct isds_ctx *context, const isds_service service,
6520 const xmlChar *service_name, const xmlChar *box_id_element,
6521 const xmlChar *box_id, const struct isds_approval *approval,
6522 xmlDocPtr *response, xmlChar **refnumber) {
6524 isds_error err = IE_SUCCESS;
6525 char *service_name_locale = NULL, *box_id_locale = NULL;
6526 xmlNodePtr request = NULL, node;
6527 xmlNsPtr isds_ns = NULL;
6529 if (!context) return IE_INVALID_CONTEXT;
6530 if (!service_name || !box_id) return IE_INVAL;
6531 if (!response) return IE_INVAL;
6533 /* Free output argument */
6534 xmlFreeDoc(*response); *response = NULL;
6536 /* Prepare strings */
6537 service_name_locale = _isds_utf82locale((char*)service_name);
6538 if (!service_name_locale) {
6539 err = IE_NOMEM;
6540 goto leave;
6542 box_id_locale = _isds_utf82locale((char*)box_id);
6543 if (!box_id_locale) {
6544 err = IE_NOMEM;
6545 goto leave;
6548 /* Build request */
6549 request = xmlNewNode(NULL, service_name);
6550 if (!request) {
6551 isds_printf_message(context,
6552 _("Could not build %s request for %s box"), service_name_locale,
6553 box_id_locale);
6554 err = IE_ERROR;
6555 goto leave;
6557 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6558 if(!isds_ns) {
6559 isds_log_message(context, _("Could not create ISDS name space"));
6560 err = IE_ERROR;
6561 goto leave;
6563 xmlSetNs(request, isds_ns);
6565 /* Add XSD:tIdDbInput children */
6566 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6567 INSERT_STRING(request, box_id_element, box_id);
6568 err = insert_GExtApproval(context, approval, request);
6569 if (err) goto leave;
6571 /* Send request and check response*/
6572 err = send_destroy_request_check_response(context,
6573 service, service_name, &request, response, refnumber, NULL);
6575 leave:
6576 free(service_name_locale);
6577 free(box_id_locale);
6578 xmlFreeNode(request);
6579 return err;
6581 #endif /* HAVE_LIBCURL */
6584 /* Get data about all users assigned to given box.
6585 * @context is session context
6586 * @box_id is box ID
6587 * @users is automatically reallocated list of struct isds_DbUserInfo */
6588 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6589 struct isds_list **users) {
6590 isds_error err = IE_SUCCESS;
6591 #if HAVE_LIBCURL
6592 xmlDocPtr response = NULL;
6593 xmlXPathContextPtr xpath_ctx = NULL;
6594 xmlXPathObjectPtr result = NULL;
6595 int i;
6596 struct isds_list *item, *prev_item = NULL;
6597 #endif
6599 if (!context) return IE_INVALID_CONTEXT;
6600 zfree(context->long_message);
6601 if (!users || !box_id) return IE_INVAL;
6602 isds_list_free(users);
6605 #if HAVE_LIBCURL
6606 /* Do request and check for success */
6607 err = build_send_dbid_request_check_response(context,
6608 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6609 BAD_CAST box_id, NULL, &response, NULL);
6610 if (err) goto leave;
6613 /* Extract data */
6614 /* Prepare structure */
6615 xpath_ctx = xmlXPathNewContext(response);
6616 if (!xpath_ctx) {
6617 err = IE_ERROR;
6618 goto leave;
6620 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6621 err = IE_ERROR;
6622 goto leave;
6625 /* Set context node */
6626 result = xmlXPathEvalExpression(BAD_CAST
6627 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6628 xpath_ctx);
6629 if (!result) {
6630 err = IE_ERROR;
6631 goto leave;
6633 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6634 /* Iterate over all users */
6635 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6637 /* Prepare structure */
6638 item = calloc(1, sizeof(*item));
6639 if (!item) {
6640 err = IE_NOMEM;
6641 goto leave;
6643 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6644 if (i == 0) *users = item;
6645 else prev_item->next = item;
6646 prev_item = item;
6648 /* Extract it */
6649 xpath_ctx->node = result->nodesetval->nodeTab[i];
6650 err = extract_DbUserInfo(context,
6651 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6652 if (err) goto leave;
6656 leave:
6657 if (err) {
6658 isds_list_free(users);
6661 xmlXPathFreeObject(result);
6662 xmlXPathFreeContext(xpath_ctx);
6663 xmlFreeDoc(response);
6665 if (!err)
6666 isds_log(ILF_ISDS, ILL_DEBUG,
6667 _("GetDataBoxUsers request processed by server "
6668 "successfully.\n"));
6669 #else /* not HAVE_LIBCURL */
6670 err = IE_NOTSUP;
6671 #endif
6673 return err;
6677 /* Update data about user assigned to given box.
6678 * @context is session context
6679 * @box is box identification. aifoIsds, address->adCode,
6680 * address->adDistrict members are ignored.
6681 * @old_user identifies user to update, aifo_ticket member is ignored
6682 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6683 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6684 * NULL, if you don't care.*/
6685 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6686 const struct isds_DbOwnerInfo *box,
6687 const struct isds_DbUserInfo *old_user,
6688 const struct isds_DbUserInfo *new_user,
6689 char **refnumber) {
6690 isds_error err = IE_SUCCESS;
6691 #if HAVE_LIBCURL
6692 xmlNsPtr isds_ns = NULL;
6693 xmlNodePtr request = NULL;
6694 xmlNodePtr node;
6695 #endif
6698 if (!context) return IE_INVALID_CONTEXT;
6699 zfree(context->long_message);
6700 if (!box || !old_user || !new_user) return IE_INVAL;
6703 #if HAVE_LIBCURL
6704 /* Build UpdateDataBoxUser request */
6705 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6706 if (!request) {
6707 isds_log_message(context,
6708 _("Could build UpdateDataBoxUser request"));
6709 return IE_ERROR;
6711 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6712 if(!isds_ns) {
6713 isds_log_message(context, _("Could not create ISDS name space"));
6714 xmlFreeNode(request);
6715 return IE_ERROR;
6717 xmlSetNs(request, isds_ns);
6719 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6720 err = insert_DbOwnerInfo(context, box, node);
6721 if (err) goto leave;
6723 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6724 err = insert_DbUserInfo(context, old_user, 0, node);
6725 if (err) goto leave;
6727 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6728 err = insert_DbUserInfo(context, new_user, 0, node);
6729 if (err) goto leave;
6731 /* Send it to server and process response */
6732 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6733 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6735 leave:
6736 xmlFreeNode(request);
6737 #else /* not HAVE_LIBCURL */
6738 err = IE_NOTSUP;
6739 #endif
6741 return err;
6745 /* Undocumented function.
6746 * @context is session context
6747 * @box_id is UTF-8 encoded box identifier
6748 * @token is UTF-8 encoded temporary password
6749 * @user_id outputs UTF-8 encoded reallocated user identifier
6750 * @password outpus UTF-8 encoded reallocated user password
6751 * Output arguments will be nulled in case of error */
6752 isds_error isds_activate(struct isds_ctx *context,
6753 const char *box_id, const char *token,
6754 char **user_id, char **password) {
6755 isds_error err = IE_SUCCESS;
6756 #if HAVE_LIBCURL
6757 xmlNsPtr isds_ns = NULL;
6758 xmlNodePtr request = NULL, node;
6759 xmlDocPtr response = NULL;
6760 xmlXPathContextPtr xpath_ctx = NULL;
6761 xmlXPathObjectPtr result = NULL;
6762 #endif
6765 if (!context) return IE_INVALID_CONTEXT;
6766 zfree(context->long_message);
6768 if (user_id) zfree(*user_id);
6769 if (password) zfree(*password);
6771 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6774 #if HAVE_LIBCURL
6775 /* Build Activate request */
6776 request = xmlNewNode(NULL, BAD_CAST "Activate");
6777 if (!request) {
6778 isds_log_message(context, _("Could build Activate request"));
6779 return IE_ERROR;
6781 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6782 if(!isds_ns) {
6783 isds_log_message(context, _("Could not create ISDS name space"));
6784 xmlFreeNode(request);
6785 return IE_ERROR;
6787 xmlSetNs(request, isds_ns);
6789 INSERT_STRING(request, "dbAccessDataId", token);
6790 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6791 INSERT_STRING(request, "dbID", box_id);
6794 /* Send request and check response*/
6795 err = send_destroy_request_check_response(context,
6796 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6797 &response, NULL, NULL);
6798 if (err) goto leave;
6801 /* Extract data */
6802 xpath_ctx = xmlXPathNewContext(response);
6803 if (!xpath_ctx) {
6804 err = IE_ERROR;
6805 goto leave;
6807 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6808 err = IE_ERROR;
6809 goto leave;
6811 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6812 xpath_ctx);
6813 if (!result) {
6814 err = IE_ERROR;
6815 goto leave;
6817 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6818 isds_log_message(context, _("Missing ActivateResponse element"));
6819 err = IE_ISDS;
6820 goto leave;
6822 if (result->nodesetval->nodeNr > 1) {
6823 isds_log_message(context, _("Multiple ActivateResponse element"));
6824 err = IE_ISDS;
6825 goto leave;
6827 xpath_ctx->node = result->nodesetval->nodeTab[0];
6828 xmlXPathFreeObject(result); result = NULL;
6830 EXTRACT_STRING("isds:userId", *user_id);
6831 if (!*user_id)
6832 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6833 "but did not return `userId' element.\n"));
6835 EXTRACT_STRING("isds:password", *password);
6836 if (!*password)
6837 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6838 "but did not return `password' element.\n"));
6840 leave:
6841 xmlXPathFreeObject(result);
6842 xmlXPathFreeContext(xpath_ctx);
6843 xmlFreeDoc(response);
6844 xmlFreeNode(request);
6846 if (!err)
6847 isds_log(ILF_ISDS, ILL_DEBUG,
6848 _("Activate request processed by server successfully.\n"));
6849 #else /* not HAVE_LIBCURL */
6850 err = IE_NOTSUP;
6851 #endif
6853 return err;
6857 /* Reset credentials of user assigned to given box.
6858 * @context is session context
6859 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
6860 * members are ignored.
6861 * @user identifies user to reset password, aifo_ticket member is ignored
6862 * @fee_paid is true if fee has been paid, false otherwise
6863 * @approval is optional external approval of box manipulation
6864 * @credentials_delivery is NULL if new password should be delivered off-line
6865 * to the user. It is valid pointer if user should obtain new password on-line
6866 * on dedicated web server. Then input @credentials_delivery.email value is
6867 * user's e-mail address user must provide to dedicated web server together
6868 * with @credentials_delivery.token. The output reallocated token user needs
6869 * to use to authorize on the web server to view his new password. Output
6870 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6871 * ISDS changed up on this call. (No reason why server could change the name
6872 * is known now.)
6873 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6874 * NULL, if you don't care.*/
6875 isds_error isds_reset_password(struct isds_ctx *context,
6876 const struct isds_DbOwnerInfo *box,
6877 const struct isds_DbUserInfo *user,
6878 const _Bool fee_paid, const struct isds_approval *approval,
6879 struct isds_credentials_delivery *credentials_delivery,
6880 char **refnumber) {
6881 isds_error err = IE_SUCCESS;
6882 #if HAVE_LIBCURL
6883 xmlNsPtr isds_ns = NULL;
6884 xmlNodePtr request = NULL, node;
6885 xmlDocPtr response = NULL;
6886 #endif
6889 if (!context) return IE_INVALID_CONTEXT;
6890 zfree(context->long_message);
6892 if (credentials_delivery) {
6893 zfree(credentials_delivery->token);
6894 zfree(credentials_delivery->new_user_name);
6896 if (!box || !user) return IE_INVAL;
6899 #if HAVE_LIBCURL
6900 /* Build NewAccessData request */
6901 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6902 if (!request) {
6903 isds_log_message(context,
6904 _("Could build NewAccessData request"));
6905 return IE_ERROR;
6907 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6908 if(!isds_ns) {
6909 isds_log_message(context, _("Could not create ISDS name space"));
6910 xmlFreeNode(request);
6911 return IE_ERROR;
6913 xmlSetNs(request, isds_ns);
6915 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6916 err = insert_DbOwnerInfo(context, box, node);
6917 if (err) goto leave;
6919 INSERT_ELEMENT(node, request, "dbUserInfo");
6920 err = insert_DbUserInfo(context, user, 0, node);
6921 if (err) goto leave;
6923 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6925 err = insert_credentials_delivery(context, credentials_delivery, request);
6926 if (err) goto leave;
6928 err = insert_GExtApproval(context, approval, request);
6929 if (err) goto leave;
6931 /* Send request and check response*/
6932 err = send_destroy_request_check_response(context,
6933 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6934 &response, (xmlChar **) refnumber, NULL);
6935 if (err) goto leave;
6938 /* Extract optional token */
6939 err = extract_credentials_delivery(context, credentials_delivery,
6940 response, "NewAccessData");
6942 leave:
6943 xmlFreeDoc(response);
6944 xmlFreeNode(request);
6946 if (!err)
6947 isds_log(ILF_ISDS, ILL_DEBUG,
6948 _("NewAccessData request processed by server "
6949 "successfully.\n"));
6950 #else /* not HAVE_LIBCURL */
6951 err = IE_NOTSUP;
6952 #endif
6954 return err;
6958 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6959 * code, destroy response and log success.
6960 * @context is ISDS session context.
6961 * @service_name is name of SERVICE_DB_MANIPULATION service
6962 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
6963 * members are ignored.
6964 * @user identifies user to remove
6965 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
6966 * @credentials_delivery is NULL if new user's password should be delivered
6967 * off-line to the user. It is valid pointer if user should obtain new
6968 * password on-line on dedicated web server. Then input
6969 * @credentials_delivery.email value is user's e-mail address user must
6970 * provide to dedicated web server together with @credentials_delivery.token.
6971 * The output reallocated token user needs to use to authorize on the web
6972 * server to view his new password. Output reallocated
6973 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6974 * assingned or changed up on this call.
6975 * @approval is optional external approval of box manipulation
6976 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6977 * NULL, if you don't care. */
6978 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6979 struct isds_ctx *context, const xmlChar *service_name,
6980 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6981 _Bool honor_aifo_ticket,
6982 struct isds_credentials_delivery *credentials_delivery,
6983 const struct isds_approval *approval, xmlChar **refnumber) {
6984 isds_error err = IE_SUCCESS;
6985 #if HAVE_LIBCURL
6986 xmlNsPtr isds_ns = NULL;
6987 xmlNodePtr request = NULL, node;
6988 xmlDocPtr response = NULL;
6989 #endif
6992 if (!context) return IE_INVALID_CONTEXT;
6993 zfree(context->long_message);
6994 if (credentials_delivery) {
6995 zfree(credentials_delivery->token);
6996 zfree(credentials_delivery->new_user_name);
6998 if (!service_name || service_name[0] == '\0' || !box || !user)
6999 return IE_INVAL;
7002 #if HAVE_LIBCURL
7003 /* Build NewAccessData or similar request */
7004 request = xmlNewNode(NULL, service_name);
7005 if (!request) {
7006 char *service_name_locale = _isds_utf82locale((char *) service_name);
7007 isds_printf_message(context, _("Could not build %s request"),
7008 service_name_locale);
7009 free(service_name_locale);
7010 return IE_ERROR;
7012 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7013 if(!isds_ns) {
7014 isds_log_message(context, _("Could not create ISDS name space"));
7015 xmlFreeNode(request);
7016 return IE_ERROR;
7018 xmlSetNs(request, isds_ns);
7020 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7021 err = insert_DbOwnerInfo(context, box, node);
7022 if (err) goto leave;
7024 INSERT_ELEMENT(node, request, "dbUserInfo");
7025 err = insert_DbUserInfo(context, user, honor_aifo_ticket, node);
7026 if (err) goto leave;
7028 err = insert_credentials_delivery(context, credentials_delivery, request);
7029 if (err) goto leave;
7031 err = insert_GExtApproval(context, approval, request);
7032 if (err) goto leave;
7035 /* Send request and check response*/
7036 err = send_destroy_request_check_response(context,
7037 SERVICE_DB_MANIPULATION, service_name, &request, &response,
7038 refnumber, NULL);
7040 xmlFreeNode(request);
7041 request = NULL;
7043 /* Pick up credentials_delivery if requested */
7044 err = extract_credentials_delivery(context, credentials_delivery, response,
7045 (char *)service_name);
7047 leave:
7048 xmlFreeDoc(response);
7049 if (request) xmlFreeNode(request);
7051 if (!err) {
7052 char *service_name_locale = _isds_utf82locale((char *) service_name);
7053 isds_log(ILF_ISDS, ILL_DEBUG,
7054 _("%s request processed by server successfully.\n"),
7055 service_name_locale);
7056 free(service_name_locale);
7058 #else /* not HAVE_LIBCURL */
7059 err = IE_NOTSUP;
7060 #endif
7062 return err;
7066 /* Assign new user to given box.
7067 * @context is session context
7068 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7069 * members are ignored.
7070 * @user defines new user to add
7071 * @credentials_delivery is NULL if new user's password should be delivered
7072 * off-line to the user. It is valid pointer if user should obtain new
7073 * password on-line on dedicated web server. Then input
7074 * @credentials_delivery.email value is user's e-mail address user must
7075 * provide to dedicated web server together with @credentials_delivery.token.
7076 * The output reallocated token user needs to use to authorize on the web
7077 * server to view his new password. Output reallocated
7078 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7079 * assingned up on this call.
7080 * @approval is optional external approval of box manipulation
7081 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7082 * NULL, if you don't care.*/
7083 isds_error isds_add_user(struct isds_ctx *context,
7084 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7085 struct isds_credentials_delivery *credentials_delivery,
7086 const struct isds_approval *approval, char **refnumber) {
7087 return build_send_manipulationboxuser_request_check_drop_response(context,
7088 BAD_CAST "AddDataBoxUser", box, user, 1, credentials_delivery,
7089 approval, (xmlChar **) refnumber);
7093 /* Remove user assigned to given box.
7094 * @context is session context
7095 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7096 * members are ignored.
7097 * @user identifies user to remove, aifo_ticket member is ignored
7098 * @approval is optional external approval of box manipulation
7099 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7100 * NULL, if you don't care.*/
7101 isds_error isds_delete_user(struct isds_ctx *context,
7102 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7103 const struct isds_approval *approval, char **refnumber) {
7104 return build_send_manipulationboxuser_request_check_drop_response(context,
7105 BAD_CAST "DeleteDataBoxUser", box, user, 0, NULL, approval,
7106 (xmlChar **) refnumber);
7110 /* Get list of boxes in ZIP archive.
7111 * @context is session context
7112 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7113 * System recognizes following values currently: ALL (all boxes), UPG
7114 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7115 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7116 * commercial messages). This argument is a string because specification
7117 * states new values can appear in the future. Not all list types are
7118 * available to all users.
7119 * @buffer is automatically reallocated memory to store the list of boxes. The
7120 * list is zipped CSV file.
7121 * @buffer_length is size of @buffer data in bytes.
7122 * In case of error @buffer will be freed and @buffer_length will be
7123 * undefined.*/
7124 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7125 const char *list_identifier, void **buffer, size_t *buffer_length) {
7126 isds_error err = IE_SUCCESS;
7127 #if HAVE_LIBCURL
7128 xmlNsPtr isds_ns = NULL;
7129 xmlNodePtr request = NULL, node;
7130 xmlDocPtr response = NULL;
7131 xmlXPathContextPtr xpath_ctx = NULL;
7132 xmlXPathObjectPtr result = NULL;
7133 char *string = NULL;
7134 #endif
7137 if (!context) return IE_INVALID_CONTEXT;
7138 zfree(context->long_message);
7139 if (buffer) zfree(*buffer);
7140 if (!buffer || !buffer_length) return IE_INVAL;
7143 #if HAVE_LIBCURL
7144 /* Check if connection is established
7145 * TODO: This check should be done downstairs. */
7146 if (!context->curl) return IE_CONNECTION_CLOSED;
7149 /* Build AuthenticateMessage request */
7150 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7151 if (!request) {
7152 isds_log_message(context,
7153 _("Could not build GetDataBoxList request"));
7154 return IE_ERROR;
7156 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7157 if(!isds_ns) {
7158 isds_log_message(context, _("Could not create ISDS name space"));
7159 xmlFreeNode(request);
7160 return IE_ERROR;
7162 xmlSetNs(request, isds_ns);
7163 INSERT_STRING(request, "dblType", list_identifier);
7165 /* Send request to server and process response */
7166 err = send_destroy_request_check_response(context,
7167 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7168 &response, NULL, NULL);
7169 if (err) goto leave;
7172 /* Extract Base-64 encoded ZIP file */
7173 xpath_ctx = xmlXPathNewContext(response);
7174 if (!xpath_ctx) {
7175 err = IE_ERROR;
7176 goto leave;
7178 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7179 err = IE_ERROR;
7180 goto leave;
7182 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7184 /* Decode non-empty archive */
7185 if (string && string[0] != '\0') {
7186 *buffer_length = _isds_b64decode(string, buffer);
7187 if (*buffer_length == (size_t) -1) {
7188 isds_printf_message(context,
7189 _("Error while Base64-decoding box list archive"));
7190 err = IE_ERROR;
7191 goto leave;
7196 leave:
7197 free(string);
7198 xmlXPathFreeObject(result);
7199 xmlXPathFreeContext(xpath_ctx);
7200 xmlFreeDoc(response);
7201 xmlFreeNode(request);
7203 if (!err) {
7204 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7205 "processed by server successfully.\n"));
7207 #else /* not HAVE_LIBCURL */
7208 err = IE_NOTSUP;
7209 #endif
7211 return err;
7215 /* Find boxes suiting given criteria.
7216 * @criteria is filter. You should fill in at least some members. aifoIsds,
7217 * address->adCode, address->adDistrict members are ignored.
7218 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7219 * possibly empty. Input NULL or valid old structure.
7220 * @return:
7221 * IE_SUCCESS if search succeeded, @boxes contains useful data
7222 * IE_NOEXIST if no such box exists, @boxes will be NULL
7223 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7224 * contains still valid data
7225 * other code if something bad happens. @boxes will be NULL. */
7226 isds_error isds_FindDataBox(struct isds_ctx *context,
7227 const struct isds_DbOwnerInfo *criteria,
7228 struct isds_list **boxes) {
7229 isds_error err = IE_SUCCESS;
7230 #if HAVE_LIBCURL
7231 _Bool truncated = 0;
7232 xmlNsPtr isds_ns = NULL;
7233 xmlNodePtr request = NULL;
7234 xmlDocPtr response = NULL;
7235 xmlChar *code = NULL, *message = NULL;
7236 xmlNodePtr db_owner_info;
7237 xmlXPathContextPtr xpath_ctx = NULL;
7238 xmlXPathObjectPtr result = NULL;
7239 xmlChar *string = NULL;
7240 #endif
7243 if (!context) return IE_INVALID_CONTEXT;
7244 zfree(context->long_message);
7245 if (!boxes) return IE_INVAL;
7246 isds_list_free(boxes);
7248 if (!criteria) {
7249 return IE_INVAL;
7252 #if HAVE_LIBCURL
7253 /* Check if connection is established
7254 * TODO: This check should be done downstairs. */
7255 if (!context->curl) return IE_CONNECTION_CLOSED;
7258 /* Build FindDataBox request */
7259 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
7260 if (!request) {
7261 isds_log_message(context,
7262 _("Could build FindDataBox request"));
7263 return IE_ERROR;
7265 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7266 if(!isds_ns) {
7267 isds_log_message(context, _("Could not create ISDS name space"));
7268 xmlFreeNode(request);
7269 return IE_ERROR;
7271 xmlSetNs(request, isds_ns);
7272 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7273 if (!db_owner_info) {
7274 isds_log_message(context, _("Could not add dbOwnerInfo child to "
7275 "FindDataBox element"));
7276 xmlFreeNode(request);
7277 return IE_ERROR;
7280 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
7281 if (err) goto leave;
7284 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
7286 /* Sent request */
7287 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7289 /* Destroy request */
7290 xmlFreeNode(request); request = NULL;
7292 if (err) {
7293 isds_log(ILF_ISDS, ILL_DEBUG,
7294 _("Processing ISDS response on FindDataBox "
7295 "request failed\n"));
7296 goto leave;
7299 /* Check for response status */
7300 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7301 &code, &message, NULL);
7302 if (err) {
7303 isds_log(ILF_ISDS, ILL_DEBUG,
7304 _("ISDS response on FindDataBox request is missing status\n"));
7305 goto leave;
7308 /* Request processed, but nothing found */
7309 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7310 !xmlStrcmp(code, BAD_CAST "5001")) {
7311 char *code_locale = _isds_utf82locale((char*)code);
7312 char *message_locale = _isds_utf82locale((char*)message);
7313 isds_log(ILF_ISDS, ILL_DEBUG,
7314 _("Server did not found any box on FindDataBox request "
7315 "(code=%s, message=%s)\n"), code_locale, message_locale);
7316 isds_log_message(context, message_locale);
7317 free(code_locale);
7318 free(message_locale);
7319 err = IE_NOEXIST;
7320 goto leave;
7323 /* Warning, not a error */
7324 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7325 char *code_locale = _isds_utf82locale((char*)code);
7326 char *message_locale = _isds_utf82locale((char*)message);
7327 isds_log(ILF_ISDS, ILL_DEBUG,
7328 _("Server truncated response on FindDataBox request "
7329 "(code=%s, message=%s)\n"), code_locale, message_locale);
7330 isds_log_message(context, message_locale);
7331 free(code_locale);
7332 free(message_locale);
7333 truncated = 1;
7336 /* Other error */
7337 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7338 char *code_locale = _isds_utf82locale((char*)code);
7339 char *message_locale = _isds_utf82locale((char*)message);
7340 isds_log(ILF_ISDS, ILL_DEBUG,
7341 _("Server refused FindDataBox request "
7342 "(code=%s, message=%s)\n"), code_locale, message_locale);
7343 isds_log_message(context, message_locale);
7344 free(code_locale);
7345 free(message_locale);
7346 err = IE_ISDS;
7347 goto leave;
7350 xpath_ctx = xmlXPathNewContext(response);
7351 if (!xpath_ctx) {
7352 err = IE_ERROR;
7353 goto leave;
7355 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7356 err = IE_ERROR;
7357 goto leave;
7360 /* Extract boxes if they present */
7361 result = xmlXPathEvalExpression(BAD_CAST
7362 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7363 xpath_ctx);
7364 if (!result) {
7365 err = IE_ERROR;
7366 goto leave;
7368 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7369 struct isds_list *item, *prev_item = NULL;
7370 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7371 item = calloc(1, sizeof(*item));
7372 if (!item) {
7373 err = IE_NOMEM;
7374 goto leave;
7377 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7378 if (i == 0) *boxes = item;
7379 else prev_item->next = item;
7380 prev_item = item;
7382 xpath_ctx->node = result->nodesetval->nodeTab[i];
7383 err = extract_DbOwnerInfo(context,
7384 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7385 if (err) goto leave;
7389 leave:
7390 if (err) {
7391 isds_list_free(boxes);
7392 } else {
7393 if (truncated) err = IE_2BIG;
7396 free(string);
7397 xmlFreeNode(request);
7398 xmlXPathFreeObject(result);
7399 xmlXPathFreeContext(xpath_ctx);
7401 free(code);
7402 free(message);
7403 xmlFreeDoc(response);
7405 if (!err)
7406 isds_log(ILF_ISDS, ILL_DEBUG,
7407 _("FindDataBox request processed by server successfully.\n"));
7408 #else /* not HAVE_LIBCURL */
7409 err = IE_NOTSUP;
7410 #endif
7412 return err;
7416 #if HAVE_LIBCURL
7417 /* Convert a string with match markers into a plain string with list of
7418 * pointers to the matches
7419 * @string is an UTF-8 encoded non-constant string with match markers
7420 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7421 * The markers will be removed from the string.
7422 * @starts is a reallocated list of static pointers into the @string pointing
7423 * to places where match start markers occured.
7424 * @ends is a reallocated list of static pointers into the @string pointing
7425 * to places where match end markers occured.
7426 * @return IE_SUCCESS in case of no failure. */
7427 static isds_error interpret_matches(xmlChar *string,
7428 struct isds_list **starts, struct isds_list **ends) {
7429 isds_error err = IE_SUCCESS;
7430 xmlChar *pointer, *destination, *source;
7431 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7433 isds_list_free(starts);
7434 isds_list_free(ends);
7435 if (NULL == starts || NULL == ends) return IE_INVAL;
7436 if (NULL == string) return IE_SUCCESS;
7438 for (pointer = string; *pointer != '\0';) {
7439 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7440 /* Remove the start marker */
7441 for (source = pointer + 14, destination = pointer;
7442 *source != '\0'; source++, destination++) {
7443 *destination = *source;
7445 *destination = '\0';
7446 /* Append the pointer into the list */
7447 item = calloc(1, sizeof(*item));
7448 if (!item) {
7449 err = IE_NOMEM;
7450 goto leave;
7452 item->destructor = (void (*)(void **))NULL;
7453 item->data = pointer;
7454 if (NULL == prev_start) *starts = item;
7455 else prev_start->next = item;
7456 prev_start = item;
7457 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7458 /* Remove the end marker */
7459 for (source = pointer + 12, destination = pointer;
7460 *source != '\0'; source++, destination++) {
7461 *destination = *source;
7463 *destination = '\0';
7464 /* Append the pointer into the list */
7465 item = calloc(1, sizeof(*item));
7466 if (!item) {
7467 err = IE_NOMEM;
7468 goto leave;
7470 item->destructor = (void (*)(void **))NULL;
7471 item->data = pointer;
7472 if (NULL == prev_end) *ends = item;
7473 else prev_end->next = item;
7474 prev_end = item;
7475 } else {
7476 pointer++;
7480 leave:
7481 if (err) {
7482 isds_list_free(starts);
7483 isds_list_free(ends);
7485 return err;
7489 /* Convert isds:dbResult XML tree into structure
7490 * @context is ISDS context.
7491 * @fulltext_result is automatically reallocated found box structure.
7492 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7493 * @collect_matches is true to interpret match markers.
7494 * In case of error @result will be freed. */
7495 static isds_error extract_dbResult(struct isds_ctx *context,
7496 struct isds_fulltext_result **fulltext_result,
7497 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7498 isds_error err = IE_SUCCESS;
7499 xmlXPathObjectPtr result = NULL;
7500 char *string = NULL;
7502 if (NULL == context) return IE_INVALID_CONTEXT;
7503 if (NULL == fulltext_result) return IE_INVAL;
7504 isds_fulltext_result_free(fulltext_result);
7505 if (!xpath_ctx) return IE_INVAL;
7508 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7509 if (NULL == *fulltext_result) {
7510 err = IE_NOMEM;
7511 goto leave;
7514 /* Extract data */
7515 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7517 EXTRACT_STRING("isds:dbType", string);
7518 if (NULL == string) {
7519 err = IE_ISDS;
7520 isds_log_message(context, _("Empty isds:dbType element"));
7521 goto leave;
7523 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7524 if (err) {
7525 if (err == IE_ENUM) {
7526 err = IE_ISDS;
7527 char *string_locale = _isds_utf82locale(string);
7528 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7529 string_locale);
7530 free(string_locale);
7532 goto leave;
7534 zfree(string);
7536 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7537 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7539 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7540 if (err) goto leave;
7542 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7543 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7544 (*fulltext_result)->dbEffectiveOVM);
7546 EXTRACT_STRING("isds:dbSendOptions", string);
7547 if (NULL == string) {
7548 err = IE_ISDS;
7549 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7550 goto leave;
7552 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7553 (*fulltext_result)->active = 1;
7554 (*fulltext_result)->public_sending = 1;
7555 (*fulltext_result)->commercial_sending = 0;
7556 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7557 (*fulltext_result)->active = 1;
7558 (*fulltext_result)->public_sending = 1;
7559 (*fulltext_result)->commercial_sending = 1;
7560 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7561 (*fulltext_result)->active = 1;
7562 (*fulltext_result)->public_sending = 0;
7563 (*fulltext_result)->commercial_sending = 1;
7564 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7565 (*fulltext_result)->active = 1;
7566 (*fulltext_result)->public_sending = 0;
7567 (*fulltext_result)->commercial_sending = 0;
7568 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7569 (*fulltext_result)->active = 0;
7570 (*fulltext_result)->public_sending = 0;
7571 (*fulltext_result)->commercial_sending = 0;
7572 } else {
7573 err = IE_ISDS;
7574 char *string_locale = _isds_utf82locale(string);
7575 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7576 string_locale);
7577 free(string_locale);
7578 goto leave;
7580 zfree(string);
7582 /* Interpret match marks */
7583 if (collect_matches) {
7584 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7585 &((*fulltext_result)->name_match_start),
7586 &((*fulltext_result)->name_match_end));
7587 if (err) goto leave;
7588 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7589 &((*fulltext_result)->address_match_start),
7590 &((*fulltext_result)->address_match_end));
7591 if (err) goto leave;
7594 leave:
7595 if (err) isds_fulltext_result_free(fulltext_result);
7596 free(string);
7597 xmlXPathFreeObject(result);
7598 return err;
7600 #endif /* HAVE_LIBCURL */
7603 /* Find boxes matching a given full-text criteria.
7604 * @context is a session context
7605 * @query is a non-empty string which consists of words to search
7606 * @target selects box attributes to search for @query words. Pass NULL if you
7607 * don't care.
7608 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7609 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7610 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7611 * which is DBTYPE_SYSTEM.
7612 * @page_size defines count of boxes to constitute a response page. It counts
7613 * from zero. Pass NULL to let server to use a default value (50 now).
7614 * @page_number defines ordinar number of the response page to return. It
7615 * counts from zero. Pass NULL to let server to use a default value (0 now).
7616 * @track_matches points to true for marking @query words found in the box
7617 * attributes. It points to false for not marking. Pass NULL to let the server
7618 * to use default value (false now).
7619 * @total_matching_boxes outputs reallocated number of all boxes matching the
7620 * query. Will be pointer to NULL if server did not provide the value.
7621 * Pass NULL if you don't care.
7622 * @current_page_beginning outputs reallocated ordinar number of the first box
7623 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7624 * server did not provide the value. Pass NULL if you don't care.
7625 * @current_page_size outputs reallocated count of boxes in the this @boxes
7626 * page. It will be pointer to NULL if the server did not provide the value.
7627 * Pass NULL if you don't care.
7628 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7629 * is the last one, false if more boxes match, NULL if the server did not
7630 * provude the value. Pass NULL if you don't care.
7631 * @boxes outputs reallocated list of isds_fulltext_result structures,
7632 * possibly empty.
7633 * @return:
7634 * IE_SUCCESS if search succeeded
7635 * IE_2BIG if @page_size is too large
7636 * other code if something bad happens; output arguments will be NULL. */
7637 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7638 const char *query,
7639 const isds_fulltext_target *target,
7640 const isds_DbType *box_type,
7641 const unsigned long int *page_size,
7642 const unsigned long int *page_number,
7643 const _Bool *track_matches,
7644 unsigned long int **total_matching_boxes,
7645 unsigned long int **current_page_beginning,
7646 unsigned long int **current_page_size,
7647 _Bool **last_page,
7648 struct isds_list **boxes) {
7649 isds_error err = IE_SUCCESS;
7650 #if HAVE_LIBCURL
7651 xmlNsPtr isds_ns = NULL;
7652 xmlNodePtr request = NULL;
7653 xmlDocPtr response = NULL;
7654 xmlNodePtr node;
7655 xmlXPathContextPtr xpath_ctx = NULL;
7656 xmlXPathObjectPtr result = NULL;
7657 const xmlChar *static_string = NULL;
7658 xmlChar *string = NULL;
7660 const xmlChar *codes[] = {
7661 BAD_CAST "1004",
7662 BAD_CAST "1152",
7663 BAD_CAST "1153",
7664 BAD_CAST "1154",
7665 BAD_CAST "1155",
7666 BAD_CAST "1156",
7667 BAD_CAST "9002",
7668 NULL
7670 const char *meanings[] = {
7671 N_("You are not allowed to perform the search"),
7672 N_("The query string is empty"),
7673 N_("Searched box ID is malformed"),
7674 N_("Searched organization ID is malformed"),
7675 N_("Invalid input"),
7676 N_("Requested page size is too large"),
7677 N_("Search engine internal error")
7679 const isds_error errors[] = {
7680 IE_ISDS,
7681 IE_INVAL,
7682 IE_INVAL,
7683 IE_INVAL,
7684 IE_INVAL,
7685 IE_2BIG,
7686 IE_ISDS
7688 struct code_map_isds_error map = {
7689 .codes = codes,
7690 .meanings = meanings,
7691 .errors = errors
7693 #endif
7696 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7697 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7698 if (NULL != current_page_size) zfree(*current_page_size);
7699 if (NULL != last_page) zfree(*last_page);
7700 isds_list_free(boxes);
7702 if (NULL == context) return IE_INVALID_CONTEXT;
7703 zfree(context->long_message);
7705 if (NULL == boxes) return IE_INVAL;
7707 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
7708 isds_log_message(context, _("Query string must be non-empty"));
7709 return IE_INVAL;
7712 #if HAVE_LIBCURL
7713 /* Check if connection is established
7714 * TODO: This check should be done downstairs. */
7715 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7717 /* Build FindDataBox request */
7718 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
7719 if (NULL == request) {
7720 isds_log_message(context,
7721 _("Could not build ISDSSearch2 request"));
7722 return IE_ERROR;
7724 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7725 if(NULL == isds_ns) {
7726 isds_log_message(context, _("Could not create ISDS name space"));
7727 xmlFreeNode(request);
7728 return IE_ERROR;
7730 xmlSetNs(request, isds_ns);
7732 INSERT_STRING(request, "searchText", query);
7734 if (NULL != target) {
7735 static_string = isds_fulltext_target2string(*(target));
7736 if (NULL == static_string) {
7737 isds_printf_message(context, _("Invalid target value: %d"),
7738 *(target));
7739 err = IE_ENUM;
7740 goto leave;
7743 INSERT_STRING(request, "searchType", static_string);
7744 static_string = NULL;
7746 if (NULL != box_type) {
7747 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7748 if (DBTYPE_SYSTEM == *box_type) {
7749 static_string = BAD_CAST "ALL";
7750 } else if (DBTYPE_OVM_MAIN == *box_type) {
7751 static_string = BAD_CAST "OVM_MAIN";
7752 } else {
7753 static_string = isds_DbType2string(*(box_type));
7754 if (NULL == static_string) {
7755 isds_printf_message(context, _("Invalid box type value: %d"),
7756 *(box_type));
7757 err = IE_ENUM;
7758 goto leave;
7762 INSERT_STRING(request, "searchScope", static_string);
7763 static_string = NULL;
7765 INSERT_ULONGINT(request, "page", page_number, string);
7766 INSERT_ULONGINT(request, "pageSize", page_size, string);
7767 INSERT_BOOLEAN(request, "highlighting", track_matches);
7769 /* Send request and check response */
7770 err = send_destroy_request_check_response(context,
7771 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
7772 &request, &response, NULL, &map);
7773 if (err) goto leave;
7775 /* Parse response */
7776 xpath_ctx = xmlXPathNewContext(response);
7777 if (NULL == xpath_ctx) {
7778 err = IE_ERROR;
7779 goto leave;
7781 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7782 err = IE_ERROR;
7783 goto leave;
7785 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
7786 xpath_ctx);
7787 if (!result) {
7788 err = IE_ERROR;
7789 goto leave;
7791 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7792 isds_log_message(context, _("Missing ISDSSearch2 element"));
7793 err = IE_ISDS;
7794 goto leave;
7796 if (result->nodesetval->nodeNr > 1) {
7797 isds_log_message(context, _("Multiple ISDSSearch2 element"));
7798 err = IE_ISDS;
7799 goto leave;
7801 xpath_ctx->node = result->nodesetval->nodeTab[0];
7802 xmlXPathFreeObject(result); result = NULL;
7805 /* Extract counters */
7806 if (NULL != total_matching_boxes) {
7807 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
7809 if (NULL != current_page_size) {
7810 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
7812 if (NULL != current_page_beginning) {
7813 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
7815 if (NULL != last_page) {
7816 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
7818 xmlXPathFreeObject(result); result = NULL;
7820 /* Extract boxes if they present */
7821 result = xmlXPathEvalExpression(BAD_CAST
7822 "isds:dbResults/isds:dbResult", xpath_ctx);
7823 if (NULL == result) {
7824 err = IE_ERROR;
7825 goto leave;
7827 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7828 struct isds_list *item, *prev_item = NULL;
7829 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7830 item = calloc(1, sizeof(*item));
7831 if (!item) {
7832 err = IE_NOMEM;
7833 goto leave;
7836 item->destructor = (void (*)(void **))isds_fulltext_result_free;
7837 if (i == 0) *boxes = item;
7838 else prev_item->next = item;
7839 prev_item = item;
7841 xpath_ctx->node = result->nodesetval->nodeTab[i];
7842 err = extract_dbResult(context,
7843 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
7844 (NULL == track_matches) ? 0 : *track_matches);
7845 if (err) goto leave;
7849 leave:
7850 if (err) {
7851 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7852 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7853 if (NULL != current_page_size) zfree(*current_page_size);
7854 if (NULL != last_page) zfree(*last_page);
7855 isds_list_free(boxes);
7858 free(string);
7859 xmlFreeNode(request);
7860 xmlXPathFreeObject(result);
7861 xmlXPathFreeContext(xpath_ctx);
7862 xmlFreeDoc(response);
7864 if (!err)
7865 isds_log(ILF_ISDS, ILL_DEBUG,
7866 _("ISDSSearch2 request processed by server successfully.\n"));
7867 #else /* not HAVE_LIBCURL */
7868 err = IE_NOTSUP;
7869 #endif
7871 return err;
7875 /* Get status of a box.
7876 * @context is ISDS session context.
7877 * @box_id is UTF-8 encoded box identifier as zero terminated string
7878 * @box_status is return value of box status.
7879 * @return:
7880 * IE_SUCCESS if box has been found and its status retrieved
7881 * IE_NOEXIST if box is not known to ISDS server
7882 * or other appropriate error.
7883 * You can use isds_DbState to enumerate box status. However out of enum
7884 * range value can be returned too. This is feature because ISDS
7885 * specification leaves the set of values open.
7886 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7887 * the box has been deleted, but ISDS still lists its former existence. */
7888 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7889 long int *box_status) {
7890 isds_error err = IE_SUCCESS;
7891 #if HAVE_LIBCURL
7892 xmlNsPtr isds_ns = NULL;
7893 xmlNodePtr request = NULL, db_id;
7894 xmlDocPtr response = NULL;
7895 xmlXPathContextPtr xpath_ctx = NULL;
7896 xmlXPathObjectPtr result = NULL;
7897 xmlChar *string = NULL;
7899 const xmlChar *codes[] = {
7900 BAD_CAST "5001",
7901 BAD_CAST "1007",
7902 BAD_CAST "2011",
7903 NULL
7905 const char *meanings[] = {
7906 "The box does not exist",
7907 "Box ID is malformed",
7908 "Box ID malformed",
7910 const isds_error errors[] = {
7911 IE_NOEXIST,
7912 IE_INVAL,
7913 IE_INVAL,
7915 struct code_map_isds_error map = {
7916 .codes = codes,
7917 .meanings = meanings,
7918 .errors = errors
7920 #endif
7922 if (!context) return IE_INVALID_CONTEXT;
7923 zfree(context->long_message);
7924 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7926 #if HAVE_LIBCURL
7927 /* Check if connection is established
7928 * TODO: This check should be done downstairs. */
7929 if (!context->curl) return IE_CONNECTION_CLOSED;
7932 /* Build CheckDataBox request */
7933 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7934 if (!request) {
7935 isds_log_message(context,
7936 _("Could build CheckDataBox request"));
7937 return IE_ERROR;
7939 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7940 if(!isds_ns) {
7941 isds_log_message(context, _("Could not create ISDS name space"));
7942 xmlFreeNode(request);
7943 return IE_ERROR;
7945 xmlSetNs(request, isds_ns);
7946 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7947 if (!db_id) {
7948 isds_log_message(context, _("Could not add dbID child to "
7949 "CheckDataBox element"));
7950 xmlFreeNode(request);
7951 return IE_ERROR;
7955 /* Send request and check response*/
7956 err = send_destroy_request_check_response(context,
7957 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7958 &request, &response, NULL, &map);
7959 if (err) goto leave;
7962 /* Extract data */
7963 xpath_ctx = xmlXPathNewContext(response);
7964 if (!xpath_ctx) {
7965 err = IE_ERROR;
7966 goto leave;
7968 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7969 err = IE_ERROR;
7970 goto leave;
7972 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7973 xpath_ctx);
7974 if (!result) {
7975 err = IE_ERROR;
7976 goto leave;
7978 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7979 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7980 err = IE_ISDS;
7981 goto leave;
7983 if (result->nodesetval->nodeNr > 1) {
7984 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7985 err = IE_ISDS;
7986 goto leave;
7988 xpath_ctx->node = result->nodesetval->nodeTab[0];
7989 xmlXPathFreeObject(result); result = NULL;
7991 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7994 leave:
7995 free(string);
7996 xmlXPathFreeObject(result);
7997 xmlXPathFreeContext(xpath_ctx);
7999 xmlFreeDoc(response);
8001 if (!err)
8002 isds_log(ILF_ISDS, ILL_DEBUG,
8003 _("CheckDataBox request processed by server successfully.\n"));
8004 #else /* not HAVE_LIBCURL */
8005 err = IE_NOTSUP;
8006 #endif
8008 return err;
8012 /* Get list of permissions to send commercial messages.
8013 * @context is ISDS session context.
8014 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
8015 * @permissions is a reallocated list of permissions (struct
8016 * isds_commercial_permission*) to send commercial messages from @box_id. The
8017 * order of permissions is significant as the server applies the permissions
8018 * and associated pre-paid credits in the order. Empty list means no
8019 * permission.
8020 * @return:
8021 * IE_SUCCESS if the list has been obtained correctly,
8022 * or other appropriate error. */
8023 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
8024 const char *box_id, struct isds_list **permissions) {
8025 isds_error err = IE_SUCCESS;
8026 #if HAVE_LIBCURL
8027 xmlDocPtr response = NULL;
8028 xmlXPathContextPtr xpath_ctx = NULL;
8029 xmlXPathObjectPtr result = NULL;
8030 #endif
8032 if (!context) return IE_INVALID_CONTEXT;
8033 zfree(context->long_message);
8034 if (NULL == permissions) return IE_INVAL;
8035 isds_list_free(permissions);
8036 if (NULL == box_id) return IE_INVAL;
8038 #if HAVE_LIBCURL
8039 /* Check if connection is established */
8040 if (!context->curl) return IE_CONNECTION_CLOSED;
8042 /* Do request and check for success */
8043 err = build_send_dbid_request_check_response(context,
8044 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
8045 BAD_CAST box_id, NULL, &response, NULL);
8046 if (!err) {
8047 isds_log(ILF_ISDS, ILL_DEBUG,
8048 _("PDZInfo request processed by server successfully.\n"));
8051 /* Extract data */
8052 /* Prepare structure */
8053 xpath_ctx = xmlXPathNewContext(response);
8054 if (!xpath_ctx) {
8055 err = IE_ERROR;
8056 goto leave;
8058 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8059 err = IE_ERROR;
8060 goto leave;
8063 /* Set context node */
8064 result = xmlXPathEvalExpression(BAD_CAST
8065 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8066 xpath_ctx);
8067 if (!result) {
8068 err = IE_ERROR;
8069 goto leave;
8071 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8072 struct isds_list *prev_item = NULL;
8074 /* Iterate over all permission records */
8075 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8076 struct isds_list *item;
8078 /* Prepare structure */
8079 item = calloc(1, sizeof(*item));
8080 if (!item) {
8081 err = IE_NOMEM;
8082 goto leave;
8084 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8085 if (i == 0) *permissions = item;
8086 else prev_item->next = item;
8087 prev_item = item;
8089 /* Extract it */
8090 xpath_ctx->node = result->nodesetval->nodeTab[i];
8091 err = extract_DbPDZRecord(context,
8092 (struct isds_commercial_permission **) (&item->data),
8093 xpath_ctx);
8094 if (err) goto leave;
8098 leave:
8099 if (err) {
8100 isds_list_free(permissions);
8103 xmlXPathFreeObject(result);
8104 xmlXPathFreeContext(xpath_ctx);
8105 xmlFreeDoc(response);
8107 #else /* not HAVE_LIBCURL */
8108 err = IE_NOTSUP;
8109 #endif
8111 return err;
8115 /* Get details about credit for sending pre-paid commercial messages.
8116 * @context is ISDS session context.
8117 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8118 * @from_date is first day of credit history to return in @history. Only
8119 * tm_year, tm_mon and tm_mday carry sane value.
8120 * @to_date is last day of credit history to return in @history. Only
8121 * tm_year, tm_mon and tm_mday carry sane value.
8122 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8123 * if you don't care. This and all other credit values are integers in
8124 * hundredths of Czech Crowns.
8125 * @email outputs notification e-mail address where notifications about credit
8126 * are sent. This is automatically reallocated string. Pass NULL if you don't
8127 * care. It can return NULL if no address is defined.
8128 * @history outputs auto-reallocated list of pointers to struct
8129 * isds_credit_event. Events in closed interval @from_time to @to_time are
8130 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8131 * are sorted by time.
8132 * @return:
8133 * IE_SUCCESS if the credit details have been obtained correctly,
8134 * or other appropriate error. Please note that server allows to retrieve
8135 * only limited history of events. */
8136 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8137 const char *box_id,
8138 const struct tm *from_date, const struct tm *to_date,
8139 long int *credit, char **email, struct isds_list **history) {
8140 isds_error err = IE_SUCCESS;
8141 #if HAVE_LIBCURL
8142 char *box_id_locale = NULL;
8143 xmlNodePtr request = NULL, node;
8144 xmlNsPtr isds_ns = NULL;
8145 xmlChar *string = NULL;
8147 xmlDocPtr response = NULL;
8148 xmlXPathContextPtr xpath_ctx = NULL;
8149 xmlXPathObjectPtr result = NULL;
8151 const xmlChar *codes[] = {
8152 BAD_CAST "1004",
8153 BAD_CAST "2011",
8154 BAD_CAST "1093",
8155 BAD_CAST "1137",
8156 BAD_CAST "1058",
8157 NULL
8159 const char *meanings[] = {
8160 "Insufficient priviledges for the box",
8161 "The box does not exist",
8162 "Date is too long (history is not available after 15 months)",
8163 "Interval is too long (limit is 3 months)",
8164 "Invalid date"
8166 const isds_error errors[] = {
8167 IE_ISDS,
8168 IE_NOEXIST,
8169 IE_DATE,
8170 IE_DATE,
8171 IE_DATE,
8173 struct code_map_isds_error map = {
8174 .codes = codes,
8175 .meanings = meanings,
8176 .errors = errors
8178 #endif
8180 if (!context) return IE_INVALID_CONTEXT;
8181 zfree(context->long_message);
8183 /* Free output argument */
8184 if (NULL != credit) *credit = 0;
8185 if (NULL != email) zfree(*email);
8186 isds_list_free(history);
8188 if (NULL == box_id) return IE_INVAL;
8190 #if HAVE_LIBCURL
8191 /* Check if connection is established */
8192 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8194 box_id_locale = _isds_utf82locale((char*)box_id);
8195 if (NULL == box_id_locale) {
8196 err = IE_NOMEM;
8197 goto leave;
8200 /* Build request */
8201 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8202 if (NULL == request) {
8203 isds_printf_message(context,
8204 _("Could not build DataBoxCreditInfo request for %s box"),
8205 box_id_locale);
8206 err = IE_ERROR;
8207 goto leave;
8209 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8210 if(!isds_ns) {
8211 isds_log_message(context, _("Could not create ISDS name space"));
8212 err = IE_ERROR;
8213 goto leave;
8215 xmlSetNs(request, isds_ns);
8217 /* Add mandatory XSD:tIdDbInput child */
8218 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8219 /* Add mandatory dates elements with optional values */
8220 if (from_date) {
8221 err = tm2datestring(from_date, &string);
8222 if (err) {
8223 isds_log_message(context,
8224 _("Could not convert `from_date' argument to ISO date "
8225 "string"));
8226 goto leave;
8228 INSERT_STRING(request, "ciFromDate", string);
8229 zfree(string);
8230 } else {
8231 INSERT_STRING(request, "ciFromDate", NULL);
8233 if (to_date) {
8234 err = tm2datestring(to_date, &string);
8235 if (err) {
8236 isds_log_message(context,
8237 _("Could not convert `to_date' argument to ISO date "
8238 "string"));
8239 goto leave;
8241 INSERT_STRING(request, "ciTodate", string);
8242 zfree(string);
8243 } else {
8244 INSERT_STRING(request, "ciTodate", NULL);
8247 /* Send request and check response*/
8248 err = send_destroy_request_check_response(context,
8249 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8250 &request, &response, NULL, &map);
8251 if (err) goto leave;
8254 /* Extract data */
8255 /* Set context to the root */
8256 xpath_ctx = xmlXPathNewContext(response);
8257 if (!xpath_ctx) {
8258 err = IE_ERROR;
8259 goto leave;
8261 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8262 err = IE_ERROR;
8263 goto leave;
8265 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8266 xpath_ctx);
8267 if (!result) {
8268 err = IE_ERROR;
8269 goto leave;
8271 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8272 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8273 err = IE_ISDS;
8274 goto leave;
8276 if (result->nodesetval->nodeNr > 1) {
8277 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8278 err = IE_ISDS;
8279 goto leave;
8281 xpath_ctx->node = result->nodesetval->nodeTab[0];
8282 xmlXPathFreeObject(result); result = NULL;
8284 /* Extract common data */
8285 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8286 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8288 /* Extract records */
8289 if (NULL == history) goto leave;
8290 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8291 xpath_ctx);
8292 if (!result) {
8293 err = IE_ERROR;
8294 goto leave;
8296 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8297 struct isds_list *prev_item = NULL;
8299 /* Iterate over all records */
8300 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8301 struct isds_list *item;
8303 /* Prepare structure */
8304 item = calloc(1, sizeof(*item));
8305 if (!item) {
8306 err = IE_NOMEM;
8307 goto leave;
8309 item->destructor = (void(*)(void**))isds_credit_event_free;
8310 if (i == 0) *history = item;
8311 else prev_item->next = item;
8312 prev_item = item;
8314 /* Extract it */
8315 xpath_ctx->node = result->nodesetval->nodeTab[i];
8316 err = extract_CiRecord(context,
8317 (struct isds_credit_event **) (&item->data),
8318 xpath_ctx);
8319 if (err) goto leave;
8323 leave:
8324 if (!err) {
8325 isds_log(ILF_ISDS, ILL_DEBUG,
8326 _("DataBoxCreditInfo request processed by server successfully.\n"));
8328 if (err) {
8329 isds_list_free(history);
8330 if (NULL != email) zfree(*email)
8333 free(box_id_locale);
8334 xmlXPathFreeObject(result);
8335 xmlXPathFreeContext(xpath_ctx);
8336 xmlFreeDoc(response);
8338 #else /* not HAVE_LIBCURL */
8339 err = IE_NOTSUP;
8340 #endif
8342 return err;
8346 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8347 * code, destroy response and log success.
8348 * @context is ISDS session context.
8349 * @service_name is name of SERVICE_DB_MANIPULATION service
8350 * @box_id is UTF-8 encoded box identifier as zero terminated string
8351 * @approval is optional external approval of box manipulation
8352 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8353 * NULL, if you don't care. */
8354 static isds_error build_send_manipulationdbid_request_check_drop_response(
8355 struct isds_ctx *context, const xmlChar *service_name,
8356 const xmlChar *box_id, const struct isds_approval *approval,
8357 xmlChar **refnumber) {
8358 isds_error err = IE_SUCCESS;
8359 #if HAVE_LIBCURL
8360 xmlDocPtr response = NULL;
8361 #endif
8363 if (!context) return IE_INVALID_CONTEXT;
8364 zfree(context->long_message);
8365 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8367 #if HAVE_LIBCURL
8368 /* Check if connection is established */
8369 if (!context->curl) return IE_CONNECTION_CLOSED;
8371 /* Do request and check for success */
8372 err = build_send_dbid_request_check_response(context,
8373 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8374 &response, refnumber);
8375 xmlFreeDoc(response);
8377 if (!err) {
8378 char *service_name_locale = _isds_utf82locale((char *) service_name);
8379 isds_log(ILF_ISDS, ILL_DEBUG,
8380 _("%s request processed by server successfully.\n"),
8381 service_name_locale);
8382 free(service_name_locale);
8384 #else /* not HAVE_LIBCURL */
8385 err = IE_NOTSUP;
8386 #endif
8388 return err;
8392 /* Switch box into state where box can receive commercial messages (off by
8393 * default)
8394 * @context is ISDS session context.
8395 * @box_id is UTF-8 encoded box identifier as zero terminated string
8396 * @allow is true for enable, false for disable commercial messages income
8397 * @approval is optional external approval of box manipulation
8398 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8399 * NULL, if you don't care. */
8400 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8401 const char *box_id, const _Bool allow,
8402 const struct isds_approval *approval, char **refnumber) {
8403 return build_send_manipulationdbid_request_check_drop_response(context,
8404 (allow) ? BAD_CAST "SetOpenAddressing" :
8405 BAD_CAST "ClearOpenAddressing",
8406 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8410 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8411 * message acceptance). This is just a box permission. Sender must apply
8412 * such role by sending each message.
8413 * @context is ISDS session context.
8414 * @box_id is UTF-8 encoded box identifier as zero terminated string
8415 * @allow is true for enable, false for disable OVM role permission
8416 * @approval is optional external approval of box manipulation
8417 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8418 * NULL, if you don't care. */
8419 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
8420 const char *box_id, const _Bool allow,
8421 const struct isds_approval *approval, char **refnumber) {
8422 return build_send_manipulationdbid_request_check_drop_response(context,
8423 (allow) ? BAD_CAST "SetEffectiveOVM" :
8424 BAD_CAST "ClearEffectiveOVM",
8425 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8429 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8430 * code, destroy response and log success.
8431 * @context is ISDS session context.
8432 * @service_name is name of SERVICE_DB_MANIPULATION service
8433 * @owner is structure describing box. aifoIsds, address->adCode,
8434 * address->adDistrict members are ignored.
8435 * @approval is optional external approval of box manipulation
8436 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8437 * NULL, if you don't care. */
8438 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8439 struct isds_ctx *context, const xmlChar *service_name,
8440 const struct isds_DbOwnerInfo *owner,
8441 const struct isds_approval *approval, xmlChar **refnumber) {
8442 isds_error err = IE_SUCCESS;
8443 #if HAVE_LIBCURL
8444 char *service_name_locale = NULL;
8445 xmlNodePtr request = NULL, db_owner_info;
8446 xmlNsPtr isds_ns = NULL;
8447 #endif
8450 if (!context) return IE_INVALID_CONTEXT;
8451 zfree(context->long_message);
8452 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8454 #if HAVE_LIBCURL
8455 service_name_locale = _isds_utf82locale((char*)service_name);
8456 if (!service_name_locale) {
8457 err = IE_NOMEM;
8458 goto leave;
8461 /* Build request */
8462 request = xmlNewNode(NULL, service_name);
8463 if (!request) {
8464 isds_printf_message(context,
8465 _("Could not build %s request"), service_name_locale);
8466 err = IE_ERROR;
8467 goto leave;
8469 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8470 if(!isds_ns) {
8471 isds_log_message(context, _("Could not create ISDS name space"));
8472 err = IE_ERROR;
8473 goto leave;
8475 xmlSetNs(request, isds_ns);
8478 /* Add XSD:tOwnerInfoInput child*/
8479 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8480 err = insert_DbOwnerInfo(context, owner, db_owner_info);
8481 if (err) goto leave;
8483 /* Add XSD:gExtApproval*/
8484 err = insert_GExtApproval(context, approval, request);
8485 if (err) goto leave;
8487 /* Send it to server and process response */
8488 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8489 service_name, &request, refnumber);
8491 leave:
8492 xmlFreeNode(request);
8493 free(service_name_locale);
8494 #else /* not HAVE_LIBCURL */
8495 err = IE_NOTSUP;
8496 #endif
8498 return err;
8502 /* Switch box accessibility state on request of box owner.
8503 * Despite the name, owner must do the request off-line. This function is
8504 * designed for such off-line meeting points (e.g. Czech POINT).
8505 * @context is ISDS session context.
8506 * @box identifies box to switch accessibility state. aifoIsds,
8507 * address->adCode, address->adDistrict members are ignored.
8508 * @allow is true for making accessible, false to disallow access.
8509 * @approval is optional external approval of box manipulation
8510 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8511 * NULL, if you don't care. */
8512 isds_error isds_switch_box_accessibility_on_owner_request(
8513 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8514 const _Bool allow, const struct isds_approval *approval,
8515 char **refnumber) {
8516 return build_send_manipulationdbowner_request_check_drop_response(context,
8517 (allow) ? BAD_CAST "EnableOwnDataBox" :
8518 BAD_CAST "DisableOwnDataBox",
8519 box, approval, (xmlChar **) refnumber);
8523 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8524 * date.
8525 * @context is ISDS session context.
8526 * @box identifies box to switch accessibility state. aifoIsds,
8527 * address->adCode, address->adDistrict members are ignored.
8528 * @since is date since accessibility has been denied. This can be past too.
8529 * Only tm_year, tm_mon and tm_mday carry sane value.
8530 * @approval is optional external approval of box manipulation
8531 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8532 * NULL, if you don't care. */
8533 isds_error isds_disable_box_accessibility_externaly(
8534 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8535 const struct tm *since, const struct isds_approval *approval,
8536 char **refnumber) {
8537 isds_error err = IE_SUCCESS;
8538 #if HAVE_LIBCURL
8539 char *service_name_locale = NULL;
8540 xmlNodePtr request = NULL, node;
8541 xmlNsPtr isds_ns = NULL;
8542 xmlChar *string = NULL;
8543 #endif
8546 if (!context) return IE_INVALID_CONTEXT;
8547 zfree(context->long_message);
8548 if (!box || !since) return IE_INVAL;
8550 #if HAVE_LIBCURL
8551 /* Build request */
8552 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
8553 if (!request) {
8554 isds_printf_message(context,
8555 _("Could not build %s request"), "DisableDataBoxExternally");
8556 err = IE_ERROR;
8557 goto leave;
8559 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8560 if(!isds_ns) {
8561 isds_log_message(context, _("Could not create ISDS name space"));
8562 err = IE_ERROR;
8563 goto leave;
8565 xmlSetNs(request, isds_ns);
8568 /* Add @box identification */
8569 INSERT_ELEMENT(node, request, "dbOwnerInfo");
8570 err = insert_DbOwnerInfo(context, box, node);
8571 if (err) goto leave;
8573 /* Add @since date */
8574 err = tm2datestring(since, &string);
8575 if(err) {
8576 isds_log_message(context,
8577 _("Could not convert `since' argument to ISO date string"));
8578 goto leave;
8580 INSERT_STRING(request, "dbOwnerDisableDate", string);
8581 zfree(string);
8583 /* Add @approval */
8584 err = insert_GExtApproval(context, approval, request);
8585 if (err) goto leave;
8587 /* Send it to server and process response */
8588 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8589 BAD_CAST "DisableDataBoxExternally", &request,
8590 (xmlChar **) refnumber);
8592 leave:
8593 free(string);
8594 xmlFreeNode(request);
8595 free(service_name_locale);
8596 #else /* not HAVE_LIBCURL */
8597 err = IE_NOTSUP;
8598 #endif
8600 return err;
8604 #if HAVE_LIBCURL
8605 /* Insert struct isds_message data (envelope (recipient data optional) and
8606 * documents into XML tree
8607 * @context is session context
8608 * @outgoing_message is libisds structure with message data
8609 * @create_message is XML CreateMessage or CreateMultipleMessage element
8610 * @process_recipient true for recipient data serialization, false for no
8611 * serialization */
8612 static isds_error insert_envelope_files(struct isds_ctx *context,
8613 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8614 const _Bool process_recipient) {
8616 isds_error err = IE_SUCCESS;
8617 xmlNodePtr envelope, dm_files, node;
8618 xmlChar *string = NULL;
8620 if (!context) return IE_INVALID_CONTEXT;
8621 if (!outgoing_message || !create_message) return IE_INVAL;
8624 /* Build envelope */
8625 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8626 if (!envelope) {
8627 isds_printf_message(context, _("Could not add dmEnvelope child to "
8628 "%s element"), create_message->name);
8629 return IE_ERROR;
8632 if (!outgoing_message->envelope) {
8633 isds_log_message(context, _("Outgoing message is missing envelope"));
8634 err = IE_INVAL;
8635 goto leave;
8638 /* Insert optional message type */
8639 err = insert_message_type(context, outgoing_message->envelope->dmType,
8640 envelope);
8641 if (err) goto leave;
8643 INSERT_STRING(envelope, "dmSenderOrgUnit",
8644 outgoing_message->envelope->dmSenderOrgUnit);
8645 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8646 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8648 if (process_recipient) {
8649 if (!outgoing_message->envelope->dbIDRecipient) {
8650 isds_log_message(context,
8651 _("Outgoing message is missing recipient box identifier"));
8652 err = IE_INVAL;
8653 goto leave;
8655 INSERT_STRING(envelope, "dbIDRecipient",
8656 outgoing_message->envelope->dbIDRecipient);
8658 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8659 outgoing_message->envelope->dmRecipientOrgUnit);
8660 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8661 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8662 INSERT_STRING(envelope, "dmToHands",
8663 outgoing_message->envelope->dmToHands);
8666 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8667 "dmAnnotation");
8668 INSERT_STRING(envelope, "dmAnnotation",
8669 outgoing_message->envelope->dmAnnotation);
8671 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8672 0, 50, "dmRecipientRefNumber");
8673 INSERT_STRING(envelope, "dmRecipientRefNumber",
8674 outgoing_message->envelope->dmRecipientRefNumber);
8676 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8677 0, 50, "dmSenderRefNumber");
8678 INSERT_STRING(envelope, "dmSenderRefNumber",
8679 outgoing_message->envelope->dmSenderRefNumber);
8681 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8682 0, 50, "dmRecipientIdent");
8683 INSERT_STRING(envelope, "dmRecipientIdent",
8684 outgoing_message->envelope->dmRecipientIdent);
8686 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8687 0, 50, "dmSenderIdent");
8688 INSERT_STRING(envelope, "dmSenderIdent",
8689 outgoing_message->envelope->dmSenderIdent);
8691 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8692 outgoing_message->envelope->dmLegalTitleLaw, string);
8693 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8694 outgoing_message->envelope->dmLegalTitleYear, string);
8695 INSERT_STRING(envelope, "dmLegalTitleSect",
8696 outgoing_message->envelope->dmLegalTitleSect);
8697 INSERT_STRING(envelope, "dmLegalTitlePar",
8698 outgoing_message->envelope->dmLegalTitlePar);
8699 INSERT_STRING(envelope, "dmLegalTitlePoint",
8700 outgoing_message->envelope->dmLegalTitlePoint);
8702 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8703 outgoing_message->envelope->dmPersonalDelivery);
8704 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8705 outgoing_message->envelope->dmAllowSubstDelivery);
8707 /* ???: Should we require value for dbEffectiveOVM sender?
8708 * ISDS has default as true */
8709 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8710 INSERT_BOOLEAN(envelope, "dmOVM",
8711 outgoing_message->envelope->dmPublishOwnID);
8714 /* Append dmFiles */
8715 if (!outgoing_message->documents) {
8716 isds_log_message(context,
8717 _("Outgoing message is missing list of documents"));
8718 err = IE_INVAL;
8719 goto leave;
8721 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8722 if (!dm_files) {
8723 isds_printf_message(context, _("Could not add dmFiles child to "
8724 "%s element"), create_message->name);
8725 err = IE_ERROR;
8726 goto leave;
8729 /* Check for document hierarchy */
8730 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8731 if (err) goto leave;
8733 /* Process each document */
8734 for (struct isds_list *item =
8735 (struct isds_list *) outgoing_message->documents;
8736 item; item = item->next) {
8737 if (!item->data) {
8738 isds_log_message(context,
8739 _("List of documents contains empty item"));
8740 err = IE_INVAL;
8741 goto leave;
8743 /* FIXME: Check for dmFileMetaType and for document references.
8744 * Only first document can be of MAIN type */
8745 err = insert_document(context, (struct isds_document*) item->data,
8746 dm_files);
8748 if (err) goto leave;
8751 leave:
8752 free(string);
8753 return err;
8755 #endif /* HAVE_LIBCURL */
8758 /* Send a message via ISDS to a recipient
8759 * @context is session context
8760 * @outgoing_message is message to send; Some members are mandatory (like
8761 * dbIDRecipient), some are optional and some are irrelevant (especially data
8762 * about sender). Included pointer to isds_list documents must contain at
8763 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8764 * members will be filled with valid data from ISDS. Exact list of write
8765 * members is subject to change. Currently dmID is changed.
8766 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8767 isds_error isds_send_message(struct isds_ctx *context,
8768 struct isds_message *outgoing_message) {
8770 isds_error err = IE_SUCCESS;
8771 #if HAVE_LIBCURL
8772 xmlNsPtr isds_ns = NULL;
8773 xmlNodePtr request = NULL;
8774 xmlDocPtr response = NULL;
8775 xmlChar *code = NULL, *message = NULL;
8776 xmlXPathContextPtr xpath_ctx = NULL;
8777 xmlXPathObjectPtr result = NULL;
8778 /*_Bool message_is_complete = 0;*/
8779 #endif
8781 if (!context) return IE_INVALID_CONTEXT;
8782 zfree(context->long_message);
8783 if (!outgoing_message) return IE_INVAL;
8785 #if HAVE_LIBCURL
8786 /* Check if connection is established
8787 * TODO: This check should be done downstairs. */
8788 if (!context->curl) return IE_CONNECTION_CLOSED;
8791 /* Build CreateMessage request */
8792 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8793 if (!request) {
8794 isds_log_message(context,
8795 _("Could not build CreateMessage request"));
8796 return IE_ERROR;
8798 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8799 if(!isds_ns) {
8800 isds_log_message(context, _("Could not create ISDS name space"));
8801 xmlFreeNode(request);
8802 return IE_ERROR;
8804 xmlSetNs(request, isds_ns);
8806 /* Append envelope and files */
8807 err = insert_envelope_files(context, outgoing_message, request, 1);
8808 if (err) goto leave;
8811 /* Signal we can serialize message since now */
8812 /*message_is_complete = 1;*/
8815 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8817 /* Sent request */
8818 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8820 /* Don't' destroy request, we want to provide it to application later */
8822 if (err) {
8823 isds_log(ILF_ISDS, ILL_DEBUG,
8824 _("Processing ISDS response on CreateMessage "
8825 "request failed\n"));
8826 goto leave;
8829 /* Check for response status */
8830 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8831 &code, &message, NULL);
8832 if (err) {
8833 isds_log(ILF_ISDS, ILL_DEBUG,
8834 _("ISDS response on CreateMessage request "
8835 "is missing status\n"));
8836 goto leave;
8839 /* Request processed, but refused by server or server failed */
8840 if (xmlStrcmp(code, BAD_CAST "0000")) {
8841 char *box_id_locale =
8842 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8843 char *code_locale = _isds_utf82locale((char*)code);
8844 char *message_locale = _isds_utf82locale((char*)message);
8845 isds_log(ILF_ISDS, ILL_DEBUG,
8846 _("Server did not accept message for %s on CreateMessage "
8847 "request (code=%s, message=%s)\n"),
8848 box_id_locale, code_locale, message_locale);
8849 isds_log_message(context, message_locale);
8850 free(box_id_locale);
8851 free(code_locale);
8852 free(message_locale);
8853 err = IE_ISDS;
8854 goto leave;
8858 /* Extract data */
8859 xpath_ctx = xmlXPathNewContext(response);
8860 if (!xpath_ctx) {
8861 err = IE_ERROR;
8862 goto leave;
8864 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8865 err = IE_ERROR;
8866 goto leave;
8868 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8869 xpath_ctx);
8870 if (!result) {
8871 err = IE_ERROR;
8872 goto leave;
8874 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8875 isds_log_message(context, _("Missing CreateMessageResponse element"));
8876 err = IE_ISDS;
8877 goto leave;
8879 if (result->nodesetval->nodeNr > 1) {
8880 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8881 err = IE_ISDS;
8882 goto leave;
8884 xpath_ctx->node = result->nodesetval->nodeTab[0];
8885 xmlXPathFreeObject(result); result = NULL;
8887 if (outgoing_message->envelope->dmID) {
8888 free(outgoing_message->envelope->dmID);
8889 outgoing_message->envelope->dmID = NULL;
8891 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8892 if (!outgoing_message->envelope->dmID) {
8893 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8894 "but did not return assigned message ID\n"));
8897 leave:
8898 /* TODO: Serialize message into structure member raw */
8899 /* XXX: Each web service transport message in different format.
8900 * Therefore it's not possible to save them directly.
8901 * To save them, one must figure out common format.
8902 * We can leave it on application, or we can implement the ESS format. */
8903 /*if (message_is_complete) {
8904 if (outgoing_message->envelope->dmID) {
8906 /* Add assigned message ID as first child*/
8907 /*xmlNodePtr dmid_text = xmlNewText(
8908 (xmlChar *) outgoing_message->envelope->dmID);
8909 if (!dmid_text) goto serialization_failed;
8911 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8912 BAD_CAST "dmID");
8913 if (!dmid_element) {
8914 xmlFreeNode(dmid_text);
8915 goto serialization_failed;
8918 xmlNodePtr dmid_element_with_text =
8919 xmlAddChild(dmid_element, dmid_text);
8920 if (!dmid_element_with_text) {
8921 xmlFreeNode(dmid_element);
8922 xmlFreeNode(dmid_text);
8923 goto serialization_failed;
8926 node = xmlAddPrevSibling(envelope->childern,
8927 dmid_element_with_text);
8928 if (!node) {
8929 xmlFreeNodeList(dmid_element_with_text);
8930 goto serialization_failed;
8934 /* Serialize message with ID into raw */
8935 /*buffer = serialize_element(envelope)*/
8936 /* }
8938 serialization_failed:
8942 /* Clean up */
8943 xmlXPathFreeObject(result);
8944 xmlXPathFreeContext(xpath_ctx);
8946 free(code);
8947 free(message);
8948 xmlFreeDoc(response);
8949 xmlFreeNode(request);
8951 if (!err)
8952 isds_log(ILF_ISDS, ILL_DEBUG,
8953 _("CreateMessage request processed by server "
8954 "successfully.\n"));
8955 #else /* not HAVE_LIBCURL */
8956 err = IE_NOTSUP;
8957 #endif
8959 return err;
8963 /* Send a message via ISDS to a multiple recipients
8964 * @context is session context
8965 * @outgoing_message is message to send; Some members are mandatory,
8966 * some are optional and some are irrelevant (especially data
8967 * about sender). Data about recipient will be substituted by ISDS from
8968 * @copies. Included pointer to isds_list documents must
8969 * contain at least one document of FILEMETATYPE_MAIN.
8970 * @copies is list of isds_message_copy structures addressing all desired
8971 * recipients. This is read-write structure, some members will be filled with
8972 * valid data from ISDS (message IDs, error codes, error descriptions).
8973 * @return
8974 * ISDS_SUCCESS if all messages have been sent
8975 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8976 * succeeded messages can be identified by copies->data->error),
8977 * or other error code if something other goes wrong. */
8978 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8979 const struct isds_message *outgoing_message,
8980 struct isds_list *copies) {
8982 isds_error err = IE_SUCCESS;
8983 #if HAVE_LIBCURL
8984 isds_error append_err;
8985 xmlNsPtr isds_ns = NULL;
8986 xmlNodePtr request = NULL, recipients, recipient, node;
8987 struct isds_list *item;
8988 struct isds_message_copy *copy;
8989 xmlDocPtr response = NULL;
8990 xmlChar *code = NULL, *message = NULL;
8991 xmlXPathContextPtr xpath_ctx = NULL;
8992 xmlXPathObjectPtr result = NULL;
8993 xmlChar *string = NULL;
8994 int i;
8995 #endif
8997 if (!context) return IE_INVALID_CONTEXT;
8998 zfree(context->long_message);
8999 if (!outgoing_message || !copies) return IE_INVAL;
9001 #if HAVE_LIBCURL
9002 /* Check if connection is established
9003 * TODO: This check should be done downstairs. */
9004 if (!context->curl) return IE_CONNECTION_CLOSED;
9007 /* Build CreateMultipleMessage request */
9008 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
9009 if (!request) {
9010 isds_log_message(context,
9011 _("Could not build CreateMultipleMessage request"));
9012 return IE_ERROR;
9014 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9015 if(!isds_ns) {
9016 isds_log_message(context, _("Could not create ISDS name space"));
9017 xmlFreeNode(request);
9018 return IE_ERROR;
9020 xmlSetNs(request, isds_ns);
9023 /* Build recipients */
9024 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
9025 if (!recipients) {
9026 isds_log_message(context, _("Could not add dmRecipients child to "
9027 "CreateMultipleMessage element"));
9028 xmlFreeNode(request);
9029 return IE_ERROR;
9032 /* Insert each recipient */
9033 for (item = copies; item; item = item->next) {
9034 copy = (struct isds_message_copy *) item->data;
9035 if (!copy) {
9036 isds_log_message(context,
9037 _("`copies' list item contains empty data"));
9038 err = IE_INVAL;
9039 goto leave;
9042 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
9043 if (!recipient) {
9044 isds_log_message(context, _("Could not add dmRecipient child to "
9045 "dmRecipients element"));
9046 err = IE_ERROR;
9047 goto leave;
9050 if (!copy->dbIDRecipient) {
9051 isds_log_message(context,
9052 _("Message copy is missing recipient box identifier"));
9053 err = IE_INVAL;
9054 goto leave;
9056 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
9057 INSERT_STRING(recipient, "dmRecipientOrgUnit",
9058 copy->dmRecipientOrgUnit);
9059 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
9060 copy->dmRecipientOrgUnitNum, string);
9061 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
9064 /* Append envelope and files */
9065 err = insert_envelope_files(context, outgoing_message, request, 0);
9066 if (err) goto leave;
9069 isds_log(ILF_ISDS, ILL_DEBUG,
9070 _("Sending CreateMultipleMessage request to ISDS\n"));
9072 /* Sent request */
9073 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9074 if (err) {
9075 isds_log(ILF_ISDS, ILL_DEBUG,
9076 _("Processing ISDS response on CreateMultipleMessage "
9077 "request failed\n"));
9078 goto leave;
9081 /* Check for response status */
9082 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9083 &code, &message, NULL);
9084 if (err) {
9085 isds_log(ILF_ISDS, ILL_DEBUG,
9086 _("ISDS response on CreateMultipleMessage request "
9087 "is missing status\n"));
9088 goto leave;
9091 /* Request processed, but some copies failed */
9092 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9093 char *box_id_locale =
9094 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9095 char *code_locale = _isds_utf82locale((char*)code);
9096 char *message_locale = _isds_utf82locale((char*)message);
9097 isds_log(ILF_ISDS, ILL_DEBUG,
9098 _("Server did accept message for multiple recipients "
9099 "on CreateMultipleMessage request but delivery to "
9100 "some of them failed (code=%s, message=%s)\n"),
9101 box_id_locale, code_locale, message_locale);
9102 isds_log_message(context, message_locale);
9103 free(box_id_locale);
9104 free(code_locale);
9105 free(message_locale);
9106 err = IE_PARTIAL_SUCCESS;
9109 /* Request refused by server as whole */
9110 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9111 char *box_id_locale =
9112 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9113 char *code_locale = _isds_utf82locale((char*)code);
9114 char *message_locale = _isds_utf82locale((char*)message);
9115 isds_log(ILF_ISDS, ILL_DEBUG,
9116 _("Server did not accept message for multiple recipients "
9117 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9118 box_id_locale, code_locale, message_locale);
9119 isds_log_message(context, message_locale);
9120 free(box_id_locale);
9121 free(code_locale);
9122 free(message_locale);
9123 err = IE_ISDS;
9124 goto leave;
9128 /* Extract data */
9129 xpath_ctx = xmlXPathNewContext(response);
9130 if (!xpath_ctx) {
9131 err = IE_ERROR;
9132 goto leave;
9134 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9135 err = IE_ERROR;
9136 goto leave;
9138 result = xmlXPathEvalExpression(
9139 BAD_CAST "/isds:CreateMultipleMessageResponse"
9140 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9141 xpath_ctx);
9142 if (!result) {
9143 err = IE_ERROR;
9144 goto leave;
9146 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9147 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9148 err = IE_ISDS;
9149 goto leave;
9152 /* Extract message ID and delivery status for each copy */
9153 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9154 item = item->next, i++) {
9155 copy = (struct isds_message_copy *) item->data;
9156 xpath_ctx->node = result->nodesetval->nodeTab[i];
9158 append_err = append_TMStatus(context, copy, xpath_ctx);
9159 if (append_err) {
9160 err = append_err;
9161 goto leave;
9164 if (item || i < result->nodesetval->nodeNr) {
9165 isds_printf_message(context, _("ISDS returned unexpected number of "
9166 "message copy delivery states: %d"),
9167 result->nodesetval->nodeNr);
9168 err = IE_ISDS;
9169 goto leave;
9173 leave:
9174 /* Clean up */
9175 free(string);
9176 xmlXPathFreeObject(result);
9177 xmlXPathFreeContext(xpath_ctx);
9179 free(code);
9180 free(message);
9181 xmlFreeDoc(response);
9182 xmlFreeNode(request);
9184 if (!err)
9185 isds_log(ILF_ISDS, ILL_DEBUG,
9186 _("CreateMultipleMessageResponse request processed by server "
9187 "successfully.\n"));
9188 #else /* not HAVE_LIBCURL */
9189 err = IE_NOTSUP;
9190 #endif
9192 return err;
9196 /* Get list of messages. This is common core for getting sent or received
9197 * messages.
9198 * Any criterion argument can be NULL, if you don't care about it.
9199 * @context is session context. Must not be NULL.
9200 * @outgoing_direction is true if you want list of outgoing messages,
9201 * it's false if you want incoming messages.
9202 * @from_time is minimal time and date of message sending inclusive.
9203 * @to_time is maximal time and date of message sending inclusive
9204 * @organization_unit_number is number of sender/recipient respectively.
9205 * @status_filter is bit field of isds_message_status values. Use special
9206 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9207 * all values, you can use bit-wise arithmetic if you want.)
9208 * @offset is index of first message we are interested in. First message is 1.
9209 * Set to 0 (or 1) if you don't care.
9210 * @number is maximal length of list you want to get as input value, outputs
9211 * number of messages matching these criteria. Can be NULL if you don't care
9212 * (applies to output value either).
9213 * @messages is automatically reallocated list of isds_message's. Be ware that
9214 * it returns only brief overview (envelope and some other fields) about each
9215 * message, not the complete message. FIXME: Specify exact fields.
9216 * The list is sorted by delivery time in ascending order.
9217 * Use NULL if you don't care about don't need the data (useful if you want to
9218 * know only the @number). If you provide &NULL, list will be allocated on
9219 * heap, if you provide pointer to non-NULL, list will be freed automatically
9220 * at first. Also in case of error the list will be NULLed.
9221 * @return IE_SUCCESS or appropriate error code. */
9222 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9223 _Bool outgoing_direction,
9224 const struct timeval *from_time, const struct timeval *to_time,
9225 const long int *organization_unit_number,
9226 const unsigned int status_filter,
9227 const unsigned long int offset, unsigned long int *number,
9228 struct isds_list **messages) {
9230 isds_error err = IE_SUCCESS;
9231 #if HAVE_LIBCURL
9232 xmlNsPtr isds_ns = NULL;
9233 xmlNodePtr request = NULL, node;
9234 xmlDocPtr response = NULL;
9235 xmlChar *code = NULL, *message = NULL;
9236 xmlXPathContextPtr xpath_ctx = NULL;
9237 xmlXPathObjectPtr result = NULL;
9238 xmlChar *string = NULL;
9239 int count = 0;
9240 #endif
9242 if (!context) return IE_INVALID_CONTEXT;
9243 zfree(context->long_message);
9245 /* Free former message list if any */
9246 if (messages) isds_list_free(messages);
9248 #if HAVE_LIBCURL
9249 /* Check if connection is established
9250 * TODO: This check should be done downstairs. */
9251 if (!context->curl) return IE_CONNECTION_CLOSED;
9253 /* Build GetListOf*Messages request */
9254 request = xmlNewNode(NULL,
9255 (outgoing_direction) ?
9256 BAD_CAST "GetListOfSentMessages" :
9257 BAD_CAST "GetListOfReceivedMessages"
9259 if (!request) {
9260 isds_log_message(context,
9261 (outgoing_direction) ?
9262 _("Could not build GetListOfSentMessages request") :
9263 _("Could not build GetListOfReceivedMessages request")
9265 return IE_ERROR;
9267 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9268 if(!isds_ns) {
9269 isds_log_message(context, _("Could not create ISDS name space"));
9270 xmlFreeNode(request);
9271 return IE_ERROR;
9273 xmlSetNs(request, isds_ns);
9276 if (from_time) {
9277 err = timeval2timestring(from_time, &string);
9278 if (err) goto leave;
9280 INSERT_STRING(request, "dmFromTime", string);
9281 free(string); string = NULL;
9283 if (to_time) {
9284 err = timeval2timestring(to_time, &string);
9285 if (err) goto leave;
9287 INSERT_STRING(request, "dmToTime", string);
9288 free(string); string = NULL;
9290 if (outgoing_direction) {
9291 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9292 organization_unit_number, string);
9293 } else {
9294 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9295 organization_unit_number, string);
9298 if (status_filter > MESSAGESTATE_ANY) {
9299 isds_printf_message(context,
9300 _("Invalid message state filter value: %ld"), status_filter);
9301 err = IE_INVAL;
9302 goto leave;
9304 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9306 if (offset > 0 ) {
9307 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9308 } else {
9309 INSERT_STRING(request, "dmOffset", "1");
9312 /* number 0 means no limit */
9313 if (number && *number == 0) {
9314 INSERT_STRING(request, "dmLimit", NULL);
9315 } else {
9316 INSERT_ULONGINT(request, "dmLimit", number, string);
9320 isds_log(ILF_ISDS, ILL_DEBUG,
9321 (outgoing_direction) ?
9322 _("Sending GetListOfSentMessages request to ISDS\n") :
9323 _("Sending GetListOfReceivedMessages request to ISDS\n")
9326 /* Sent request */
9327 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9328 xmlFreeNode(request); request = NULL;
9330 if (err) {
9331 isds_log(ILF_ISDS, ILL_DEBUG,
9332 (outgoing_direction) ?
9333 _("Processing ISDS response on GetListOfSentMessages "
9334 "request failed\n") :
9335 _("Processing ISDS response on GetListOfReceivedMessages "
9336 "request failed\n")
9338 goto leave;
9341 /* Check for response status */
9342 err = isds_response_status(context, SERVICE_DM_INFO, response,
9343 &code, &message, NULL);
9344 if (err) {
9345 isds_log(ILF_ISDS, ILL_DEBUG,
9346 (outgoing_direction) ?
9347 _("ISDS response on GetListOfSentMessages request "
9348 "is missing status\n") :
9349 _("ISDS response on GetListOfReceivedMessages request "
9350 "is missing status\n")
9352 goto leave;
9355 /* Request processed, but nothing found */
9356 if (xmlStrcmp(code, BAD_CAST "0000")) {
9357 char *code_locale = _isds_utf82locale((char*)code);
9358 char *message_locale = _isds_utf82locale((char*)message);
9359 isds_log(ILF_ISDS, ILL_DEBUG,
9360 (outgoing_direction) ?
9361 _("Server refused GetListOfSentMessages request "
9362 "(code=%s, message=%s)\n") :
9363 _("Server refused GetListOfReceivedMessages request "
9364 "(code=%s, message=%s)\n"),
9365 code_locale, message_locale);
9366 isds_log_message(context, message_locale);
9367 free(code_locale);
9368 free(message_locale);
9369 err = IE_ISDS;
9370 goto leave;
9374 /* Extract data */
9375 xpath_ctx = xmlXPathNewContext(response);
9376 if (!xpath_ctx) {
9377 err = IE_ERROR;
9378 goto leave;
9380 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9381 err = IE_ERROR;
9382 goto leave;
9384 result = xmlXPathEvalExpression(
9385 (outgoing_direction) ?
9386 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9387 "isds:dmRecords/isds:dmRecord" :
9388 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9389 "isds:dmRecords/isds:dmRecord",
9390 xpath_ctx);
9391 if (!result) {
9392 err = IE_ERROR;
9393 goto leave;
9396 /* Fill output arguments in */
9397 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9398 struct isds_envelope *envelope;
9399 struct isds_list *item = NULL, *last_item = NULL;
9401 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9402 /* Create new message */
9403 item = calloc(1, sizeof(*item));
9404 if (!item) {
9405 err = IE_NOMEM;
9406 goto leave;
9408 item->destructor = (void(*)(void**)) &isds_message_free;
9409 item->data = calloc(1, sizeof(struct isds_message));
9410 if (!item->data) {
9411 isds_list_free(&item);
9412 err = IE_NOMEM;
9413 goto leave;
9416 /* Extract envelope data */
9417 xpath_ctx->node = result->nodesetval->nodeTab[count];
9418 envelope = NULL;
9419 err = extract_DmRecord(context, &envelope, xpath_ctx);
9420 if (err) {
9421 isds_list_free(&item);
9422 goto leave;
9425 /* Attach extracted envelope */
9426 ((struct isds_message *) item->data)->envelope = envelope;
9428 /* Append new message into the list */
9429 if (!*messages) {
9430 *messages = last_item = item;
9431 } else {
9432 last_item->next = item;
9433 last_item = item;
9437 if (number) *number = count;
9439 leave:
9440 if (err) {
9441 isds_list_free(messages);
9444 free(string);
9445 xmlXPathFreeObject(result);
9446 xmlXPathFreeContext(xpath_ctx);
9448 free(code);
9449 free(message);
9450 xmlFreeDoc(response);
9451 xmlFreeNode(request);
9453 if (!err)
9454 isds_log(ILF_ISDS, ILL_DEBUG,
9455 (outgoing_direction) ?
9456 _("GetListOfSentMessages request processed by server "
9457 "successfully.\n") :
9458 _("GetListOfReceivedMessages request processed by server "
9459 "successfully.\n")
9461 #else /* not HAVE_LIBCURL */
9462 err = IE_NOTSUP;
9463 #endif
9464 return err;
9468 /* Get list of outgoing (already sent) messages.
9469 * Any criterion argument can be NULL, if you don't care about it.
9470 * @context is session context. Must not be NULL.
9471 * @from_time is minimal time and date of message sending inclusive.
9472 * @to_time is maximal time and date of message sending inclusive
9473 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9474 * @status_filter is bit field of isds_message_status values. Use special
9475 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9476 * all values, you can use bit-wise arithmetic if you want.)
9477 * @offset is index of first message we are interested in. First message is 1.
9478 * Set to 0 (or 1) if you don't care.
9479 * @number is maximal length of list you want to get as input value, outputs
9480 * number of messages matching these criteria. Can be NULL if you don't care
9481 * (applies to output value either).
9482 * @messages is automatically reallocated list of isds_message's. Be ware that
9483 * it returns only brief overview (envelope and some other fields) about each
9484 * message, not the complete message. FIXME: Specify exact fields.
9485 * The list is sorted by delivery time in ascending order.
9486 * Use NULL if you don't care about the meta data (useful if you want to know
9487 * only the @number). If you provide &NULL, list will be allocated on heap,
9488 * if you provide pointer to non-NULL, list will be freed automatically at
9489 * first. Also in case of error the list will be NULLed.
9490 * @return IE_SUCCESS or appropriate error code. */
9491 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9492 const struct timeval *from_time, const struct timeval *to_time,
9493 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9494 const unsigned long int offset, unsigned long int *number,
9495 struct isds_list **messages) {
9497 return isds_get_list_of_messages(
9498 context, 1,
9499 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9500 offset, number,
9501 messages);
9505 /* Get list of incoming (addressed to you) messages.
9506 * Any criterion argument can be NULL, if you don't care about it.
9507 * @context is session context. Must not be NULL.
9508 * @from_time is minimal time and date of message sending inclusive.
9509 * @to_time is maximal time and date of message sending inclusive
9510 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9511 * @status_filter is bit field of isds_message_status values. Use special
9512 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9513 * all values, you can use bit-wise arithmetic if you want.)
9514 * @offset is index of first message we are interested in. First message is 1.
9515 * Set to 0 (or 1) if you don't care.
9516 * @number is maximal length of list you want to get as input value, outputs
9517 * number of messages matching these criteria. Can be NULL if you don't care
9518 * (applies to output value either).
9519 * @messages is automatically reallocated list of isds_message's. Be ware that
9520 * it returns only brief overview (envelope and some other fields) about each
9521 * message, not the complete message. FIXME: Specify exact fields.
9522 * Use NULL if you don't care about the meta data (useful if you want to know
9523 * only the @number). If you provide &NULL, list will be allocated on heap,
9524 * if you provide pointer to non-NULL, list will be freed automatically at
9525 * first. Also in case of error the list will be NULLed.
9526 * @return IE_SUCCESS or appropriate error code. */
9527 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9528 const struct timeval *from_time, const struct timeval *to_time,
9529 const long int *dmRecipientOrgUnitNum,
9530 const unsigned int status_filter,
9531 const unsigned long int offset, unsigned long int *number,
9532 struct isds_list **messages) {
9534 return isds_get_list_of_messages(
9535 context, 0,
9536 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9537 offset, number,
9538 messages);
9542 /* Get list of sent message state changes.
9543 * Any criterion argument can be NULL, if you don't care about it.
9544 * @context is session context. Must not be NULL.
9545 * @from_time is minimal time and date of status changes inclusive
9546 * @to_time is maximal time and date of status changes inclusive
9547 * @changed_states is automatically reallocated list of
9548 * isds_message_status_change's. If you provide &NULL, list will be allocated
9549 * on heap, if you provide pointer to non-NULL, list will be freed
9550 * automatically at first. Also in case of error the list will be NULLed.
9551 * XXX: The list item ordering is not specified.
9552 * XXX: Server provides only `recent' changes.
9553 * @return IE_SUCCESS or appropriate error code. */
9554 isds_error isds_get_list_of_sent_message_state_changes(
9555 struct isds_ctx *context,
9556 const struct timeval *from_time, const struct timeval *to_time,
9557 struct isds_list **changed_states) {
9559 isds_error err = IE_SUCCESS;
9560 #if HAVE_LIBCURL
9561 xmlNsPtr isds_ns = NULL;
9562 xmlNodePtr request = NULL, node;
9563 xmlDocPtr response = NULL;
9564 xmlXPathContextPtr xpath_ctx = NULL;
9565 xmlXPathObjectPtr result = NULL;
9566 xmlChar *string = NULL;
9567 int count = 0;
9568 #endif
9570 if (!context) return IE_INVALID_CONTEXT;
9571 zfree(context->long_message);
9573 /* Free former message list if any */
9574 isds_list_free(changed_states);
9576 #if HAVE_LIBCURL
9577 /* Check if connection is established
9578 * TODO: This check should be done downstairs. */
9579 if (!context->curl) return IE_CONNECTION_CLOSED;
9581 /* Build GetMessageStateChanges request */
9582 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
9583 if (!request) {
9584 isds_log_message(context,
9585 _("Could not build GetMessageStateChanges request"));
9586 return IE_ERROR;
9588 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9589 if(!isds_ns) {
9590 isds_log_message(context, _("Could not create ISDS name space"));
9591 xmlFreeNode(request);
9592 return IE_ERROR;
9594 xmlSetNs(request, isds_ns);
9597 if (from_time) {
9598 err = timeval2timestring(from_time, &string);
9599 if (err) goto leave;
9601 INSERT_STRING(request, "dmFromTime", string);
9602 zfree(string);
9604 if (to_time) {
9605 err = timeval2timestring(to_time, &string);
9606 if (err) goto leave;
9608 INSERT_STRING(request, "dmToTime", string);
9609 zfree(string);
9612 /* Sent request */
9613 err = send_destroy_request_check_response(context,
9614 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9615 &response, NULL, NULL);
9616 if (err) goto leave;
9619 /* Extract data */
9620 xpath_ctx = xmlXPathNewContext(response);
9621 if (!xpath_ctx) {
9622 err = IE_ERROR;
9623 goto leave;
9625 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9626 err = IE_ERROR;
9627 goto leave;
9629 result = xmlXPathEvalExpression(
9630 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9631 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9632 if (!result) {
9633 err = IE_ERROR;
9634 goto leave;
9637 /* Fill output arguments in */
9638 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9639 struct isds_list *item = NULL, *last_item = NULL;
9641 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9642 /* Create new status change */
9643 item = calloc(1, sizeof(*item));
9644 if (!item) {
9645 err = IE_NOMEM;
9646 goto leave;
9648 item->destructor =
9649 (void(*)(void**)) &isds_message_status_change_free;
9651 /* Extract message status change */
9652 xpath_ctx->node = result->nodesetval->nodeTab[count];
9653 err = extract_StateChangesRecord(context,
9654 (struct isds_message_status_change **) &item->data,
9655 xpath_ctx);
9656 if (err) {
9657 isds_list_free(&item);
9658 goto leave;
9661 /* Append new message status change into the list */
9662 if (!*changed_states) {
9663 *changed_states = last_item = item;
9664 } else {
9665 last_item->next = item;
9666 last_item = item;
9671 leave:
9672 if (err) {
9673 isds_list_free(changed_states);
9676 free(string);
9677 xmlXPathFreeObject(result);
9678 xmlXPathFreeContext(xpath_ctx);
9679 xmlFreeDoc(response);
9680 xmlFreeNode(request);
9682 if (!err)
9683 isds_log(ILF_ISDS, ILL_DEBUG,
9684 _("GetMessageStateChanges request processed by server "
9685 "successfully.\n"));
9686 #else /* not HAVE_LIBCURL */
9687 err = IE_NOTSUP;
9688 #endif
9689 return err;
9693 #if HAVE_LIBCURL
9694 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9695 * code
9696 * @context is session context
9697 * @service is ISDS WS service handler
9698 * @service_name is name of SERVICE_DM_OPERATIONS
9699 * @message_id is message ID to send as service argument to ISDS
9700 * @response is reallocated server SOAP body response as XML document
9701 * @raw_response is reallocated bit stream with response body. Use
9702 * NULL if you don't care
9703 * @raw_response_length is size of @raw_response in bytes
9704 * @code is reallocated ISDS status code
9705 * @status_message is reallocated ISDS status message
9706 * @return error coded from lower layer, context message will be set up
9707 * appropriately. */
9708 static isds_error build_send_check_message_request(struct isds_ctx *context,
9709 const isds_service service, const xmlChar *service_name,
9710 const char *message_id,
9711 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9712 xmlChar **code, xmlChar **status_message) {
9714 isds_error err = IE_SUCCESS;
9715 char *service_name_locale = NULL, *message_id_locale = NULL;
9716 xmlNodePtr request = NULL, node;
9717 xmlNsPtr isds_ns = NULL;
9719 if (!context) return IE_INVALID_CONTEXT;
9720 if (!service_name || !message_id) return IE_INVAL;
9721 if (!response || !code || !status_message) return IE_INVAL;
9722 if (!raw_response_length && raw_response) return IE_INVAL;
9724 /* Free output argument */
9725 xmlFreeDoc(*response); *response = NULL;
9726 if (raw_response) zfree(*raw_response);
9727 zfree(*code);
9728 zfree(*status_message);
9731 /* Check if connection is established
9732 * TODO: This check should be done downstairs. */
9733 if (!context->curl) return IE_CONNECTION_CLOSED;
9735 service_name_locale = _isds_utf82locale((char*)service_name);
9736 message_id_locale = _isds_utf82locale(message_id);
9737 if (!service_name_locale || !message_id_locale) {
9738 err = IE_NOMEM;
9739 goto leave;
9742 /* Build request */
9743 request = xmlNewNode(NULL, service_name);
9744 if (!request) {
9745 isds_printf_message(context,
9746 _("Could not build %s request for %s message ID"),
9747 service_name_locale, message_id_locale);
9748 err = IE_ERROR;
9749 goto leave;
9751 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9752 if(!isds_ns) {
9753 isds_log_message(context, _("Could not create ISDS name space"));
9754 err = IE_ERROR;
9755 goto leave;
9757 xmlSetNs(request, isds_ns);
9760 /* Add requested ID */
9761 err = validate_message_id_length(context, (xmlChar *) message_id);
9762 if (err) goto leave;
9763 INSERT_STRING(request, "dmID", message_id);
9766 isds_log(ILF_ISDS, ILL_DEBUG,
9767 _("Sending %s request for %s message ID to ISDS\n"),
9768 service_name_locale, message_id_locale);
9770 /* Send request */
9771 err = _isds(context, service, request, response,
9772 raw_response, raw_response_length);
9773 xmlFreeNode(request); request = NULL;
9775 if (err) {
9776 isds_log(ILF_ISDS, ILL_DEBUG,
9777 _("Processing ISDS response on %s request failed\n"),
9778 service_name_locale);
9779 goto leave;
9782 /* Check for response status */
9783 err = isds_response_status(context, service, *response,
9784 code, status_message, NULL);
9785 if (err) {
9786 isds_log(ILF_ISDS, ILL_DEBUG,
9787 _("ISDS response on %s request is missing status\n"),
9788 service_name_locale);
9789 goto leave;
9792 /* Request processed, but nothing found */
9793 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9794 char *code_locale = _isds_utf82locale((char*) *code);
9795 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9796 isds_log(ILF_ISDS, ILL_DEBUG,
9797 _("Server refused %s request for %s message ID "
9798 "(code=%s, message=%s)\n"),
9799 service_name_locale, message_id_locale,
9800 code_locale, status_message_locale);
9801 isds_log_message(context, status_message_locale);
9802 free(code_locale);
9803 free(status_message_locale);
9804 err = IE_ISDS;
9805 goto leave;
9808 leave:
9809 free(message_id_locale);
9810 free(service_name_locale);
9811 xmlFreeNode(request);
9812 return err;
9816 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9817 * signed data and free ISDS response.
9818 * @context is session context
9819 * @message_id is UTF-8 encoded message ID for logging purpose
9820 * @response is parsed XML document. It will be freed and NULLed in the middle
9821 * of function run to save memory. This is not guaranteed in case of error.
9822 * @request_name is name of ISDS request used to construct response root
9823 * element name and for logging purpose.
9824 * @raw is reallocated output buffer with DER encoded CMS data
9825 * @raw_length is size of @raw buffer in bytes
9826 * @returns standard error codes, in case of error, @raw will be freed and
9827 * NULLed, @response sometimes. */
9828 static isds_error find_extract_signed_data_free_response(
9829 struct isds_ctx *context, const xmlChar *message_id,
9830 xmlDocPtr *response, const xmlChar *request_name,
9831 void **raw, size_t *raw_length) {
9833 isds_error err = IE_SUCCESS;
9834 char *xpath_expression = NULL;
9835 xmlXPathContextPtr xpath_ctx = NULL;
9836 xmlXPathObjectPtr result = NULL;
9837 char *encoded_structure = NULL;
9839 if (!context) return IE_INVALID_CONTEXT;
9840 if (!raw) return IE_INVAL;
9841 zfree(*raw);
9842 if (!message_id || !response || !*response || !request_name || !raw_length)
9843 return IE_INVAL;
9845 /* Build XPath expression */
9846 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9847 "Response/isds:dmSignature");
9848 if (!xpath_expression) return IE_NOMEM;
9850 /* Extract data */
9851 xpath_ctx = xmlXPathNewContext(*response);
9852 if (!xpath_ctx) {
9853 err = IE_ERROR;
9854 goto leave;
9856 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9857 err = IE_ERROR;
9858 goto leave;
9860 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9861 if (!result) {
9862 err = IE_ERROR;
9863 goto leave;
9865 /* Empty response */
9866 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9867 char *message_id_locale = _isds_utf82locale((char*) message_id);
9868 isds_printf_message(context,
9869 _("Server did not return any signed data for message ID `%s' "
9870 "on %s request"),
9871 message_id_locale, request_name);
9872 free(message_id_locale);
9873 err = IE_ISDS;
9874 goto leave;
9876 /* More responses */
9877 if (result->nodesetval->nodeNr > 1) {
9878 char *message_id_locale = _isds_utf82locale((char*) message_id);
9879 isds_printf_message(context,
9880 _("Server did return more signed data for message ID `%s' "
9881 "on %s request"),
9882 message_id_locale, request_name);
9883 free(message_id_locale);
9884 err = IE_ISDS;
9885 goto leave;
9887 /* One response */
9888 xpath_ctx->node = result->nodesetval->nodeTab[0];
9890 /* Extract PKCS#7 structure */
9891 EXTRACT_STRING(".", encoded_structure);
9892 if (!encoded_structure) {
9893 isds_log_message(context, _("dmSignature element is empty"));
9896 /* Here we have delivery info as standalone CMS in encoded_structure.
9897 * We don't need any other data, free them: */
9898 xmlXPathFreeObject(result); result = NULL;
9899 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9900 xmlFreeDoc(*response); *response = NULL;
9903 /* Decode PKCS#7 to DER format */
9904 *raw_length = _isds_b64decode(encoded_structure, raw);
9905 if (*raw_length == (size_t) -1) {
9906 isds_log_message(context,
9907 _("Error while Base64-decoding PKCS#7 structure"));
9908 err = IE_ERROR;
9909 goto leave;
9912 leave:
9913 if (err) {
9914 zfree(*raw);
9915 raw_length = 0;
9918 free(encoded_structure);
9919 xmlXPathFreeObject(result);
9920 xmlXPathFreeContext(xpath_ctx);
9921 free(xpath_expression);
9923 return err;
9925 #endif /* HAVE_LIBCURL */
9928 /* Download incoming message envelope identified by ID.
9929 * @context is session context
9930 * @message_id is message identifier (you can get them from
9931 * isds_get_list_of_received_messages())
9932 * @message is automatically reallocated message retrieved from ISDS.
9933 * It will miss documents per se. Use isds_get_received_message(), if you are
9934 * interested in documents (content) too.
9935 * Returned hash and timestamp require documents to be verifiable. */
9936 isds_error isds_get_received_envelope(struct isds_ctx *context,
9937 const char *message_id, struct isds_message **message) {
9939 isds_error err = IE_SUCCESS;
9940 #if HAVE_LIBCURL
9941 xmlDocPtr response = NULL;
9942 xmlChar *code = NULL, *status_message = NULL;
9943 xmlXPathContextPtr xpath_ctx = NULL;
9944 xmlXPathObjectPtr result = NULL;
9945 #endif
9947 if (!context) return IE_INVALID_CONTEXT;
9948 zfree(context->long_message);
9950 /* Free former message if any */
9951 if (!message) return IE_INVAL;
9952 isds_message_free(message);
9954 #if HAVE_LIBCURL
9955 /* Do request and check for success */
9956 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9957 BAD_CAST "MessageEnvelopeDownload", message_id,
9958 &response, NULL, NULL, &code, &status_message);
9959 if (err) goto leave;
9961 /* Extract data */
9962 xpath_ctx = xmlXPathNewContext(response);
9963 if (!xpath_ctx) {
9964 err = IE_ERROR;
9965 goto leave;
9967 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9968 err = IE_ERROR;
9969 goto leave;
9971 result = xmlXPathEvalExpression(
9972 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9973 "isds:dmReturnedMessageEnvelope",
9974 xpath_ctx);
9975 if (!result) {
9976 err = IE_ERROR;
9977 goto leave;
9979 /* Empty response */
9980 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9981 char *message_id_locale = _isds_utf82locale((char*) message_id);
9982 isds_printf_message(context,
9983 _("Server did not return any envelope for ID `%s' "
9984 "on MessageEnvelopeDownload request"), message_id_locale);
9985 free(message_id_locale);
9986 err = IE_ISDS;
9987 goto leave;
9989 /* More envelops */
9990 if (result->nodesetval->nodeNr > 1) {
9991 char *message_id_locale = _isds_utf82locale((char*) message_id);
9992 isds_printf_message(context,
9993 _("Server did return more envelopes for ID `%s' "
9994 "on MessageEnvelopeDownload request"), message_id_locale);
9995 free(message_id_locale);
9996 err = IE_ISDS;
9997 goto leave;
9999 /* One message */
10000 xpath_ctx->node = result->nodesetval->nodeTab[0];
10002 /* Extract the envelope (= message without documents, hence 0) */
10003 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10004 if (err) goto leave;
10006 /* Save XML blob */
10007 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10008 &(*message)->raw_length);
10010 leave:
10011 if (err) {
10012 isds_message_free(message);
10015 xmlXPathFreeObject(result);
10016 xmlXPathFreeContext(xpath_ctx);
10018 free(code);
10019 free(status_message);
10020 if (!*message || !(*message)->xml) {
10021 xmlFreeDoc(response);
10024 if (!err)
10025 isds_log(ILF_ISDS, ILL_DEBUG,
10026 _("MessageEnvelopeDownload request processed by server "
10027 "successfully.\n")
10029 #else /* not HAVE_LIBCURL */
10030 err = IE_NOTSUP;
10031 #endif
10032 return err;
10036 /* Load delivery info of any format from buffer.
10037 * @context is session context
10038 * @raw_type advertises format of @buffer content. Only delivery info types
10039 * are accepted.
10040 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10041 * retrieve such data from message->raw after calling
10042 * isds_get_signed_delivery_info().
10043 * @length is length of buffer in bytes.
10044 * @message is automatically reallocated message parsed from @buffer.
10045 * @strategy selects how buffer will be attached into raw isds_message member.
10046 * */
10047 isds_error isds_load_delivery_info(struct isds_ctx *context,
10048 const isds_raw_type raw_type,
10049 const void *buffer, const size_t length,
10050 struct isds_message **message, const isds_buffer_strategy strategy) {
10052 isds_error err = IE_SUCCESS;
10053 message_ns_type message_ns;
10054 xmlDocPtr message_doc = NULL;
10055 xmlXPathContextPtr xpath_ctx = NULL;
10056 xmlXPathObjectPtr result = NULL;
10057 void *xml_stream = NULL;
10058 size_t xml_stream_length = 0;
10060 if (!context) return IE_INVALID_CONTEXT;
10061 zfree(context->long_message);
10062 if (!message) return IE_INVAL;
10063 isds_message_free(message);
10064 if (!buffer) return IE_INVAL;
10067 /* Select buffer format and extract XML from CMS*/
10068 switch (raw_type) {
10069 case RAWTYPE_DELIVERYINFO:
10070 message_ns = MESSAGE_NS_UNSIGNED;
10071 xml_stream = (void *) buffer;
10072 xml_stream_length = length;
10073 break;
10075 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
10076 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10077 xml_stream = (void *) buffer;
10078 xml_stream_length = length;
10079 break;
10081 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10082 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10083 err = _isds_extract_cms_data(context, buffer, length,
10084 &xml_stream, &xml_stream_length);
10085 if (err) goto leave;
10086 break;
10088 default:
10089 isds_log_message(context, _("Bad raw delivery representation type"));
10090 return IE_INVAL;
10091 break;
10094 isds_log(ILF_ISDS, ILL_DEBUG,
10095 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10096 xml_stream_length, xml_stream);
10098 /* Convert delivery info XML stream into XPath context */
10099 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10100 if (!message_doc) {
10101 err = IE_XML;
10102 goto leave;
10104 xpath_ctx = xmlXPathNewContext(message_doc);
10105 if (!xpath_ctx) {
10106 err = IE_ERROR;
10107 goto leave;
10109 /* XXX: Name spaces mangled for signed delivery info:
10110 * http://isds.czechpoint.cz/v20/delivery:
10112 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10113 * <q:dmDelivery>
10114 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10115 * <p:dmID>170272</p:dmID>
10116 * ...
10117 * </p:dmDm>
10118 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10119 * ...
10120 * </q:dmEvents>...</q:dmEvents>
10121 * </q:dmDelivery>
10122 * </q:GetDeliveryInfoResponse>
10123 * */
10124 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10125 err = IE_ERROR;
10126 goto leave;
10128 result = xmlXPathEvalExpression(
10129 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10130 xpath_ctx);
10131 if (!result) {
10132 err = IE_ERROR;
10133 goto leave;
10135 /* Empty delivery info */
10136 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10137 isds_printf_message(context,
10138 _("XML document is not sisds:dmDelivery document"));
10139 err = IE_ISDS;
10140 goto leave;
10142 /* More delivery info's */
10143 if (result->nodesetval->nodeNr > 1) {
10144 isds_printf_message(context,
10145 _("XML document has more sisds:dmDelivery elements"));
10146 err = IE_ISDS;
10147 goto leave;
10149 /* One delivery info */
10150 xpath_ctx->node = result->nodesetval->nodeTab[0];
10152 /* Extract the envelope (= message without documents, hence 0).
10153 * XXX: extract_TReturnedMessage() can obtain attachments size,
10154 * but delivery info carries none. It's coded as option elements,
10155 * so it should work. */
10156 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10157 if (err) goto leave;
10159 /* Extract events */
10160 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10161 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10162 if (err) { err = IE_ERROR; goto leave; }
10163 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10164 if (err) goto leave;
10166 /* Append raw CMS structure into message */
10167 (*message)->raw_type = raw_type;
10168 switch (strategy) {
10169 case BUFFER_DONT_STORE:
10170 break;
10171 case BUFFER_COPY:
10172 (*message)->raw = malloc(length);
10173 if (!(*message)->raw) {
10174 err = IE_NOMEM;
10175 goto leave;
10177 memcpy((*message)->raw, buffer, length);
10178 (*message)->raw_length = length;
10179 break;
10180 case BUFFER_MOVE:
10181 (*message)->raw = (void *) buffer;
10182 (*message)->raw_length = length;
10183 break;
10184 default:
10185 err = IE_ENUM;
10186 goto leave;
10189 leave:
10190 if (err) {
10191 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10192 isds_message_free(message);
10195 xmlXPathFreeObject(result);
10196 xmlXPathFreeContext(xpath_ctx);
10197 if (!*message || !(*message)->xml) {
10198 xmlFreeDoc(message_doc);
10200 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10202 if (!err)
10203 isds_log(ILF_ISDS, ILL_DEBUG,
10204 _("Delivery info loaded successfully.\n"));
10205 return err;
10209 /* Download signed delivery info-sheet of given message identified by ID.
10210 * @context is session context
10211 * @message_id is message identifier (you can get them from
10212 * isds_get_list_of_{sent,received}_messages())
10213 * @message is automatically reallocated message retrieved from ISDS.
10214 * It will miss documents per se. Use isds_get_signed_received_message(),
10215 * if you are interested in documents (content). OTOH, only this function
10216 * can get list events message has gone through. */
10217 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10218 const char *message_id, struct isds_message **message) {
10220 isds_error err = IE_SUCCESS;
10221 #if HAVE_LIBCURL
10222 xmlDocPtr response = NULL;
10223 xmlChar *code = NULL, *status_message = NULL;
10224 void *raw = NULL;
10225 size_t raw_length = 0;
10226 #endif
10228 if (!context) return IE_INVALID_CONTEXT;
10229 zfree(context->long_message);
10231 /* Free former message if any */
10232 if (!message) return IE_INVAL;
10233 isds_message_free(message);
10235 #if HAVE_LIBCURL
10236 /* Do request and check for success */
10237 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10238 BAD_CAST "GetSignedDeliveryInfo", message_id,
10239 &response, NULL, NULL, &code, &status_message);
10240 if (err) goto leave;
10242 /* Find signed delivery info, extract it into raw and maybe free
10243 * response */
10244 err = find_extract_signed_data_free_response(context,
10245 (xmlChar *)message_id, &response,
10246 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10247 if (err) goto leave;
10249 /* Parse delivery info */
10250 err = isds_load_delivery_info(context,
10251 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10252 message, BUFFER_MOVE);
10253 if (err) goto leave;
10255 raw = NULL;
10257 leave:
10258 if (err) {
10259 isds_message_free(message);
10262 free(raw);
10263 free(code);
10264 free(status_message);
10265 xmlFreeDoc(response);
10267 if (!err)
10268 isds_log(ILF_ISDS, ILL_DEBUG,
10269 _("GetSignedDeliveryInfo request processed by server "
10270 "successfully.\n")
10272 #else /* not HAVE_LIBCURL */
10273 err = IE_NOTSUP;
10274 #endif
10275 return err;
10279 /* Download delivery info-sheet of given message identified by ID.
10280 * @context is session context
10281 * @message_id is message identifier (you can get them from
10282 * isds_get_list_of_{sent,received}_messages())
10283 * @message is automatically reallocated message retrieved from ISDS.
10284 * It will miss documents per se. Use isds_get_received_message(), if you are
10285 * interested in documents (content). OTOH, only this function can get list
10286 * of events message has gone through. */
10287 isds_error isds_get_delivery_info(struct isds_ctx *context,
10288 const char *message_id, struct isds_message **message) {
10290 isds_error err = IE_SUCCESS;
10291 #if HAVE_LIBCURL
10292 xmlDocPtr response = NULL;
10293 xmlChar *code = NULL, *status_message = NULL;
10294 xmlNodePtr delivery_node = NULL;
10295 void *raw = NULL;
10296 size_t raw_length = 0;
10297 #endif
10299 if (!context) return IE_INVALID_CONTEXT;
10300 zfree(context->long_message);
10302 /* Free former message if any */
10303 if (!message) return IE_INVAL;
10304 isds_message_free(message);
10306 #if HAVE_LIBCURL
10307 /* Do request and check for success */
10308 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10309 BAD_CAST "GetDeliveryInfo", message_id,
10310 &response, NULL, NULL, &code, &status_message);
10311 if (err) goto leave;
10314 /* Serialize delivery info */
10315 delivery_node = xmlDocGetRootElement(response);
10316 if (!delivery_node) {
10317 char *message_id_locale = _isds_utf82locale((char*) message_id);
10318 isds_printf_message(context,
10319 _("Server did not return any delivery info for ID `%s' "
10320 "on GetDeliveryInfo request"), message_id_locale);
10321 free(message_id_locale);
10322 err = IE_ISDS;
10323 goto leave;
10325 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10326 if (err) goto leave;
10328 /* Parse delivery info */
10329 /* TODO: Here we parse the response second time. We could single delivery
10330 * parser from isds_load_delivery_info() to make things faster. */
10331 err = isds_load_delivery_info(context,
10332 RAWTYPE_DELIVERYINFO, raw, raw_length,
10333 message, BUFFER_MOVE);
10334 if (err) goto leave;
10336 raw = NULL;
10339 leave:
10340 if (err) {
10341 isds_message_free(message);
10344 free(raw);
10345 free(code);
10346 free(status_message);
10347 xmlFreeDoc(response);
10349 if (!err)
10350 isds_log(ILF_ISDS, ILL_DEBUG,
10351 _("GetDeliveryInfo request processed by server "
10352 "successfully.\n")
10354 #else /* not HAVE_LIBCURL */
10355 err = IE_NOTSUP;
10356 #endif
10357 return err;
10361 /* Download incoming message identified by ID.
10362 * @context is session context
10363 * @message_id is message identifier (you can get them from
10364 * isds_get_list_of_received_messages())
10365 * @message is automatically reallocated message retrieved from ISDS */
10366 isds_error isds_get_received_message(struct isds_ctx *context,
10367 const char *message_id, struct isds_message **message) {
10369 isds_error err = IE_SUCCESS;
10370 #if HAVE_LIBCURL
10371 xmlDocPtr response = NULL;
10372 void *xml_stream = NULL;
10373 size_t xml_stream_length;
10374 xmlChar *code = NULL, *status_message = NULL;
10375 xmlXPathContextPtr xpath_ctx = NULL;
10376 xmlXPathObjectPtr result = NULL;
10377 char *phys_path = NULL;
10378 size_t phys_start, phys_end;
10379 #endif
10381 if (!context) return IE_INVALID_CONTEXT;
10382 zfree(context->long_message);
10384 /* Free former message if any */
10385 if (NULL == message) return IE_INVAL;
10386 if (message) isds_message_free(message);
10388 #if HAVE_LIBCURL
10389 /* Do request and check for success */
10390 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10391 BAD_CAST "MessageDownload", message_id,
10392 &response, &xml_stream, &xml_stream_length,
10393 &code, &status_message);
10394 if (err) goto leave;
10396 /* Extract data */
10397 xpath_ctx = xmlXPathNewContext(response);
10398 if (!xpath_ctx) {
10399 err = IE_ERROR;
10400 goto leave;
10402 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10403 err = IE_ERROR;
10404 goto leave;
10406 result = xmlXPathEvalExpression(
10407 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10408 xpath_ctx);
10409 if (!result) {
10410 err = IE_ERROR;
10411 goto leave;
10413 /* Empty response */
10414 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10415 char *message_id_locale = _isds_utf82locale((char*) message_id);
10416 isds_printf_message(context,
10417 _("Server did not return any message for ID `%s' "
10418 "on MessageDownload request"), message_id_locale);
10419 free(message_id_locale);
10420 err = IE_ISDS;
10421 goto leave;
10423 /* More messages */
10424 if (result->nodesetval->nodeNr > 1) {
10425 char *message_id_locale = _isds_utf82locale((char*) message_id);
10426 isds_printf_message(context,
10427 _("Server did return more messages for ID `%s' "
10428 "on MessageDownload request"), message_id_locale);
10429 free(message_id_locale);
10430 err = IE_ISDS;
10431 goto leave;
10433 /* One message */
10434 xpath_ctx->node = result->nodesetval->nodeTab[0];
10436 /* Extract the message */
10437 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10438 if (err) goto leave;
10440 /* Locate raw XML blob */
10441 phys_path = strdup(
10442 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10443 PHYSXML_ELEMENT_SEPARATOR
10444 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10445 PHYSXML_ELEMENT_SEPARATOR
10446 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10448 if (!phys_path) {
10449 err = IE_NOMEM;
10450 goto leave;
10452 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10453 phys_path, &phys_start, &phys_end);
10454 zfree(phys_path);
10455 if (err) {
10456 isds_log_message(context,
10457 _("Substring with isds:MessageDownloadResponse element "
10458 "could not be located in raw SOAP message"));
10459 goto leave;
10461 /* Save XML blob */
10462 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10463 &(*message)->raw_length);*/
10464 /* TODO: Store name space declarations from ancestors */
10465 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10466 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10467 (*message)->raw_length = phys_end - phys_start + 1;
10468 (*message)->raw = malloc((*message)->raw_length);
10469 if (!(*message)->raw) {
10470 err = IE_NOMEM;
10471 goto leave;
10473 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10476 leave:
10477 if (err) {
10478 isds_message_free(message);
10481 free(phys_path);
10483 xmlXPathFreeObject(result);
10484 xmlXPathFreeContext(xpath_ctx);
10486 free(code);
10487 free(status_message);
10488 free(xml_stream);
10489 if (!*message || !(*message)->xml) {
10490 xmlFreeDoc(response);
10493 if (!err)
10494 isds_log(ILF_ISDS, ILL_DEBUG,
10495 _("MessageDownload request processed by server "
10496 "successfully.\n")
10498 #else /* not HAVE_LIBCURL */
10499 err = IE_NOTSUP;
10500 #endif
10501 return err;
10505 /* Load message of any type from buffer.
10506 * @context is session context
10507 * @raw_type defines content type of @buffer. Only message types are allowed.
10508 * @buffer is message raw representation. Format (CMS, plain signed,
10509 * message direction) is defined in @raw_type. You can retrieve such data
10510 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10511 * @length is length of buffer in bytes.
10512 * @message is automatically reallocated message parsed from @buffer.
10513 * @strategy selects how buffer will be attached into raw isds_message member.
10514 * */
10515 isds_error isds_load_message(struct isds_ctx *context,
10516 const isds_raw_type raw_type, const void *buffer, const size_t length,
10517 struct isds_message **message, const isds_buffer_strategy strategy) {
10519 isds_error err = IE_SUCCESS;
10520 void *xml_stream = NULL;
10521 size_t xml_stream_length = 0;
10522 message_ns_type message_ns;
10523 xmlDocPtr message_doc = NULL;
10524 xmlXPathContextPtr xpath_ctx = NULL;
10525 xmlXPathObjectPtr result = NULL;
10527 if (!context) return IE_INVALID_CONTEXT;
10528 zfree(context->long_message);
10529 if (!message) return IE_INVAL;
10530 isds_message_free(message);
10531 if (!buffer) return IE_INVAL;
10534 /* Select buffer format and extract XML from CMS*/
10535 switch (raw_type) {
10536 case RAWTYPE_INCOMING_MESSAGE:
10537 message_ns = MESSAGE_NS_UNSIGNED;
10538 xml_stream = (void *) buffer;
10539 xml_stream_length = length;
10540 break;
10542 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10543 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10544 xml_stream = (void *) buffer;
10545 xml_stream_length = length;
10546 break;
10548 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10549 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10550 err = _isds_extract_cms_data(context, buffer, length,
10551 &xml_stream, &xml_stream_length);
10552 if (err) goto leave;
10553 break;
10555 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10556 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10557 xml_stream = (void *) buffer;
10558 xml_stream_length = length;
10559 break;
10561 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10562 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10563 err = _isds_extract_cms_data(context, buffer, length,
10564 &xml_stream, &xml_stream_length);
10565 if (err) goto leave;
10566 break;
10568 default:
10569 isds_log_message(context, _("Bad raw message representation type"));
10570 return IE_INVAL;
10571 break;
10574 isds_log(ILF_ISDS, ILL_DEBUG,
10575 _("Loading message:\n%.*s\nEnd of message\n"),
10576 xml_stream_length, xml_stream);
10578 /* Convert messages XML stream into XPath context */
10579 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10580 if (!message_doc) {
10581 err = IE_XML;
10582 goto leave;
10584 xpath_ctx = xmlXPathNewContext(message_doc);
10585 if (!xpath_ctx) {
10586 err = IE_ERROR;
10587 goto leave;
10589 /* XXX: Standard name space for unsigned incoming direction:
10590 * http://isds.czechpoint.cz/v20/
10592 * XXX: Name spaces mangled for signed outgoing direction:
10593 * http://isds.czechpoint.cz/v20/SentMessage:
10595 * <q:MessageDownloadResponse
10596 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10597 * <q:dmReturnedMessage>
10598 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10599 * <p:dmID>151916</p:dmID>
10600 * ...
10601 * </p:dmDm>
10602 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10603 * ...
10604 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10605 * </q:dmReturnedMessage>
10606 * </q:MessageDownloadResponse>
10608 * XXX: Name spaces mangled for signed incoming direction:
10609 * http://isds.czechpoint.cz/v20/message:
10611 * <q:MessageDownloadResponse
10612 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10613 * <q:dmReturnedMessage>
10614 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10615 * <p:dmID>151916</p:dmID>
10616 * ...
10617 * </p:dmDm>
10618 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10619 * ...
10620 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10621 * </q:dmReturnedMessage>
10622 * </q:MessageDownloadResponse>
10624 * Stupidity of ISDS developers is unlimited */
10625 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10626 err = IE_ERROR;
10627 goto leave;
10629 result = xmlXPathEvalExpression(
10630 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10631 xpath_ctx);
10632 if (!result) {
10633 err = IE_ERROR;
10634 goto leave;
10636 /* Empty message */
10637 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10638 isds_printf_message(context,
10639 _("XML document does not contain "
10640 "sisds:dmReturnedMessage element"));
10641 err = IE_ISDS;
10642 goto leave;
10644 /* More messages */
10645 if (result->nodesetval->nodeNr > 1) {
10646 isds_printf_message(context,
10647 _("XML document has more sisds:dmReturnedMessage elements"));
10648 err = IE_ISDS;
10649 goto leave;
10651 /* One message */
10652 xpath_ctx->node = result->nodesetval->nodeTab[0];
10654 /* Extract the message */
10655 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10656 if (err) goto leave;
10658 /* Append raw buffer into message */
10659 (*message)->raw_type = raw_type;
10660 switch (strategy) {
10661 case BUFFER_DONT_STORE:
10662 break;
10663 case BUFFER_COPY:
10664 (*message)->raw = malloc(length);
10665 if (!(*message)->raw) {
10666 err = IE_NOMEM;
10667 goto leave;
10669 memcpy((*message)->raw, buffer, length);
10670 (*message)->raw_length = length;
10671 break;
10672 case BUFFER_MOVE:
10673 (*message)->raw = (void *) buffer;
10674 (*message)->raw_length = length;
10675 break;
10676 default:
10677 err = IE_ENUM;
10678 goto leave;
10682 leave:
10683 if (err) {
10684 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10685 isds_message_free(message);
10688 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10689 xmlXPathFreeObject(result);
10690 xmlXPathFreeContext(xpath_ctx);
10691 if (!*message || !(*message)->xml) {
10692 xmlFreeDoc(message_doc);
10695 if (!err)
10696 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10697 return err;
10701 /* Determine type of raw message or delivery info according some heuristics.
10702 * It does not validate the raw blob.
10703 * @context is session context
10704 * @raw_type returns content type of @buffer. Valid only if exit code of this
10705 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10706 * reallocated memory.
10707 * @buffer is message raw representation.
10708 * @length is length of buffer in bytes. */
10709 isds_error isds_guess_raw_type(struct isds_ctx *context,
10710 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10711 isds_error err;
10712 void *xml_stream = NULL;
10713 size_t xml_stream_length = 0;
10714 xmlDocPtr document = NULL;
10715 xmlNodePtr root = NULL;
10717 if (!context) return IE_INVALID_CONTEXT;
10718 zfree(context->long_message);
10719 if (length == 0 || !buffer) return IE_INVAL;
10720 if (!raw_type) return IE_INVAL;
10722 /* Try CMS */
10723 err = _isds_extract_cms_data(context, buffer, length,
10724 &xml_stream, &xml_stream_length);
10725 if (err) {
10726 xml_stream = (void *) buffer;
10727 xml_stream_length = (size_t) length;
10728 err = IE_SUCCESS;
10731 /* Try XML */
10732 document = xmlParseMemory(xml_stream, xml_stream_length);
10733 if (!document) {
10734 isds_printf_message(context,
10735 _("Could not parse data as XML document"));
10736 err = IE_NOTSUP;
10737 goto leave;
10740 /* Get root element */
10741 root = xmlDocGetRootElement(document);
10742 if (!root) {
10743 isds_printf_message(context,
10744 _("XML document is missing root element"));
10745 err = IE_XML;
10746 goto leave;
10749 if (!root->ns || !root->ns->href) {
10750 isds_printf_message(context,
10751 _("Root element does not belong to any name space"));
10752 err = IE_NOTSUP;
10753 goto leave;
10756 /* Test name space */
10757 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10758 if (xml_stream == buffer)
10759 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10760 else
10761 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10762 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10763 if (xml_stream == buffer)
10764 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10765 else
10766 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10767 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10768 if (xml_stream == buffer)
10769 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10770 else
10771 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10772 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10773 if (xml_stream != buffer) {
10774 isds_printf_message(context,
10775 _("Document in ISDS name space is encapsulated into CMS" ));
10776 err = IE_NOTSUP;
10777 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10778 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10779 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10780 *raw_type = RAWTYPE_DELIVERYINFO;
10781 else {
10782 isds_printf_message(context,
10783 _("Unknown root element in ISDS name space"));
10784 err = IE_NOTSUP;
10786 } else {
10787 isds_printf_message(context,
10788 _("Unknown name space"));
10789 err = IE_NOTSUP;
10792 leave:
10793 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10794 xmlFreeDoc(document);
10795 return err;
10799 /* Download signed incoming/outgoing message identified by ID.
10800 * @context is session context
10801 * @output is true for outgoing message, false for incoming message
10802 * @message_id is message identifier (you can get them from
10803 * isds_get_list_of_{sent,received}_messages())
10804 * @message is automatically reallocated message retrieved from ISDS. The raw
10805 * member will be filled with PKCS#7 structure in DER format. */
10806 static isds_error isds_get_signed_message(struct isds_ctx *context,
10807 const _Bool outgoing, const char *message_id,
10808 struct isds_message **message) {
10810 isds_error err = IE_SUCCESS;
10811 #if HAVE_LIBCURL
10812 xmlDocPtr response = NULL;
10813 xmlChar *code = NULL, *status_message = NULL;
10814 xmlXPathContextPtr xpath_ctx = NULL;
10815 xmlXPathObjectPtr result = NULL;
10816 char *encoded_structure = NULL;
10817 void *raw = NULL;
10818 size_t raw_length = 0;
10819 #endif
10821 if (!context) return IE_INVALID_CONTEXT;
10822 zfree(context->long_message);
10823 if (!message) return IE_INVAL;
10824 isds_message_free(message);
10826 #if HAVE_LIBCURL
10827 /* Do request and check for success */
10828 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10829 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10830 BAD_CAST "SignedMessageDownload",
10831 message_id, &response, NULL, NULL, &code, &status_message);
10832 if (err) goto leave;
10834 /* Find signed message, extract it into raw and maybe free
10835 * response */
10836 err = find_extract_signed_data_free_response(context,
10837 (xmlChar *)message_id, &response,
10838 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10839 BAD_CAST "SignedMessageDownload",
10840 &raw, &raw_length);
10841 if (err) goto leave;
10843 /* Parse message */
10844 err = isds_load_message(context,
10845 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10846 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10847 raw, raw_length, message, BUFFER_MOVE);
10848 if (err) goto leave;
10850 raw = NULL;
10852 leave:
10853 if (err) {
10854 isds_message_free(message);
10857 free(encoded_structure);
10858 xmlXPathFreeObject(result);
10859 xmlXPathFreeContext(xpath_ctx);
10860 free(raw);
10862 free(code);
10863 free(status_message);
10864 xmlFreeDoc(response);
10866 if (!err)
10867 isds_log(ILF_ISDS, ILL_DEBUG,
10868 (outgoing) ?
10869 _("SignedSentMessageDownload request processed by server "
10870 "successfully.\n") :
10871 _("SignedMessageDownload request processed by server "
10872 "successfully.\n")
10874 #else /* not HAVE_LIBCURL */
10875 err = IE_NOTSUP;
10876 #endif
10877 return err;
10881 /* Download signed incoming message identified by ID.
10882 * @context is session context
10883 * @message_id is message identifier (you can get them from
10884 * isds_get_list_of_received_messages())
10885 * @message is automatically reallocated message retrieved from ISDS. The raw
10886 * member will be filled with PKCS#7 structure in DER format. */
10887 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10888 const char *message_id, struct isds_message **message) {
10889 return isds_get_signed_message(context, 0, message_id, message);
10893 /* Download signed outgoing 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_sent_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_sent_message(struct isds_ctx *context,
10900 const char *message_id, struct isds_message **message) {
10901 return isds_get_signed_message(context, 1, message_id, message);
10905 /* Get type and name of user who sent a message identified by ID.
10906 * @context is session context
10907 * @message_id is message identifier
10908 * @sender_type is pointer to automatically allocated type of sender detected
10909 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10910 * library or to the server, NULL will be returned. Pass NULL if you don't
10911 * care about it.
10912 * @raw_sender_type is automatically reallocated UTF-8 string describing
10913 * sender type or NULL if not known to server. Pass NULL if you don't care.
10914 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10915 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10916 isds_error isds_get_message_sender(struct isds_ctx *context,
10917 const char *message_id, isds_sender_type **sender_type,
10918 char **raw_sender_type, char **sender_name) {
10919 isds_error err = IE_SUCCESS;
10920 #if HAVE_LIBCURL
10921 xmlDocPtr response = NULL;
10922 xmlChar *code = NULL, *status_message = NULL;
10923 xmlXPathContextPtr xpath_ctx = NULL;
10924 xmlXPathObjectPtr result = NULL;
10925 char *type_string = NULL;
10926 #endif
10928 if (!context) return IE_INVALID_CONTEXT;
10929 zfree(context->long_message);
10930 if (sender_type) zfree(*sender_type);
10931 if (raw_sender_type) zfree(*raw_sender_type);
10932 if (sender_name) zfree(*sender_name);
10933 if (!message_id) return IE_INVAL;
10935 #if HAVE_LIBCURL
10936 /* Do request and check for success */
10937 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10938 BAD_CAST "GetMessageAuthor",
10939 message_id, &response, NULL, NULL, &code, &status_message);
10940 if (err) goto leave;
10942 /* Extract data */
10943 xpath_ctx = xmlXPathNewContext(response);
10944 if (!xpath_ctx) {
10945 err = IE_ERROR;
10946 goto leave;
10948 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10949 err = IE_ERROR;
10950 goto leave;
10952 result = xmlXPathEvalExpression(
10953 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10954 if (!result) {
10955 err = IE_ERROR;
10956 goto leave;
10958 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10959 isds_log_message(context,
10960 _("Missing GetMessageAuthorResponse element"));
10961 err = IE_ISDS;
10962 goto leave;
10964 if (result->nodesetval->nodeNr > 1) {
10965 isds_log_message(context,
10966 _("Multiple GetMessageAuthorResponse element"));
10967 err = IE_ISDS;
10968 goto leave;
10970 xpath_ctx->node = result->nodesetval->nodeTab[0];
10971 xmlXPathFreeObject(result); result = NULL;
10973 /* Fill output arguments in */
10974 EXTRACT_STRING("isds:userType", type_string);
10975 if (NULL != type_string) {
10976 if (NULL != sender_type) {
10977 *sender_type = calloc(1, sizeof(**sender_type));
10978 if (NULL == *sender_type) {
10979 err = IE_NOMEM;
10980 goto leave;
10983 err = string2isds_sender_type((xmlChar *)type_string,
10984 *sender_type);
10985 if (err) {
10986 zfree(*sender_type);
10987 if (err == IE_ENUM) {
10988 err = IE_SUCCESS;
10989 char *type_string_locale = _isds_utf82locale(type_string);
10990 isds_log(ILF_ISDS, ILL_WARNING,
10991 _("Unknown isds:userType value: %s"),
10992 type_string_locale);
10993 free(type_string_locale);
10998 if (NULL != sender_name)
10999 EXTRACT_STRING("isds:authorName", *sender_name);
11001 leave:
11002 if (err) {
11003 if (NULL != sender_type) zfree(*sender_type);
11004 zfree(type_string);
11005 if (NULL != sender_name) zfree(*sender_name);
11007 if (NULL != raw_sender_type) *raw_sender_type = type_string;
11009 xmlXPathFreeObject(result);
11010 xmlXPathFreeContext(xpath_ctx);
11012 free(code);
11013 free(status_message);
11014 xmlFreeDoc(response);
11016 if (!err)
11017 isds_log(ILF_ISDS, ILL_DEBUG,
11018 _("GetMessageAuthor request processed by server "
11019 "successfully.\n"));
11020 #else /* not HAVE_LIBCURL */
11021 err = IE_NOTSUP;
11022 #endif
11023 return err;
11027 /* Retrieve hash of message identified by ID stored in ISDS.
11028 * @context is session context
11029 * @message_id is message identifier
11030 * @hash is automatically reallocated message hash downloaded from ISDS.
11031 * Message must exist in system and must not be deleted. */
11032 isds_error isds_download_message_hash(struct isds_ctx *context,
11033 const char *message_id, struct isds_hash **hash) {
11035 isds_error err = IE_SUCCESS;
11036 #if HAVE_LIBCURL
11037 xmlDocPtr response = NULL;
11038 xmlChar *code = NULL, *status_message = NULL;
11039 xmlXPathContextPtr xpath_ctx = NULL;
11040 xmlXPathObjectPtr result = NULL;
11041 #endif
11043 if (!context) return IE_INVALID_CONTEXT;
11044 zfree(context->long_message);
11046 isds_hash_free(hash);
11048 #if HAVE_LIBCURL
11049 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11050 BAD_CAST "VerifyMessage", message_id,
11051 &response, NULL, NULL, &code, &status_message);
11052 if (err) goto leave;
11055 /* Extract data */
11056 xpath_ctx = xmlXPathNewContext(response);
11057 if (!xpath_ctx) {
11058 err = IE_ERROR;
11059 goto leave;
11061 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11062 err = IE_ERROR;
11063 goto leave;
11065 result = xmlXPathEvalExpression(
11066 BAD_CAST "/isds:VerifyMessageResponse",
11067 xpath_ctx);
11068 if (!result) {
11069 err = IE_ERROR;
11070 goto leave;
11072 /* Empty response */
11073 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11074 char *message_id_locale = _isds_utf82locale((char*) message_id);
11075 isds_printf_message(context,
11076 _("Server did not return any response for ID `%s' "
11077 "on VerifyMessage request"), message_id_locale);
11078 free(message_id_locale);
11079 err = IE_ISDS;
11080 goto leave;
11082 /* More responses */
11083 if (result->nodesetval->nodeNr > 1) {
11084 char *message_id_locale = _isds_utf82locale((char*) message_id);
11085 isds_printf_message(context,
11086 _("Server did return more responses for ID `%s' "
11087 "on VerifyMessage request"), message_id_locale);
11088 free(message_id_locale);
11089 err = IE_ISDS;
11090 goto leave;
11092 /* One response */
11093 xpath_ctx->node = result->nodesetval->nodeTab[0];
11095 /* Extract the hash */
11096 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11098 leave:
11099 if (err) {
11100 isds_hash_free(hash);
11103 xmlXPathFreeObject(result);
11104 xmlXPathFreeContext(xpath_ctx);
11106 free(code);
11107 free(status_message);
11108 xmlFreeDoc(response);
11110 if (!err)
11111 isds_log(ILF_ISDS, ILL_DEBUG,
11112 _("VerifyMessage request processed by server "
11113 "successfully.\n")
11115 #else /* not HAVE_LIBCURL */
11116 err = IE_NOTSUP;
11117 #endif
11118 return err;
11122 /* Erase message specified by @message_id from long term storage. Other
11123 * message cannot be erased on user request.
11124 * @context is session context
11125 * @message_id is message identifier.
11126 * @incoming is true for incoming message, false for outgoing message.
11127 * @return
11128 * IE_SUCCESS if message has ben removed
11129 * IE_INVAL if message does not exist in long term storage or message
11130 * belongs to different box
11131 * TODO: IE_NOEPRM if user has no permission to erase a message */
11132 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11133 const char *message_id, _Bool incoming) {
11134 isds_error err = IE_SUCCESS;
11135 #if HAVE_LIBCURL
11136 xmlNodePtr request = NULL, node;
11137 xmlNsPtr isds_ns = NULL;
11138 xmlDocPtr response = NULL;
11139 xmlChar *code = NULL, *status_message = NULL;
11140 #endif
11142 if (!context) return IE_INVALID_CONTEXT;
11143 zfree(context->long_message);
11144 if (NULL == message_id) return IE_INVAL;
11146 /* Check if connection is established
11147 * TODO: This check should be done downstairs. */
11148 if (!context->curl) return IE_CONNECTION_CLOSED;
11150 #if HAVE_LIBCURL
11151 /* Build request */
11152 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11153 if (!request) {
11154 isds_log_message(context,
11155 _("Could build EraseMessage request"));
11156 return IE_ERROR;
11158 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11159 if(!isds_ns) {
11160 isds_log_message(context, _("Could not create ISDS name space"));
11161 xmlFreeNode(request);
11162 return IE_ERROR;
11164 xmlSetNs(request, isds_ns);
11166 err = validate_message_id_length(context, (xmlChar *) message_id);
11167 if (err) goto leave;
11168 INSERT_STRING(request, "dmID", message_id);
11170 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11173 /* Send request */
11174 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11175 "message ID %s to ISDS\n"), message_id);
11176 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11177 xmlFreeNode(request); request = NULL;
11179 if (err) {
11180 isds_log(ILF_ISDS, ILL_DEBUG,
11181 _("Processing ISDS response on EraseMessage request "
11182 "failed\n"));
11183 goto leave;
11186 /* Check for response status */
11187 err = isds_response_status(context, SERVICE_DM_INFO, response,
11188 &code, &status_message, NULL);
11189 if (err) {
11190 isds_log(ILF_ISDS, ILL_DEBUG,
11191 _("ISDS response on EraseMessage request is missing "
11192 "status\n"));
11193 goto leave;
11196 /* Check server status code */
11197 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11198 isds_log_message(context, _("Message to erase belongs to other box"));
11199 err = IE_INVAL;
11200 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11201 isds_log_message(context, _("Message to erase is not saved in "
11202 "long term storage or the direction does not match"));
11203 err = IE_INVAL;
11204 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11205 char *code_locale = _isds_utf82locale((char*) code);
11206 char *message_locale = _isds_utf82locale((char*) status_message);
11207 isds_log(ILF_ISDS, ILL_DEBUG,
11208 _("Server refused EraseMessage request "
11209 "(code=%s, message=%s)\n"),
11210 code_locale, message_locale);
11211 isds_log_message(context, message_locale);
11212 free(code_locale);
11213 free(message_locale);
11214 err = IE_ISDS;
11215 goto leave;
11218 leave:
11219 free(code);
11220 free(status_message);
11221 xmlFreeDoc(response);
11222 xmlFreeNode(request);
11224 if (!err)
11225 isds_log(ILF_ISDS, ILL_DEBUG,
11226 _("EraseMessage request processed by server "
11227 "successfully.\n")
11229 #else /* not HAVE_LIBCURL */
11230 err = IE_NOTSUP;
11231 #endif
11232 return err;
11236 /* Mark message as read. This is a transactional commit function to acknowledge
11237 * to ISDS the message has been downloaded and processed by client properly.
11238 * @context is session context
11239 * @message_id is message identifier. */
11240 isds_error isds_mark_message_read(struct isds_ctx *context,
11241 const char *message_id) {
11243 isds_error err = IE_SUCCESS;
11244 #if HAVE_LIBCURL
11245 xmlDocPtr response = NULL;
11246 xmlChar *code = NULL, *status_message = NULL;
11247 #endif
11249 if (!context) return IE_INVALID_CONTEXT;
11250 zfree(context->long_message);
11252 #if HAVE_LIBCURL
11253 /* Do request and check for success */
11254 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11255 BAD_CAST "MarkMessageAsDownloaded", message_id,
11256 &response, NULL, NULL, &code, &status_message);
11258 free(code);
11259 free(status_message);
11260 xmlFreeDoc(response);
11262 if (!err)
11263 isds_log(ILF_ISDS, ILL_DEBUG,
11264 _("MarkMessageAsDownloaded request processed by server "
11265 "successfully.\n")
11267 #else /* not HAVE_LIBCURL */
11268 err = IE_NOTSUP;
11269 #endif
11270 return err;
11274 /* Mark message as received by recipient. This is applicable only to
11275 * commercial message. Use envelope->dmType message member to distinguish
11276 * commercial message from government message. Government message is
11277 * received automatically (by law), commercial message on recipient request.
11278 * @context is session context
11279 * @message_id is message identifier. */
11280 isds_error isds_mark_message_received(struct isds_ctx *context,
11281 const char *message_id) {
11283 isds_error err = IE_SUCCESS;
11284 #if HAVE_LIBCURL
11285 xmlDocPtr response = NULL;
11286 xmlChar *code = NULL, *status_message = NULL;
11287 #endif
11289 if (!context) return IE_INVALID_CONTEXT;
11290 zfree(context->long_message);
11292 #if HAVE_LIBCURL
11293 /* Do request and check for success */
11294 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11295 BAD_CAST "ConfirmDelivery", message_id,
11296 &response, NULL, NULL, &code, &status_message);
11298 free(code);
11299 free(status_message);
11300 xmlFreeDoc(response);
11302 if (!err)
11303 isds_log(ILF_ISDS, ILL_DEBUG,
11304 _("ConfirmDelivery request processed by server "
11305 "successfully.\n")
11307 #else /* not HAVE_LIBCURL */
11308 err = IE_NOTSUP;
11309 #endif
11310 return err;
11314 /* Send document for authorized conversion into Czech POINT system.
11315 * This is public anonymous service, no log-in necessary. Special context is
11316 * used to reuse keep-a-live HTTPS connection.
11317 * @context is Czech POINT session context. DO NOT use context connected to
11318 * ISDS server. Use new context or context used by this function previously.
11319 * @document is document to convert. Only data, data_length, dmFileDescr and
11320 * is_xml members are significant. Be ware that not all document formats can be
11321 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11322 * @id is reallocated identifier assigned by Czech POINT system to
11323 * your document on submit. Use is to tell it to Czech POINT officer.
11324 * @date is reallocated document submit date (submitted documents
11325 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11326 * value. */
11327 isds_error czp_convert_document(struct isds_ctx *context,
11328 const struct isds_document *document,
11329 char **id, struct tm **date) {
11330 isds_error err = IE_SUCCESS;
11331 #if HAVE_LIBCURL
11332 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11333 xmlNodePtr request = NULL, node;
11334 xmlDocPtr response = NULL;
11336 xmlXPathContextPtr xpath_ctx = NULL;
11337 xmlXPathObjectPtr result = NULL;
11338 long int status = -1;
11339 long int *status_ptr = &status;
11340 char *string = NULL;
11341 #endif
11344 if (!context) return IE_INVALID_CONTEXT;
11345 zfree(context->long_message);
11346 if (!document || !id || !date) return IE_INVAL;
11348 if (document->is_xml) {
11349 isds_log_message(context,
11350 _("XML documents cannot be submitted to conversion"));
11351 return IE_NOTSUP;
11354 /* Free output arguments */
11355 zfree(*id);
11356 zfree(*date);
11358 #if HAVE_LIBCURL
11359 /* Store configuration */
11360 context->type = CTX_TYPE_CZP;
11361 free(context->url);
11362 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11363 if (!(context->url))
11364 return IE_NOMEM;
11366 /* Prepare CURL handle if not yet connected */
11367 if (!context->curl) {
11368 context->curl = curl_easy_init();
11369 if (!(context->curl))
11370 return IE_ERROR;
11373 /* Build conversion request */
11374 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11375 if (!request) {
11376 isds_log_message(context,
11377 _("Could not build Czech POINT conversion request"));
11378 return IE_ERROR;
11380 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11381 if(!deposit_ns) {
11382 isds_log_message(context,
11383 _("Could not create Czech POINT deposit name space"));
11384 xmlFreeNode(request);
11385 return IE_ERROR;
11387 xmlSetNs(request, deposit_ns);
11389 /* Insert children. They are in empty namespace! */
11390 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11391 if(!empty_ns) {
11392 isds_log_message(context, _("Could not create empty name space"));
11393 err = IE_ERROR;
11394 goto leave;
11396 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11397 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11398 document->dmFileDescr);
11400 /* Document encoded in Base64 */
11401 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11402 document->data, document->data_length);
11403 if (err) goto leave;
11405 isds_log(ILF_ISDS, ILL_DEBUG,
11406 _("Submitting document for conversion into Czech POINT deposit"));
11408 /* Send conversion request */
11409 err = _czp_czpdeposit(context, request, &response);
11410 xmlFreeNode(request); request = NULL;
11412 if (err) {
11413 czp_do_close_connection(context);
11414 goto leave;
11418 /* Extract response */
11419 xpath_ctx = xmlXPathNewContext(response);
11420 if (!xpath_ctx) {
11421 err = IE_ERROR;
11422 goto leave;
11424 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11425 err = IE_ERROR;
11426 goto leave;
11428 result = xmlXPathEvalExpression(
11429 BAD_CAST "/deposit:saveDocumentResponse/return",
11430 xpath_ctx);
11431 if (!result) {
11432 err = IE_ERROR;
11433 goto leave;
11435 /* Empty response */
11436 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11437 isds_printf_message(context,
11438 _("Missing `return' element in Czech POINT deposit response"));
11439 err = IE_ISDS;
11440 goto leave;
11442 /* More responses */
11443 if (result->nodesetval->nodeNr > 1) {
11444 isds_printf_message(context,
11445 _("Multiple `return' element in Czech POINT deposit response"));
11446 err = IE_ISDS;
11447 goto leave;
11449 /* One response */
11450 xpath_ctx->node = result->nodesetval->nodeTab[0];
11452 /* Get status */
11453 EXTRACT_LONGINT("status", status_ptr, 1);
11454 if (status) {
11455 EXTRACT_STRING("statusMsg", string);
11456 char *string_locale = _isds_utf82locale(string);
11457 isds_printf_message(context,
11458 _("Czech POINT deposit refused document for conversion "
11459 "(code=%ld, message=%s)"),
11460 status, string_locale);
11461 free(string_locale);
11462 err = IE_ISDS;
11463 goto leave;
11466 /* Get document ID */
11467 EXTRACT_STRING("documentID", *id);
11469 /* Get submit date */
11470 EXTRACT_STRING("dateInserted", string);
11471 if (string) {
11472 *date = calloc(1, sizeof(**date));
11473 if (!*date) {
11474 err = IE_NOMEM;
11475 goto leave;
11477 err = _isds_datestring2tm((xmlChar *)string, *date);
11478 if (err) {
11479 if (err == IE_NOTSUP) {
11480 err = IE_ISDS;
11481 char *string_locale = _isds_utf82locale(string);
11482 isds_printf_message(context,
11483 _("Invalid dateInserted value: %s"), string_locale);
11484 free(string_locale);
11486 goto leave;
11490 leave:
11491 free(string);
11492 xmlXPathFreeObject(result);
11493 xmlXPathFreeContext(xpath_ctx);
11495 xmlFreeDoc(response);
11496 xmlFreeNode(request);
11498 if (!err) {
11499 char *id_locale = _isds_utf82locale((char *) *id);
11500 isds_log(ILF_ISDS, ILL_DEBUG,
11501 _("Document %s has been submitted for conversion "
11502 "to server successfully\n"), id_locale);
11503 free(id_locale);
11505 #else /* not HAVE_LIBCURL */
11506 err = IE_NOTSUP;
11507 #endif
11508 return err;
11512 /* Close possibly opened connection to Czech POINT document deposit.
11513 * @context is Czech POINT session context. */
11514 isds_error czp_close_connection(struct isds_ctx *context) {
11515 if (!context) return IE_INVALID_CONTEXT;
11516 zfree(context->long_message);
11517 #if HAVE_LIBCURL
11518 return czp_do_close_connection(context);
11519 #else
11520 return IE_NOTSUP;
11521 #endif
11525 /* Send request for new box creation in testing ISDS instance.
11526 * It's not possible to request for a production box currently, as it
11527 * communicates via e-mail.
11528 * XXX: This function does not work either. Server complains about invalid
11529 * e-mail address.
11530 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11531 * this function
11532 * @context is special session context for box creation request. DO NOT use
11533 * standard context as it could reveal your password. Use fresh new context or
11534 * context previously used by this function.
11535 * @box is box description to create including single primary user (in case of
11536 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
11537 * ignored. It outputs box ID assigned by ISDS in dbID element.
11538 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11539 * box, or contact address of PFO box owner). The email member is mandatory as
11540 * it will be used to deliver credentials.
11541 * @former_names is former name of box owner. Pass NULL if you don't care.
11542 * @approval is optional external approval of box manipulation
11543 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11544 * NULL, if you don't care.*/
11545 isds_error isds_request_new_testing_box(struct isds_ctx *context,
11546 struct isds_DbOwnerInfo *box, const struct isds_list *users,
11547 const char *former_names, const struct isds_approval *approval,
11548 char **refnumber) {
11549 isds_error err = IE_SUCCESS;
11550 #if HAVE_LIBCURL
11551 xmlNodePtr request = NULL;
11552 xmlDocPtr response = NULL;
11553 xmlXPathContextPtr xpath_ctx = NULL;
11554 xmlXPathObjectPtr result = NULL;
11555 #endif
11558 if (!context) return IE_INVALID_CONTEXT;
11559 zfree(context->long_message);
11560 if (!box) return IE_INVAL;
11562 #if HAVE_LIBCURL
11563 if (!box->email || box->email[0] == '\0') {
11564 isds_log_message(context, _("E-mail field is mandatory"));
11565 return IE_INVAL;
11568 /* Scratch box ID */
11569 zfree(box->dbID);
11571 /* Store configuration */
11572 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
11573 free(context->url);
11574 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
11575 if (!(context->url))
11576 return IE_NOMEM;
11578 /* Prepare CURL handle if not yet connected */
11579 if (!context->curl) {
11580 context->curl = curl_easy_init();
11581 if (!(context->curl))
11582 return IE_ERROR;
11585 /* Build CreateDataBox request */
11586 err = build_CreateDBInput_request(context,
11587 &request, BAD_CAST "CreateDataBox",
11588 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
11589 if (err) goto leave;
11591 /* Send it to server and process response */
11592 err = send_destroy_request_check_response(context,
11593 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
11594 &response, (xmlChar **) refnumber, NULL);
11595 if (err) goto leave;
11597 /* Extract box ID */
11598 xpath_ctx = xmlXPathNewContext(response);
11599 if (!xpath_ctx) {
11600 err = IE_ERROR;
11601 goto leave;
11603 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11604 err = IE_ERROR;
11605 goto leave;
11607 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
11609 leave:
11610 xmlXPathFreeObject(result);
11611 xmlXPathFreeContext(xpath_ctx);
11612 xmlFreeDoc(response);
11613 xmlFreeNode(request);
11615 if (!err) {
11616 isds_log(ILF_ISDS, ILL_DEBUG,
11617 _("CreateDataBox request processed by server successfully.\n"));
11619 #else /* not HAVE_LIBCURL */
11620 err = IE_NOTSUP;
11621 #endif
11623 return err;
11627 /* Submit CMS signed message to ISDS to verify its originality. This is
11628 * stronger form of isds_verify_message_hash() because ISDS does more checks
11629 * than simple one (potentialy old weak) hash comparison.
11630 * @context is session context
11631 * @message is memory with raw CMS signed message bit stream
11632 * @length is @message size in bytes
11633 * @return
11634 * IE_SUCCESS if message originates in ISDS
11635 * IE_NOTEQUAL if message is unknown to ISDS
11636 * other code for other errors */
11637 isds_error isds_authenticate_message(struct isds_ctx *context,
11638 const void *message, size_t length) {
11639 isds_error err = IE_SUCCESS;
11640 #if HAVE_LIBCURL
11641 xmlNsPtr isds_ns = NULL;
11642 xmlNodePtr request = NULL;
11643 xmlDocPtr response = NULL;
11644 xmlXPathContextPtr xpath_ctx = NULL;
11645 xmlXPathObjectPtr result = NULL;
11646 _Bool *authentic = NULL;
11647 #endif
11649 if (!context) return IE_INVALID_CONTEXT;
11650 zfree(context->long_message);
11651 if (!message || length == 0) return IE_INVAL;
11653 #if HAVE_LIBCURL
11654 /* Check if connection is established
11655 * TODO: This check should be done downstairs. */
11656 if (!context->curl) return IE_CONNECTION_CLOSED;
11659 /* Build AuthenticateMessage request */
11660 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11661 if (!request) {
11662 isds_log_message(context,
11663 _("Could not build AuthenticateMessage request"));
11664 return IE_ERROR;
11666 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11667 if(!isds_ns) {
11668 isds_log_message(context, _("Could not create ISDS name space"));
11669 xmlFreeNode(request);
11670 return IE_ERROR;
11672 xmlSetNs(request, isds_ns);
11674 /* Insert Base64 encoded message */
11675 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11676 message, length);
11677 if (err) goto leave;
11679 /* Send request to server and process response */
11680 err = send_destroy_request_check_response(context,
11681 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11682 &response, NULL, NULL);
11683 if (err) goto leave;
11686 /* ISDS has decided */
11687 xpath_ctx = xmlXPathNewContext(response);
11688 if (!xpath_ctx) {
11689 err = IE_ERROR;
11690 goto leave;
11692 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11693 err = IE_ERROR;
11694 goto leave;
11697 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11699 if (!authentic) {
11700 isds_log_message(context,
11701 _("Server did not return any response on "
11702 "AuthenticateMessage request"));
11703 err = IE_ISDS;
11704 goto leave;
11706 if (*authentic) {
11707 isds_log(ILF_ISDS, ILL_DEBUG,
11708 _("ISDS authenticated the message successfully\n"));
11709 } else {
11710 isds_log_message(context, _("ISDS does not know the message"));
11711 err = IE_NOTEQUAL;
11715 leave:
11716 free(authentic);
11717 xmlXPathFreeObject(result);
11718 xmlXPathFreeContext(xpath_ctx);
11720 xmlFreeDoc(response);
11721 xmlFreeNode(request);
11722 #else /* not HAVE_LIBCURL */
11723 err = IE_NOTSUP;
11724 #endif
11726 return err;
11730 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11731 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11732 * be re-signed.
11733 * @context is session context
11734 * @input_data is memory with raw CMS signed message or delivery info bit
11735 * stream to re-sign
11736 * @input_length is @input_data size in bytes
11737 * @output_data is pointer to auto-allocated memory where to store re-signed
11738 * input data blob. Caller must free it.
11739 * @output_data is pointer where to store @output_data size in bytes
11740 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11741 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11742 * @return
11743 * IE_SUCCESS if CMS blob has been re-signed successfully
11744 * other code for other errors */
11745 isds_error isds_resign_message(struct isds_ctx *context,
11746 const void *input_data, size_t input_length,
11747 void **output_data, size_t *output_length, struct tm **valid_to) {
11748 isds_error err = IE_SUCCESS;
11749 #if HAVE_LIBCURL
11750 xmlNsPtr isds_ns = NULL;
11751 xmlNodePtr request = NULL;
11752 xmlDocPtr response = NULL;
11753 xmlXPathContextPtr xpath_ctx = NULL;
11754 xmlXPathObjectPtr result = NULL;
11755 char *string = NULL;
11756 const xmlChar *codes[] = {
11757 BAD_CAST "2200",
11758 BAD_CAST "2201",
11759 BAD_CAST "2204",
11760 BAD_CAST "2207",
11761 NULL
11763 const char *meanings[] = {
11764 "Message is bad",
11765 "Message is not original",
11766 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11767 "Time stamp could not been generated in time"
11769 const isds_error errors[] = {
11770 IE_INVAL,
11771 IE_NOTUNIQ,
11772 IE_INVAL,
11773 IE_ISDS,
11775 struct code_map_isds_error map = {
11776 .codes = codes,
11777 .meanings = meanings,
11778 .errors = errors
11780 #endif
11782 if (NULL != output_data) *output_data = NULL;
11783 if (NULL != output_length) *output_length = 0;
11784 if (NULL != valid_to) *valid_to = NULL;
11786 if (NULL == context) return IE_INVALID_CONTEXT;
11787 zfree(context->long_message);
11788 if (NULL == input_data || 0 == input_length) {
11789 isds_log_message(context, _("Empty CMS blob on input"));
11790 return IE_INVAL;
11792 if (NULL == output_data || NULL == output_length) {
11793 isds_log_message(context,
11794 _("NULL pointer provided for output CMS blob"));
11795 return IE_INVAL;
11798 #if HAVE_LIBCURL
11799 /* Check if connection is established
11800 * TODO: This check should be done downstairs. */
11801 if (!context->curl) return IE_CONNECTION_CLOSED;
11804 /* Build Re-signISDSDocument request */
11805 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11806 if (!request) {
11807 isds_log_message(context,
11808 _("Could not build Re-signISDSDocument request"));
11809 return IE_ERROR;
11811 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11812 if(!isds_ns) {
11813 isds_log_message(context, _("Could not create ISDS name space"));
11814 xmlFreeNode(request);
11815 return IE_ERROR;
11817 xmlSetNs(request, isds_ns);
11819 /* Insert Base64 encoded CMS blob */
11820 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11821 input_data, input_length);
11822 if (err) goto leave;
11824 /* Send request to server and process response */
11825 err = send_destroy_request_check_response(context,
11826 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11827 &response, NULL, &map);
11828 if (err) goto leave;
11831 /* Extract re-signed data */
11832 xpath_ctx = xmlXPathNewContext(response);
11833 if (!xpath_ctx) {
11834 err = IE_ERROR;
11835 goto leave;
11837 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11838 err = IE_ERROR;
11839 goto leave;
11841 result = xmlXPathEvalExpression(
11842 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11843 if (!result) {
11844 err = IE_ERROR;
11845 goto leave;
11847 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11848 isds_log_message(context,
11849 _("Missing Re-signISDSDocumentResponse element"));
11850 err = IE_ISDS;
11851 goto leave;
11853 if (result->nodesetval->nodeNr > 1) {
11854 isds_log_message(context,
11855 _("Multiple Re-signISDSDocumentResponse element"));
11856 err = IE_ISDS;
11857 goto leave;
11859 xpath_ctx->node = result->nodesetval->nodeTab[0];
11860 xmlXPathFreeObject(result); result = NULL;
11862 EXTRACT_STRING("isds:dmResultDoc", string);
11863 /* Decode non-empty data */
11864 if (NULL != string && string[0] != '\0') {
11865 *output_length = _isds_b64decode(string, output_data);
11866 if (*output_length == (size_t) -1) {
11867 isds_log_message(context,
11868 _("Error while Base64-decoding re-signed data"));
11869 err = IE_ERROR;
11870 goto leave;
11872 } else {
11873 isds_log_message(context, _("Server did not send re-signed data"));
11874 err = IE_ISDS;
11875 goto leave;
11877 zfree(string);
11879 if (NULL != valid_to) {
11880 /* Get time stamp expiration date */
11881 EXTRACT_STRING("isds:dmValidTo", string);
11882 if (NULL != string) {
11883 *valid_to = calloc(1, sizeof(**valid_to));
11884 if (!*valid_to) {
11885 err = IE_NOMEM;
11886 goto leave;
11888 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11889 if (err) {
11890 if (err == IE_NOTSUP) {
11891 err = IE_ISDS;
11892 char *string_locale = _isds_utf82locale(string);
11893 isds_printf_message(context,
11894 _("Invalid dmValidTo value: %s"), string_locale);
11895 free(string_locale);
11897 goto leave;
11902 leave:
11903 free(string);
11905 xmlXPathFreeObject(result);
11906 xmlXPathFreeContext(xpath_ctx);
11908 xmlFreeDoc(response);
11909 xmlFreeNode(request);
11910 #else /* not HAVE_LIBCURL */
11911 err = IE_NOTSUP;
11912 #endif
11914 return err;
11917 #undef INSERT_ELEMENT
11918 #undef CHECK_FOR_STRING_LENGTH
11919 #undef INSERT_STRING_ATTRIBUTE
11920 #undef INSERT_ULONGINTNOPTR
11921 #undef INSERT_ULONGINT
11922 #undef INSERT_LONGINT
11923 #undef INSERT_BOOLEAN
11924 #undef INSERT_SCALAR_BOOLEAN
11925 #undef INSERT_STRING
11926 #undef INSERT_STRING_WITH_NS
11927 #undef EXTRACT_STRING_ATTRIBUTE
11928 #undef EXTRACT_ULONGINT
11929 #undef EXTRACT_LONGINT
11930 #undef EXTRACT_BOOLEAN
11931 #undef EXTRACT_STRING
11934 /* Compute hash of message from raw representation and store it into envelope.
11935 * Original hash structure will be destroyed in envelope.
11936 * @context is session context
11937 * @message is message carrying raw XML message blob
11938 * @algorithm is desired hash algorithm to use */
11939 isds_error isds_compute_message_hash(struct isds_ctx *context,
11940 struct isds_message *message, const isds_hash_algorithm algorithm) {
11941 isds_error err = IE_SUCCESS;
11942 const char *nsuri;
11943 void *xml_stream = NULL;
11944 size_t xml_stream_length;
11945 size_t phys_start, phys_end;
11946 char *phys_path = NULL;
11947 struct isds_hash *new_hash = NULL;
11950 if (!context) return IE_INVALID_CONTEXT;
11951 zfree(context->long_message);
11952 if (!message) return IE_INVAL;
11954 if (!message->raw) {
11955 isds_log_message(context,
11956 _("Message does not carry raw representation"));
11957 return IE_INVAL;
11960 switch (message->raw_type) {
11961 case RAWTYPE_INCOMING_MESSAGE:
11962 nsuri = ISDS_NS;
11963 xml_stream = message->raw;
11964 xml_stream_length = message->raw_length;
11965 break;
11967 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11968 nsuri = SISDS_INCOMING_NS;
11969 xml_stream = message->raw;
11970 xml_stream_length = message->raw_length;
11971 break;
11973 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11974 nsuri = SISDS_INCOMING_NS;
11975 err = _isds_extract_cms_data(context,
11976 message->raw, message->raw_length,
11977 &xml_stream, &xml_stream_length);
11978 if (err) goto leave;
11979 break;
11981 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11982 nsuri = SISDS_OUTGOING_NS;
11983 xml_stream = message->raw;
11984 xml_stream_length = message->raw_length;
11985 break;
11987 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11988 nsuri = SISDS_OUTGOING_NS;
11989 err = _isds_extract_cms_data(context,
11990 message->raw, message->raw_length,
11991 &xml_stream, &xml_stream_length);
11992 if (err) goto leave;
11993 break;
11995 default:
11996 isds_log_message(context, _("Bad raw representation type"));
11997 return IE_INVAL;
11998 break;
12002 /* XXX: Hash is computed from original string representing isds:dmDm
12003 * subtree. That means no encoding, white space, xmlns attributes changes.
12004 * In other words, input for hash can be invalid XML stream. */
12005 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
12006 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
12007 PHYSXML_ELEMENT_SEPARATOR,
12008 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
12009 PHYSXML_ELEMENT_SEPARATOR
12010 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
12011 err = IE_NOMEM;
12012 goto leave;
12014 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
12015 phys_path, &phys_start, &phys_end);
12016 zfree(phys_path);
12017 if (err) {
12018 isds_log_message(context,
12019 _("Substring with isds:dmDM element could not be located "
12020 "in raw message"));
12021 goto leave;
12025 /* Compute hash */
12026 new_hash = calloc(1, sizeof(*new_hash));
12027 if (!new_hash) {
12028 err = IE_NOMEM;
12029 goto leave;
12031 new_hash->algorithm = algorithm;
12032 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
12033 new_hash);
12034 if (err) {
12035 isds_log_message(context, _("Could not compute message hash"));
12036 goto leave;
12039 /* Save computed hash */
12040 if (!message->envelope) {
12041 message->envelope = calloc(1, sizeof(*message->envelope));
12042 if (!message->envelope) {
12043 err = IE_NOMEM;
12044 goto leave;
12047 isds_hash_free(&message->envelope->hash);
12048 message->envelope->hash = new_hash;
12050 leave:
12051 if (err) {
12052 isds_hash_free(&new_hash);
12055 free(phys_path);
12056 if (xml_stream != message->raw) free(xml_stream);
12057 return err;
12061 /* Compare two hashes.
12062 * @h1 is first hash
12063 * @h2 is another hash
12064 * @return
12065 * IE_SUCCESS if hashes equal
12066 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12067 * IE_ENUM if not comparable, but both structures defined
12068 * IE_INVAL if some of the structures are undefined (NULL)
12069 * IE_ERROR if internal error occurs */
12070 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
12071 if (h1 == NULL || h2 == NULL) return IE_INVAL;
12072 if (h1->algorithm != h2->algorithm) return IE_ENUM;
12073 if (h1->length != h2->length) return IE_ERROR;
12074 if (h1->length > 0 && !h1->value) return IE_ERROR;
12075 if (h2->length > 0 && !h2->value) return IE_ERROR;
12077 for (size_t i = 0; i < h1->length; i++) {
12078 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12079 return IE_NOTEQUAL;
12081 return IE_SUCCESS;
12085 /* Check message has gone through ISDS by comparing message hash stored in
12086 * ISDS and locally computed hash. You must provide message with valid raw
12087 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12088 * This is convenient wrapper for isds_download_message_hash(),
12089 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12090 * @context is session context
12091 * @message is message with valid raw and envelope member; envelope->hash
12092 * member will be changed during function run. Use envelope on heap only.
12093 * @return
12094 * IE_SUCCESS if message originates in ISDS
12095 * IE_NOTEQUAL if message is unknown to ISDS
12096 * other code for other errors */
12097 isds_error isds_verify_message_hash(struct isds_ctx *context,
12098 struct isds_message *message) {
12099 isds_error err = IE_SUCCESS;
12100 struct isds_hash *downloaded_hash = NULL;
12102 if (!context) return IE_INVALID_CONTEXT;
12103 zfree(context->long_message);
12104 if (!message) return IE_INVAL;
12106 if (!message->envelope) {
12107 isds_log_message(context,
12108 _("Given message structure is missing envelope"));
12109 return IE_INVAL;
12111 if (!message->raw) {
12112 isds_log_message(context,
12113 _("Given message structure is missing raw representation"));
12114 return IE_INVAL;
12117 err = isds_download_message_hash(context, message->envelope->dmID,
12118 &downloaded_hash);
12119 if (err) goto leave;
12121 err = isds_compute_message_hash(context, message,
12122 downloaded_hash->algorithm);
12123 if (err) goto leave;
12125 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12127 leave:
12128 isds_hash_free(&downloaded_hash);
12129 return err;
12133 /* Search for document by document ID in list of documents. IDs are compared
12134 * as UTF-8 string.
12135 * @documents is list of isds_documents
12136 * @id is document identifier
12137 * @return first matching document or NULL. */
12138 const struct isds_document *isds_find_document_by_id(
12139 const struct isds_list *documents, const char *id) {
12140 const struct isds_list *item;
12141 const struct isds_document *document;
12143 for (item = documents; item; item = item->next) {
12144 document = (struct isds_document *) item->data;
12145 if (!document) continue;
12147 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12148 return document;
12151 return NULL;
12155 /* Normalize @mime_type to be proper MIME type.
12156 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12157 * guess regular MIME type (e.g. "application/pdf").
12158 * @mime_type is UTF-8 encoded MIME type to fix
12159 * @return original @mime_type if no better interpretation exists, or
12160 * constant static UTF-8 encoded string with proper MIME type. */
12161 const char *isds_normalize_mime_type(const char *mime_type) {
12162 if (!mime_type) return NULL;
12164 for (size_t offset = 0;
12165 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12166 offset += 2) {
12167 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12168 extension_map_mime[offset]))
12169 return (const char *) extension_map_mime[offset + 1];
12172 return mime_type;
12176 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12177 struct isds_message **message);
12178 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12179 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12180 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12181 struct isds_address **address);
12183 int isds_message_free(struct isds_message **message);
12184 int isds_address_free(struct isds_address **address);
12188 /* Makes known all relevant namespaces to given XPath context
12189 * @xpath_ctx is XPath context
12190 * @message_ns selects proper message name space. Unsigned and signed
12191 * messages and delivery info's differ in prefix and URI. */
12192 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12193 const message_ns_type message_ns) {
12194 const xmlChar *message_namespace = NULL;
12196 if (!xpath_ctx) return IE_ERROR;
12198 switch(message_ns) {
12199 case MESSAGE_NS_1:
12200 message_namespace = BAD_CAST ISDS1_NS; break;
12201 case MESSAGE_NS_UNSIGNED:
12202 message_namespace = BAD_CAST ISDS_NS; break;
12203 case MESSAGE_NS_SIGNED_INCOMING:
12204 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12205 case MESSAGE_NS_SIGNED_OUTGOING:
12206 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12207 case MESSAGE_NS_SIGNED_DELIVERY:
12208 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12209 default:
12210 return IE_ENUM;
12213 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12214 return IE_ERROR;
12215 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12216 return IE_ERROR;
12217 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12218 return IE_ERROR;
12219 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12220 return IE_ERROR;
12221 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12222 return IE_ERROR;
12223 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12224 return IE_ERROR;
12225 return IE_SUCCESS;