Do not use dolar in formatting strings
[libisds.git] / src / isds.c
blob3e6a732e1a8324cc7fe9aeb1c37421c16b9e8993
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 /* Deallocate struct isds_box_state_period recursively and NULL it */
465 void isds_box_state_period_free(struct isds_box_state_period **period) {
466 if (NULL == period || NULL == *period) return;
467 zfree(*period);
471 /* *DUP_OR_ERROR macros needs error label */
472 #define STRDUP_OR_ERROR(new, template) { \
473 if (!template) { \
474 (new) = NULL; \
475 } else { \
476 (new) = strdup(template); \
477 if (!new) goto error; \
481 #define FLATDUP_OR_ERROR(new, template) { \
482 if (!template) { \
483 (new) = NULL; \
484 } else { \
485 (new) = malloc(sizeof(*(new))); \
486 if (!new) goto error; \
487 memcpy((new), (template), sizeof(*(template))); \
491 /* Copy structure isds_pki_credentials recursively. */
492 struct isds_pki_credentials *isds_pki_credentials_duplicate(
493 const struct isds_pki_credentials *template) {
494 struct isds_pki_credentials *new = NULL;
496 if(!template) return NULL;
498 new = calloc(1, sizeof(*new));
499 if (!new) return NULL;
501 STRDUP_OR_ERROR(new->engine, template->engine);
502 new->certificate_format = template->certificate_format;
503 STRDUP_OR_ERROR(new->certificate, template->certificate);
504 new->key_format = template->key_format;
505 STRDUP_OR_ERROR(new->key, template->key);
506 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
508 return new;
510 error:
511 isds_pki_credentials_free(&new);
512 return NULL;
516 /* Copy structure isds_PersonName recursively */
517 struct isds_PersonName *isds_PersonName_duplicate(
518 const struct isds_PersonName *src) {
519 struct isds_PersonName *new = NULL;
521 if (!src) return NULL;
523 new = calloc(1, sizeof(*new));
524 if (!new) return NULL;
526 STRDUP_OR_ERROR(new->pnFirstName, src->pnFirstName);
527 STRDUP_OR_ERROR(new->pnMiddleName, src->pnMiddleName);
528 STRDUP_OR_ERROR(new->pnLastName, src->pnLastName);
529 STRDUP_OR_ERROR(new->pnLastNameAtBirth, src->pnLastNameAtBirth);
531 return new;
533 error:
534 isds_PersonName_free(&new);
535 return NULL;
539 /* Copy structure isds_BirthInfo recursively */
540 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
541 const struct isds_BirthInfo *template) {
542 struct isds_BirthInfo *new = NULL;
544 if (!template) return NULL;
546 new = calloc(1, sizeof(*new));
547 if (!new) return NULL;
549 FLATDUP_OR_ERROR(new->biDate, template->biDate);
550 STRDUP_OR_ERROR(new->biCity, template->biCity);
551 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
552 STRDUP_OR_ERROR(new->biState, template->biState);
554 return new;
556 error:
557 isds_BirthInfo_free(&new);
558 return NULL;
562 /* Copy structure isds_Address recursively */
563 struct isds_Address *isds_Address_duplicate(
564 const struct isds_Address *src) {
565 struct isds_Address *new = NULL;
567 if (!src) return NULL;
569 new = calloc(1, sizeof(*new));
570 if (!new) return NULL;
572 FLATDUP_OR_ERROR(new->adCode, src->adCode);
573 STRDUP_OR_ERROR(new->adCity, src->adCity);
574 STRDUP_OR_ERROR(new->adDistrict, src->adDistrict);
575 STRDUP_OR_ERROR(new->adStreet, src->adStreet);
576 STRDUP_OR_ERROR(new->adNumberInStreet, src->adNumberInStreet);
577 STRDUP_OR_ERROR(new->adNumberInMunicipality,
578 src->adNumberInMunicipality);
579 STRDUP_OR_ERROR(new->adZipCode, src->adZipCode);
580 STRDUP_OR_ERROR(new->adState, src->adState);
582 return new;
584 error:
585 isds_Address_free(&new);
586 return NULL;
590 /* Copy structure isds_DbOwnerInfo recursively */
591 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
592 const struct isds_DbOwnerInfo *src) {
593 struct isds_DbOwnerInfo *new = NULL;
594 if (!src) return NULL;
596 new = calloc(1, sizeof(*new));
597 if (!new) return NULL;
599 STRDUP_OR_ERROR(new->dbID, src->dbID);
600 FLATDUP_OR_ERROR(new->dbType, src->dbType);
601 STRDUP_OR_ERROR(new->ic, src->ic);
603 if (src->personName) {
604 if (!(new->personName =
605 isds_PersonName_duplicate(src->personName)))
606 goto error;
609 STRDUP_OR_ERROR(new->firmName, src->firmName);
611 if (src->birthInfo) {
612 if (!(new->birthInfo =
613 isds_BirthInfo_duplicate(src->birthInfo)))
614 goto error;
617 if (src->address) {
618 if (!(new->address = isds_Address_duplicate(src->address)))
619 goto error;
622 STRDUP_OR_ERROR(new->nationality, src->nationality);
623 STRDUP_OR_ERROR(new->email, src->email);
624 STRDUP_OR_ERROR(new->telNumber, src->telNumber);
625 STRDUP_OR_ERROR(new->identifier, src->identifier);
626 FLATDUP_OR_ERROR(new->aifoIsds, src->aifoIsds);
627 STRDUP_OR_ERROR(new->registryCode, src->registryCode);
628 FLATDUP_OR_ERROR(new->dbState, src->dbState);
629 FLATDUP_OR_ERROR(new->dbEffectiveOVM, src->dbEffectiveOVM);
630 FLATDUP_OR_ERROR(new->dbOpenAddressing, src->dbOpenAddressing);
632 return new;
634 error:
635 isds_DbOwnerInfo_free(&new);
636 return NULL;
640 /* Copy structure isds_DbUserInfo recursively */
641 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
642 const struct isds_DbUserInfo *src) {
643 struct isds_DbUserInfo *new = NULL;
644 if (!src) return NULL;
646 new = calloc(1, sizeof(*new));
647 if (!new) return NULL;
649 STRDUP_OR_ERROR(new->userID, src->userID);
650 FLATDUP_OR_ERROR(new->userType, src->userType);
651 FLATDUP_OR_ERROR(new->userPrivils, src->userPrivils);
653 if (src->personName) {
654 if (!(new->personName =
655 isds_PersonName_duplicate(src->personName)))
656 goto error;
659 if (src->address) {
660 if (!(new->address = isds_Address_duplicate(src->address)))
661 goto error;
664 FLATDUP_OR_ERROR(new->biDate, src->biDate);
665 STRDUP_OR_ERROR(new->ic, src->ic);
666 STRDUP_OR_ERROR(new->firmName, src->firmName);
667 STRDUP_OR_ERROR(new->caStreet, src->caStreet);
668 STRDUP_OR_ERROR(new->caCity, src->caCity);
669 STRDUP_OR_ERROR(new->caZipCode, src->caZipCode);
670 STRDUP_OR_ERROR(new->caState, src->caState);
671 STRDUP_OR_ERROR(new->aifo_ticket, src->aifo_ticket);
673 return new;
675 error:
676 isds_DbUserInfo_free(&new);
677 return NULL;
681 /* Copy structure isds_box_state_period recursively */
682 struct isds_box_state_period *isds_box_state_period_duplicate(
683 const struct isds_box_state_period *src) {
684 struct isds_box_state_period *new = NULL;
685 if (!src) return NULL;
687 new = calloc(1, sizeof(*new));
688 if (!new) return NULL;
690 memcpy(&new->from, &src->from, sizeof(src->from));
691 memcpy(&new->to, &src->to, sizeof(src->to));
692 new->dbState = src->dbState;
694 return new;
697 #undef FLATDUP_OR_ERROR
698 #undef STRDUP_OR_ERROR
701 /* Logs libxml2 errors. Should be registered to libxml2 library.
702 * @ctx is unused currently
703 * @msg is printf-like formated message from libxml2 (UTF-8?)
704 * @... are variadic arguments for @msg */
705 static void log_xml(void *ctx, const char *msg, ...) {
706 va_list ap;
707 char *text = NULL;
709 /* Silent warning for unused function argument.
710 * The prototype is an API of libxml2's xmlSetGenericErrorFunc(). */
711 (void)ctx;
713 if (!msg) return;
715 va_start(ap, msg);
716 isds_vasprintf(&text, msg, ap);
717 va_end(ap);
719 if (text)
720 isds_log(ILF_XML, ILL_ERR, "%s", text);
721 free(text);
725 /* Initialize ISDS library.
726 * Global function, must be called before other functions.
727 * If it fails you can not use ISDS library and must call isds_cleanup() to
728 * free partially initialized global variables. */
729 isds_error isds_init(void) {
730 /* NULL global variables */
731 log_facilities = ILF_ALL;
732 log_level = ILL_WARNING;
733 log_callback = NULL;
734 log_callback_data = NULL;
736 #if ENABLE_NLS
737 /* Initialize gettext */
738 bindtextdomain(PACKAGE, LOCALEDIR);
739 #endif
741 #if HAVE_LIBCURL
742 /* Initialize CURL */
743 if (curl_global_init(CURL_GLOBAL_ALL)) {
744 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
745 return IE_ERROR;
747 #endif /* HAVE_LIBCURL */
749 /* Initialise cryptographic back-ends. */
750 if (IE_SUCCESS != _isds_init_crypto()) {
751 isds_log(ILF_ISDS, ILL_CRIT,
752 _("Initialization of cryptographic back-end failed\n"));
753 return IE_ERROR;
756 /* This can _exit() current program. Find not so assertive check. */
757 LIBXML_TEST_VERSION;
758 xmlSetGenericErrorFunc(NULL, log_xml);
760 /* Check expat */
761 if (_isds_init_expat(&version_expat)) {
762 isds_log(ILF_ISDS, ILL_CRIT,
763 _("expat library initialization failed\n"));
764 return IE_ERROR;
767 /* Allocate global variables */
770 return IE_SUCCESS;
774 /* Deinitialize ISDS library.
775 * Global function, must be called as last library function. */
776 isds_error isds_cleanup(void) {
777 /* XML */
778 xmlCleanupParser();
780 #if HAVE_LIBCURL
781 /* Curl */
782 curl_global_cleanup();
783 #endif
785 return IE_SUCCESS;
789 /* Return version string of this library. Version of dependencies can be
790 * embedded. Do no try to parse it. You must free it. */
791 char *isds_version(void) {
792 char *buffer = NULL;
794 isds_asprintf(&buffer,
795 #if HAVE_LIBCURL
796 # ifndef USE_OPENSSL_BACKEND
797 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
798 # else
799 _("%s (%s, %s, %s, libxml2 %s)"),
800 # endif
801 #else
802 # ifndef USE_OPENSSL_BACKEND
803 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
804 # else
805 _("%s (%s, %s, libxml2 %s)"),
806 # endif
807 #endif
808 PACKAGE_VERSION,
809 #if HAVE_LIBCURL
810 curl_version(),
811 #endif
812 #ifndef USE_OPENSSL_BACKEND
813 version_gpgme, version_gcrypt,
814 #else
815 version_openssl,
816 #endif
817 version_expat, xmlParserVersion);
818 return buffer;
822 /* Return text description of ISDS error */
823 const char *isds_strerror(const isds_error error) {
824 switch (error) {
825 case IE_SUCCESS:
826 return(_("Success")); break;
827 case IE_ERROR:
828 return(_("Unspecified error")); break;
829 case IE_NOTSUP:
830 return(_("Not supported")); break;
831 case IE_INVAL:
832 return(_("Invalid value")); break;
833 case IE_INVALID_CONTEXT:
834 return(_("Invalid context")); break;
835 case IE_NOT_LOGGED_IN:
836 return(_("Not logged in")); break;
837 case IE_CONNECTION_CLOSED:
838 return(_("Connection closed")); break;
839 case IE_TIMED_OUT:
840 return(_("Timed out")); break;
841 case IE_NOEXIST:
842 return(_("Not exist")); break;
843 case IE_NOMEM:
844 return(_("Out of memory")); break;
845 case IE_NETWORK:
846 return(_("Network problem")); break;
847 case IE_HTTP:
848 return(_("HTTP problem")); break;
849 case IE_SOAP:
850 return(_("SOAP problem")); break;
851 case IE_XML:
852 return(_("XML problem")); break;
853 case IE_ISDS:
854 return(_("ISDS server problem")); break;
855 case IE_ENUM:
856 return(_("Invalid enum value")); break;
857 case IE_DATE:
858 return(_("Invalid date value")); break;
859 case IE_2BIG:
860 return(_("Too big")); break;
861 case IE_2SMALL:
862 return(_("Too small")); break;
863 case IE_NOTUNIQ:
864 return(_("Value not unique")); break;
865 case IE_NOTEQUAL:
866 return(_("Values not equal")); break;
867 case IE_PARTIAL_SUCCESS:
868 return(_("Some suboperations failed")); break;
869 case IE_ABORTED:
870 return(_("Operation aborted")); break;
871 case IE_SECURITY:
872 return(_("Security problem")); break;
873 default:
874 return(_("Unknown error"));
879 /* Create ISDS context.
880 * Each context can be used for different sessions to (possibly) different
881 * ISDS server with different credentials. */
882 struct isds_ctx *isds_ctx_create(void) {
883 struct isds_ctx *context;
884 context = malloc(sizeof(*context));
885 if (context) memset(context, 0, sizeof(*context));
886 return context;
889 #if HAVE_LIBCURL
890 /* Close possibly opened connection to Czech POINT document deposit without
891 * resetting long_message buffer.
892 * XXX: Do not use czp_close_connection() if you do not want to destroy log
893 * message.
894 * @context is Czech POINT session context. */
895 static isds_error czp_do_close_connection(struct isds_ctx *context) {
896 if (!context) return IE_INVALID_CONTEXT;
897 _isds_close_connection(context);
898 return IE_SUCCESS;
902 /* Discard credentials.
903 * @context is ISDS context
904 * @discard_saved_username is true for removing saved username, false for
905 * keeping it.
906 * Only that. It does not cause log out, connection close or similar. */
907 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context,
908 _Bool discard_saved_username) {
909 if(!context) return IE_INVALID_CONTEXT;
911 if (context->username) {
912 memset(context->username, 0, strlen(context->username));
913 zfree(context->username);
915 if (context->password) {
916 memset(context->password, 0, strlen(context->password));
917 zfree(context->password);
919 isds_pki_credentials_free(&context->pki_credentials);
920 if (discard_saved_username && context->saved_username) {
921 memset(context->saved_username, 0, strlen(context->saved_username));
922 zfree(context->saved_username);
925 return IE_SUCCESS;
927 #endif /* HAVE_LIBCURL */
930 /* Destroy ISDS context and free memory.
931 * @context will be NULLed on success. */
932 isds_error isds_ctx_free(struct isds_ctx **context) {
933 if (!context || !*context) {
934 return IE_INVALID_CONTEXT;
937 #if HAVE_LIBCURL
938 /* Discard credentials and close connection */
939 switch ((*context)->type) {
940 case CTX_TYPE_NONE: break;
941 case CTX_TYPE_ISDS: isds_logout(*context); break;
942 case CTX_TYPE_CZP:
943 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
944 czp_do_close_connection(*context); break;
947 /* For sure */
948 _isds_discard_credentials(*context, 1);
950 /* Free other structures */
951 free((*context)->url);
952 free((*context)->tls_verify_server);
953 free((*context)->tls_ca_file);
954 free((*context)->tls_ca_dir);
955 free((*context)->tls_crl_file);
956 #endif /* HAVE_LIBCURL */
957 free((*context)->long_message);
959 free(*context);
960 *context = NULL;
961 return IE_SUCCESS;
965 /* Return long message text produced by library function, e.g. detailed error
966 * message. Returned pointer is only valid until new library function is
967 * called for the same context. Could be NULL, especially if NULL context is
968 * supplied. Return string is locale encoded. */
969 char *isds_long_message(const struct isds_ctx *context) {
970 if (!context) return NULL;
971 return context->long_message;
975 /* Stores message into context' long_message buffer.
976 * Application can pick the message up using isds_long_message().
977 * NULL @message truncates the buffer but does not deallocate it.
978 * @message is coded in locale encoding */
979 _hidden isds_error isds_log_message(struct isds_ctx *context,
980 const char *message) {
981 char *buffer;
982 size_t length;
984 if (!context) return IE_INVALID_CONTEXT;
986 /* FIXME: Check for integer overflow */
987 length = 1 + ((message) ? strlen(message) : 0);
988 buffer = realloc(context->long_message, length);
989 if (!buffer) return IE_NOMEM;
991 if (message)
992 strcpy(buffer, message);
993 else
994 *buffer = '\0';
996 context->long_message = buffer;
997 return IE_SUCCESS;
1001 /* Appends message into context' long_message buffer.
1002 * Application can pick the message up using isds_long_message().
1003 * NULL message has void effect. */
1004 _hidden isds_error isds_append_message(struct isds_ctx *context,
1005 const char *message) {
1006 char *buffer;
1007 size_t old_length, length;
1009 if (!context) return IE_INVALID_CONTEXT;
1010 if (!message) return IE_SUCCESS;
1011 if (!context->long_message)
1012 return isds_log_message(context, message);
1014 old_length = strlen(context->long_message);
1015 /* FIXME: Check for integer overflow */
1016 length = 1 + old_length + strlen(message);
1017 buffer = realloc(context->long_message, length);
1018 if (!buffer) return IE_NOMEM;
1020 strcpy(buffer + old_length, message);
1022 context->long_message = buffer;
1023 return IE_SUCCESS;
1027 /* Stores formatted message into context' long_message buffer.
1028 * Application can pick the message up using isds_long_message(). */
1029 _hidden isds_error isds_printf_message(struct isds_ctx *context,
1030 const char *format, ...) {
1031 va_list ap;
1032 int length;
1034 if (!context) return IE_INVALID_CONTEXT;
1035 va_start(ap, format);
1036 length = isds_vasprintf(&(context->long_message), format, ap);
1037 va_end(ap);
1039 return (length < 0) ? IE_ERROR: IE_SUCCESS;
1043 /* Set logging up.
1044 * @facilities is bit mask of isds_log_facility values,
1045 * @level is verbosity level. */
1046 void isds_set_logging(const unsigned int facilities,
1047 const isds_log_level level) {
1048 log_facilities = facilities;
1049 log_level = level;
1053 /* Register callback function libisds calls when new global log message is
1054 * produced by library. Library logs to stderr by default.
1055 * @callback is function provided by application libisds will call. See type
1056 * definition for @callback argument explanation. Pass NULL to revert logging to
1057 * default behaviour.
1058 * @data is application specific data @callback gets as last argument */
1059 void isds_set_log_callback(isds_log_callback callback, void *data) {
1060 log_callback = callback;
1061 log_callback_data = data;
1065 /* Log @message in class @facility with log @level into global log. @message
1066 * is printf(3) formatting string, variadic arguments may be necessary.
1067 * For debugging purposes. */
1068 _hidden isds_error isds_log(const isds_log_facility facility,
1069 const isds_log_level level, const char *message, ...) {
1070 va_list ap;
1071 char *buffer = NULL;
1072 int length;
1074 if (level > log_level) return IE_SUCCESS;
1075 if (!(log_facilities & facility)) return IE_SUCCESS;
1076 if (!message) return IE_INVAL;
1078 if (log_callback) {
1079 /* Pass message to application supplied callback function */
1080 va_start(ap, message);
1081 length = isds_vasprintf(&buffer, message, ap);
1082 va_end(ap);
1084 if (length == -1) {
1085 return IE_ERROR;
1087 if (length > 0) {
1088 log_callback(facility, level, buffer, length, log_callback_data);
1090 free(buffer);
1091 } else {
1092 /* Default: Log it to stderr */
1093 va_start(ap, message);
1094 vfprintf(stderr, message, ap);
1095 va_end(ap);
1096 /* Line buffered printf is default.
1097 * fflush(stderr);*/
1100 return IE_SUCCESS;
1104 /* Set timeout in milliseconds for each network job like connecting to server
1105 * or sending message. Use 0 to disable timeout limits. */
1106 isds_error isds_set_timeout(struct isds_ctx *context,
1107 const unsigned int timeout) {
1108 if (!context) return IE_INVALID_CONTEXT;
1109 zfree(context->long_message);
1111 #if HAVE_LIBCURL
1112 context->timeout = timeout;
1114 if (context->curl) {
1115 CURLcode curl_err;
1117 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
1118 if (!curl_err)
1119 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1120 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
1121 context->timeout);
1122 #else
1123 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
1124 context->timeout / 1000);
1125 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1126 if (curl_err) return IE_ERROR;
1129 return IE_SUCCESS;
1130 #else /* not HAVE_LIBCURL */
1131 return IE_NOTSUP;
1132 #endif
1136 /* Register callback function libisds calls periodically during HTTP data
1137 * transfer.
1138 * @context is session context
1139 * @callback is function provided by application libisds will call. See type
1140 * definition for @callback argument explanation.
1141 * @data is application specific data @callback gets as last argument */
1142 isds_error isds_set_progress_callback(struct isds_ctx *context,
1143 isds_progress_callback callback, void *data) {
1144 if (!context) return IE_INVALID_CONTEXT;
1145 zfree(context->long_message);
1147 #if HAVE_LIBCURL
1148 context->progress_callback = callback;
1149 context->progress_callback_data = data;
1151 return IE_SUCCESS;
1152 #else /* not HAVE_LIBCURL */
1153 return IE_NOTSUP;
1154 #endif
1158 /* Change context settings.
1159 * @context is context which setting will be applied to
1160 * @option is name of option. It determines the type of last argument. See
1161 * isds_option definition for more info.
1162 * @... is value of new setting. Type is determined by @option
1163 * */
1164 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1165 ...) {
1166 isds_error err = IE_SUCCESS;
1167 va_list ap;
1168 #if HAVE_LIBCURL
1169 char *pointer, *string;
1170 #endif
1172 if (!context) return IE_INVALID_CONTEXT;
1173 zfree(context->long_message);
1175 va_start(ap, option);
1177 #define REPLACE_VA_BOOLEAN(destination) { \
1178 if (!(destination)) { \
1179 (destination) = malloc(sizeof(*(destination))); \
1180 if (!(destination)) { \
1181 err = IE_NOMEM; goto leave; \
1184 *(destination) = (_Bool) !!va_arg(ap, int); \
1187 #define REPLACE_VA_STRING(destination) { \
1188 string = va_arg(ap, char *); \
1189 if (string) { \
1190 pointer = realloc((destination), 1 + strlen(string)); \
1191 if (!pointer) { err = IE_NOMEM; goto leave; } \
1192 strcpy(pointer, string); \
1193 (destination) = pointer; \
1194 } else { \
1195 free(destination); \
1196 (destination) = NULL; \
1200 switch (option) {
1201 case IOPT_TLS_VERIFY_SERVER:
1202 #if HAVE_LIBCURL
1203 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1204 #else
1205 err = IE_NOTSUP; goto leave;
1206 #endif
1207 break;
1208 case IOPT_TLS_CA_FILE:
1209 #if HAVE_LIBCURL
1210 REPLACE_VA_STRING(context->tls_ca_file);
1211 #else
1212 err = IE_NOTSUP; goto leave;
1213 #endif
1214 break;
1215 case IOPT_TLS_CA_DIRECTORY:
1216 #if HAVE_LIBCURL
1217 REPLACE_VA_STRING(context->tls_ca_dir);
1218 #else
1219 err = IE_NOTSUP; goto leave;
1220 #endif
1221 break;
1222 case IOPT_TLS_CRL_FILE:
1223 #if HAVE_LIBCURL
1224 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1225 REPLACE_VA_STRING(context->tls_crl_file);
1226 #else
1227 isds_log_message(context,
1228 _("Curl library does not support CRL definition"));
1229 err = IE_NOTSUP;
1230 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1231 #else
1232 err = IE_NOTSUP; goto leave;
1233 #endif /* not HAVE_LIBCURL */
1234 break;
1235 case IOPT_NORMALIZE_MIME_TYPE:
1236 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1237 break;
1239 default:
1240 err = IE_ENUM; goto leave;
1243 #undef REPLACE_VA_STRING
1244 #undef REPLACE_VA_BOOLEAN
1246 leave:
1247 va_end(ap);
1248 return err;
1252 #if HAVE_LIBCURL
1253 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1254 * Destination for NULL argument will not be touched.
1255 * Destination pointers must be freed before calling this function.
1256 * If @username is @context->saved_username, the saved_username will not be
1257 * replaced. The saved_username is clobbered only if context has set otp
1258 * member.
1259 * Return IE_SUCCESS on success. */
1260 static isds_error _isds_store_credentials(struct isds_ctx *context,
1261 const char *username, const char *password,
1262 const struct isds_pki_credentials *pki_credentials) {
1263 if (NULL == context) return IE_INVALID_CONTEXT;
1265 /* FIXME: mlock password
1266 * (I have a library) */
1268 if (username) {
1269 context->username = strdup(username);
1270 if (context->otp && context->saved_username != username)
1271 context->saved_username = strdup(username);
1273 if (password) {
1274 if (NULL == context->otp_credentials)
1275 context->password = strdup(password);
1276 else
1277 context->password = _isds_astrcat(password,
1278 context->otp_credentials->otp_code);
1280 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1282 if ((NULL != username && NULL == context->username) ||
1283 (NULL != password && NULL == context->password) ||
1284 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1285 (context->otp && NULL != context->username &&
1286 NULL == context->saved_username)) {
1287 return IE_NOMEM;
1290 return IE_SUCCESS;
1292 #endif
1295 /* Connect and log into ISDS server.
1296 * All required arguments will be copied, you do not have to keep them after
1297 * that.
1298 * ISDS supports six different authentication methods. Exact method is
1299 * selected on @username, @password, @pki_credentials, and @otp arguments:
1300 * - If @pki_credentials == NULL, @username and @password must be supplied
1301 * and then
1302 * - If @otp == NULL, simple authentication by username and password will
1303 * be proceeded.
1304 * - If @otp != NULL, authentication by username and password and OTP
1305 * will be used.
1306 * - If @pki_credentials != NULL, then
1307 * - If @username == NULL, only certificate will be used
1308 * - If @username != NULL, then
1309 * - If @password == NULL, then certificate will be used and
1310 * @username shifts meaning to box ID. This is used for hosted
1311 * services.
1312 * - Otherwise all three arguments will be used.
1313 * Please note, that different cases require different certificate type
1314 * (system qualified one or commercial non qualified one). This library
1315 * does not check such political issues. Please see ISDS Specification
1316 * for more details.
1317 * @url is base address of ISDS web service. Pass extern isds_locator
1318 * variable to use production ISDS instance without client certificate
1319 * authentication (or extern isds_cert_locator with client certificate
1320 * authentication or extern isds_otp_locators with OTP authentication).
1321 * Passing NULL has the same effect, autoselection between isds_locator,
1322 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1323 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1324 * isds_otp_testing_locator) variable to select testing instance.
1325 * @username is user name of ISDS user or box ID
1326 * @password is user's secret password
1327 * @pki_credentials defines public key cryptographic material to use in client
1328 * authentication.
1329 * @otp selects one-time password authentication method to use, defines OTP
1330 * code (if known) and returns fine grade resolution of OTP procedure.
1331 * @return:
1332 * IE_SUCCESS if authentication succeeds
1333 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1334 * requested, fine grade reason will be set into @otp->resolution. Error
1335 * message from server can be obtained by isds_long_message() call.
1336 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1337 * server has sent OTP code through side channel. Application is expected to
1338 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1339 * this call to complete second phase of TOTP authentication;
1340 * or other appropriate error. */
1341 isds_error isds_login(struct isds_ctx *context, const char *url,
1342 const char *username, const char *password,
1343 const struct isds_pki_credentials *pki_credentials,
1344 struct isds_otp *otp) {
1345 #if HAVE_LIBCURL
1346 isds_error err = IE_NOT_LOGGED_IN;
1347 isds_error soap_err;
1348 xmlNsPtr isds_ns = NULL;
1349 xmlNodePtr request = NULL;
1350 #endif /* HAVE_LIBCURL */
1352 if (!context) return IE_INVALID_CONTEXT;
1353 zfree(context->long_message);
1355 #if HAVE_LIBCURL
1356 /* Close connection if already logged in */
1357 if (context->curl) {
1358 _isds_close_connection(context);
1361 /* Store configuration */
1362 context->type = CTX_TYPE_ISDS;
1363 zfree(context->url);
1365 /* Mangle base URI according to requested authentication method */
1366 if (NULL == pki_credentials) {
1367 isds_log(ILF_SEC, ILL_INFO,
1368 _("Selected authentication method: no certificate, "
1369 "username and password\n"));
1370 if (!username || !password) {
1371 isds_log_message(context,
1372 _("Both username and password must be supplied"));
1373 return IE_INVAL;
1375 context->otp_credentials = otp;
1376 context->otp = (NULL != context->otp_credentials);
1378 if (!context->otp) {
1379 /* Default locator is official system (without certificate or
1380 * OTP) */
1381 context->url = strdup((NULL != url) ? url : isds_locator);
1382 } else {
1383 const char *authenticator_uri = NULL;
1384 if (!url) url = isds_otp_locator;
1385 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1386 switch (context->otp_credentials->method) {
1387 case OTP_HMAC:
1388 isds_log(ILF_SEC, ILL_INFO,
1389 _("Selected authentication method: "
1390 "HMAC-based one-time password\n"));
1391 authenticator_uri =
1392 "%sas/processLogin?type=hotp&uri=%sapps/";
1393 break;
1394 case OTP_TIME:
1395 isds_log(ILF_SEC, ILL_INFO,
1396 _("Selected authentication method: "
1397 "Time-based one-time password\n"));
1398 if (context->otp_credentials->otp_code == NULL) {
1399 isds_log(ILF_SEC, ILL_INFO,
1400 _("OTP code has not been provided by "
1401 "application, requesting server for "
1402 "new one.\n"));
1403 authenticator_uri =
1404 "%sas/processLogin?type=totp&sendSms=true&"
1405 "uri=%sapps/";
1406 } else {
1407 isds_log(ILF_SEC, ILL_INFO,
1408 _("OTP code has been provided by "
1409 "application, not requesting server "
1410 "for new one.\n"));
1411 authenticator_uri =
1412 "%sas/processLogin?type=totp&"
1413 "uri=%sapps/";
1415 break;
1416 default:
1417 isds_log_message(context,
1418 _("Unknown one-time password authentication "
1419 "method requested by application"));
1420 return IE_ENUM;
1422 if (-1 == isds_asprintf(&context->url, authenticator_uri, url, url))
1423 return IE_NOMEM;
1425 } else {
1426 /* Default locator is official system (with client certificate) */
1427 context->otp = 0;
1428 context->otp_credentials = NULL;
1429 if (!url) url = isds_cert_locator;
1431 if (!username) {
1432 isds_log(ILF_SEC, ILL_INFO,
1433 _("Selected authentication method: system certificate, "
1434 "no username and no password\n"));
1435 password = NULL;
1436 context->url = _isds_astrcat(url, "cert/");
1437 } else {
1438 if (!password) {
1439 isds_log(ILF_SEC, ILL_INFO,
1440 _("Selected authentication method: system certificate, "
1441 "box ID and no password\n"));
1442 context->url = _isds_astrcat(url, "hspis/");
1443 } else {
1444 isds_log(ILF_SEC, ILL_INFO,
1445 _("Selected authentication method: commercial "
1446 "certificate, username and password\n"));
1447 context->url = _isds_astrcat(url, "certds/");
1451 if (!(context->url))
1452 return IE_NOMEM;
1454 /* Prepare CURL handle */
1455 context->curl = curl_easy_init();
1456 if (!(context->curl))
1457 return IE_ERROR;
1459 /* Build log-in request */
1460 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1461 if (!request) {
1462 isds_log_message(context, _("Could not build ISDS log-in request"));
1463 return IE_ERROR;
1465 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1466 if(!isds_ns) {
1467 isds_log_message(context, _("Could not create ISDS name space"));
1468 xmlFreeNode(request);
1469 return IE_ERROR;
1471 xmlSetNs(request, isds_ns);
1473 /* Store credentials */
1474 _isds_discard_credentials(context, 1);
1475 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1476 _isds_discard_credentials(context, 1);
1477 xmlFreeNode(request);
1478 return IE_NOMEM;
1481 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1482 username, url);
1484 /* XXX: ISDS documentation does not specify response body for
1485 * DummyOperation request. However real server sends back
1486 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1487 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1488 * SOAP body content, e.g. the dmStatus element. */
1490 /* Send log-in request */
1491 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1493 if (context->otp) {
1494 /* Revert context URL from OTP authentication service URL to OTP web
1495 * service base URL for subsequent calls. Potenial isds_login() retry
1496 * will re-set context URL again. */
1497 zfree(context->url);
1498 context->url = _isds_astrcat(url, "apps/");
1499 if (context->url == NULL) {
1500 soap_err = IE_NOMEM;
1502 /* Detach pointer to OTP credentials from context */
1503 context->otp_credentials = NULL;
1506 /* Remove credentials */
1507 _isds_discard_credentials(context, 0);
1509 /* Destroy log-in request */
1510 xmlFreeNode(request);
1512 if (soap_err) {
1513 _isds_close_connection(context);
1514 return soap_err;
1517 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1518 * authentication succeeded if soap_err == IE_SUCCESS */
1519 err = IE_SUCCESS;
1521 if (!err)
1522 isds_log(ILF_ISDS, ILL_DEBUG,
1523 _("User %s has been logged into server %s successfully\n"),
1524 username, url);
1525 return err;
1526 #else /* not HAVE_LIBCURL */
1527 return IE_NOTSUP;
1528 #endif
1532 /* Log out from ISDS server discards credentials and connection configuration. */
1533 isds_error isds_logout(struct isds_ctx *context) {
1534 if (!context) return IE_INVALID_CONTEXT;
1535 zfree(context->long_message);
1537 #if HAVE_LIBCURL
1538 if (context->curl) {
1539 if (context->otp) {
1540 isds_error err = _isds_invalidate_otp_cookie(context);
1541 if (err) return err;
1544 /* Close connection */
1545 _isds_close_connection(context);
1547 /* Discard credentials for sure. They should not survive isds_login(),
1548 * even successful .*/
1549 _isds_discard_credentials(context, 1);
1551 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1552 } else {
1553 _isds_discard_credentials(context, 1);
1555 zfree(context->url);
1556 return IE_SUCCESS;
1557 #else /* not HAVE_LIBCURL */
1558 return IE_NOTSUP;
1559 #endif
1563 /* Verify connection to ISDS is alive and server is responding.
1564 * Send dummy request to ISDS and expect dummy response. */
1565 isds_error isds_ping(struct isds_ctx *context) {
1566 #if HAVE_LIBCURL
1567 isds_error soap_err;
1568 xmlNsPtr isds_ns = NULL;
1569 xmlNodePtr request = NULL;
1570 #endif /* HAVE_LIBCURL */
1572 if (!context) return IE_INVALID_CONTEXT;
1573 zfree(context->long_message);
1575 #if HAVE_LIBCURL
1576 /* Check if connection is established */
1577 if (!context->curl) return IE_CONNECTION_CLOSED;
1580 /* Build dummy request */
1581 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1582 if (!request) {
1583 isds_log_message(context, _("Could build ISDS dummy request"));
1584 return IE_ERROR;
1586 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1587 if(!isds_ns) {
1588 isds_log_message(context, _("Could not create ISDS name space"));
1589 xmlFreeNode(request);
1590 return IE_ERROR;
1592 xmlSetNs(request, isds_ns);
1594 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1596 /* XXX: ISDS documentation does not specify response body for
1597 * DummyOperation request. However real server sends back
1598 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1599 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1600 * SOAP body content, e.g. the dmStatus element. */
1602 /* Send dummy request */
1603 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1605 /* Destroy log-in request */
1606 xmlFreeNode(request);
1608 if (soap_err) {
1609 isds_log(ILF_ISDS, ILL_DEBUG,
1610 _("ISDS server could not be contacted\n"));
1611 return soap_err;
1614 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1615 * authentication succeeded if soap_err == IE_SUCCESS */
1618 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1620 return IE_SUCCESS;
1621 #else /* not HAVE_LIBCURL */
1622 return IE_NOTSUP;
1623 #endif
1627 /* Send bogus request to ISDS.
1628 * Just for test purposes */
1629 isds_error isds_bogus_request(struct isds_ctx *context) {
1630 #if HAVE_LIBCURL
1631 isds_error err;
1632 xmlNsPtr isds_ns = NULL;
1633 xmlNodePtr request = NULL;
1634 xmlDocPtr response = NULL;
1635 xmlChar *code = NULL, *message = NULL;
1636 #endif
1638 if (!context) return IE_INVALID_CONTEXT;
1639 zfree(context->long_message);
1641 #if HAVE_LIBCURL
1642 /* Check if connection is established */
1643 if (!context->curl) {
1644 /* Testing printf message */
1645 isds_printf_message(context, "%s", _("I said connection closed"));
1646 return IE_CONNECTION_CLOSED;
1650 /* Build dummy request */
1651 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1652 if (!request) {
1653 isds_log_message(context, _("Could build ISDS bogus request"));
1654 return IE_ERROR;
1656 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1657 if(!isds_ns) {
1658 isds_log_message(context, _("Could not create ISDS name space"));
1659 xmlFreeNode(request);
1660 return IE_ERROR;
1662 xmlSetNs(request, isds_ns);
1664 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1666 /* Sent bogus request */
1667 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1669 /* Destroy request */
1670 xmlFreeNode(request);
1672 if (err) {
1673 isds_log(ILF_ISDS, ILL_DEBUG,
1674 _("Processing ISDS response on bogus request failed\n"));
1675 xmlFreeDoc(response);
1676 return err;
1679 /* Check for response status */
1680 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1681 &code, &message, NULL);
1682 if (err) {
1683 isds_log(ILF_ISDS, ILL_DEBUG,
1684 _("ISDS response on bogus request is missing status\n"));
1685 free(code);
1686 free(message);
1687 xmlFreeDoc(response);
1688 return err;
1690 if (xmlStrcmp(code, BAD_CAST "0000")) {
1691 char *code_locale = _isds_utf82locale((char*)code);
1692 char *message_locale = _isds_utf82locale((char*)message);
1693 isds_log(ILF_ISDS, ILL_DEBUG,
1694 _("Server refused bogus request (code=%s, message=%s)\n"),
1695 code_locale, message_locale);
1696 /* XXX: Literal error messages from ISDS are Czech messages
1697 * (English sometimes) in UTF-8. It's hard to catch them for
1698 * translation. Successfully gettextized would return in locale
1699 * encoding, unsuccessfully translated would pass in UTF-8. */
1700 isds_log_message(context, message_locale);
1701 free(code_locale);
1702 free(message_locale);
1703 free(code);
1704 free(message);
1705 xmlFreeDoc(response);
1706 return IE_ISDS;
1710 free(code);
1711 free(message);
1712 xmlFreeDoc(response);
1714 isds_log(ILF_ISDS, ILL_DEBUG,
1715 _("Bogus message accepted by server. This should not happen.\n"));
1717 return IE_SUCCESS;
1718 #else /* not HAVE_LIBCURL */
1719 return IE_NOTSUP;
1720 #endif
1724 #if HAVE_LIBCURL
1725 /* Serialize XML subtree to buffer preserving XML indentation.
1726 * @context is session context
1727 * @subtree is XML element to be serialized (with children)
1728 * @buffer is automatically reallocated buffer where serialize to
1729 * @length is size of serialized stream in bytes
1730 * @return standard error code, free @buffer in case of error */
1731 static isds_error serialize_subtree(struct isds_ctx *context,
1732 xmlNodePtr subtree, void **buffer, size_t *length) {
1733 isds_error err = IE_SUCCESS;
1734 xmlBufferPtr xml_buffer = NULL;
1735 xmlSaveCtxtPtr save_ctx = NULL;
1736 xmlDocPtr subtree_doc = NULL;
1737 xmlNodePtr subtree_copy;
1738 xmlNsPtr isds_ns;
1739 void *new_buffer;
1741 if (!context) return IE_INVALID_CONTEXT;
1742 if (!buffer) return IE_INVAL;
1743 zfree(*buffer);
1744 if (!subtree || !length) return IE_INVAL;
1746 /* Make temporary XML document with @subtree root element */
1747 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1748 * It can result in not well-formed on invalid XML tree (e.g. name space
1749 * prefix definition can miss. */
1750 /*FIXME */
1752 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1753 if (!subtree_doc) {
1754 isds_log_message(context, _("Could not build temporary document"));
1755 err = IE_ERROR;
1756 goto leave;
1759 /* XXX: Copy subtree and attach the copy to document.
1760 * One node can not bee attached into more document at the same time.
1761 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1762 * automatically.
1763 * XXX: Check xmlSaveTree() too. */
1764 subtree_copy = xmlCopyNodeList(subtree);
1765 if (!subtree_copy) {
1766 isds_log_message(context, _("Could not copy subtree"));
1767 err = IE_ERROR;
1768 goto leave;
1770 xmlDocSetRootElement(subtree_doc, subtree_copy);
1772 /* Only this way we get namespace definition as @xmlns:isds,
1773 * otherwise we get namespace prefix without definition */
1774 /* FIXME: Don't overwrite original default namespace */
1775 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1776 if(!isds_ns) {
1777 isds_log_message(context, _("Could not create ISDS name space"));
1778 err = IE_ERROR;
1779 goto leave;
1781 xmlSetNs(subtree_copy, isds_ns);
1784 /* Serialize the document into buffer */
1785 xml_buffer = xmlBufferCreate();
1786 if (!xml_buffer) {
1787 isds_log_message(context, _("Could not create xmlBuffer"));
1788 err = IE_ERROR;
1789 goto leave;
1791 /* Last argument 0 means to not format the XML tree */
1792 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1793 if (!save_ctx) {
1794 isds_log_message(context, _("Could not create XML serializer"));
1795 err = IE_ERROR;
1796 goto leave;
1798 /* XXX: According LibXML documentation, this function does not return
1799 * meaningful value yet */
1800 xmlSaveDoc(save_ctx, subtree_doc);
1801 if (-1 == xmlSaveFlush(save_ctx)) {
1802 isds_log_message(context,
1803 _("Could not serialize XML subtree"));
1804 err = IE_ERROR;
1805 goto leave;
1807 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1808 * even after xmlSaveFlush(). Thus close it here */
1809 xmlSaveClose(save_ctx); save_ctx = NULL;
1812 /* Store and detach buffer from xml_buffer */
1813 *buffer = xml_buffer->content;
1814 *length = xml_buffer->use;
1815 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1817 /* Shrink buffer */
1818 new_buffer = realloc(*buffer, *length);
1819 if (new_buffer) *buffer = new_buffer;
1821 leave:
1822 if (err) {
1823 zfree(*buffer);
1824 *length = 0;
1827 xmlSaveClose(save_ctx);
1828 xmlBufferFree(xml_buffer);
1829 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1830 return err;
1832 #endif /* HAVE_LIBCURL */
1835 #if 0
1836 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1837 * @context is session context
1838 * @document is original document where @nodeset points to
1839 * @nodeset is XPath node set to dump (recursively)
1840 * @buffer is automatically reallocated buffer where serialize to
1841 * @length is size of serialized stream in bytes
1842 * @return standard error code, free @buffer in case of error */
1843 static isds_error dump_nodeset(struct isds_ctx *context,
1844 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1845 void **buffer, size_t *length) {
1846 isds_error err = IE_SUCCESS;
1847 xmlBufferPtr xml_buffer = NULL;
1848 void *new_buffer;
1850 if (!context) return IE_INVALID_CONTEXT;
1851 if (!buffer) return IE_INVAL;
1852 zfree(*buffer);
1853 if (!document || !nodeset || !length) return IE_INVAL;
1854 *length = 0;
1856 /* Empty node set results into NULL buffer */
1857 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1858 goto leave;
1861 /* Resulting the document into buffer */
1862 xml_buffer = xmlBufferCreate();
1863 if (!xml_buffer) {
1864 isds_log_message(context, _("Could not create xmlBuffer"));
1865 err = IE_ERROR;
1866 goto leave;
1869 /* Iterate over all nodes */
1870 for (int i = 0; i < nodeset->nodeNr; i++) {
1871 /* Serialize node.
1872 * XXX: xmlNodeDump() appends to xml_buffer. */
1873 if (-1 ==
1874 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1875 isds_log_message(context, _("Could not dump XML node"));
1876 err = IE_ERROR;
1877 goto leave;
1881 /* Store and detach buffer from xml_buffer */
1882 *buffer = xml_buffer->content;
1883 *length = xml_buffer->use;
1884 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1886 /* Shrink buffer */
1887 new_buffer = realloc(*buffer, *length);
1888 if (new_buffer) *buffer = new_buffer;
1891 leave:
1892 if (err) {
1893 zfree(*buffer);
1894 *length = 0;
1897 xmlBufferFree(xml_buffer);
1898 return err;
1900 #endif
1902 #if 0
1903 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1904 * @context is session context
1905 * @document is original document where @nodeset points to
1906 * @nodeset is XPath node set to dump (recursively)
1907 * @buffer is automatically reallocated buffer where serialize to
1908 * @length is size of serialized stream in bytes
1909 * @return standard error code, free @buffer in case of error */
1910 static isds_error dump_nodeset(struct isds_ctx *context,
1911 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1912 void **buffer, size_t *length) {
1913 isds_error err = IE_SUCCESS;
1914 xmlBufferPtr xml_buffer = NULL;
1915 xmlSaveCtxtPtr save_ctx = NULL;
1916 void *new_buffer;
1918 if (!context) return IE_INVALID_CONTEXT;
1919 if (!buffer) return IE_INVAL;
1920 zfree(*buffer);
1921 if (!document || !nodeset || !length) return IE_INVAL;
1922 *length = 0;
1924 /* Empty node set results into NULL buffer */
1925 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1926 goto leave;
1929 /* Resulting the document into buffer */
1930 xml_buffer = xmlBufferCreate();
1931 if (!xml_buffer) {
1932 isds_log_message(context, _("Could not create xmlBuffer"));
1933 err = IE_ERROR;
1934 goto leave;
1936 if (xmlSubstituteEntitiesDefault(1)) {
1937 isds_log_message(context, _("Could not disable attribute escaping"));
1938 err = IE_ERROR;
1939 goto leave;
1941 /* Last argument means:
1942 * 0 to not format the XML tree
1943 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1944 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1945 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1946 if (!save_ctx) {
1947 isds_log_message(context, _("Could not create XML serializer"));
1948 err = IE_ERROR;
1949 goto leave;
1951 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1952 isds_log_message(context, _("Could not disable attribute escaping"));
1953 err = IE_ERROR;
1954 goto leave;
1958 /* Iterate over all nodes */
1959 for (int i = 0; i < nodeset->nodeNr; i++) {
1960 /* Serialize node.
1961 * XXX: xmlNodeDump() appends to xml_buffer. */
1962 /*if (-1 ==
1963 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1965 /* XXX: According LibXML documentation, this function does not return
1966 * meaningful value yet */
1967 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1968 if (-1 == xmlSaveFlush(save_ctx)) {
1969 isds_log_message(context,
1970 _("Could not serialize XML subtree"));
1971 err = IE_ERROR;
1972 goto leave;
1976 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1977 * even after xmlSaveFlush(). Thus close it here */
1978 xmlSaveClose(save_ctx); save_ctx = NULL;
1980 /* Store and detach buffer from xml_buffer */
1981 *buffer = xml_buffer->content;
1982 *length = xml_buffer->use;
1983 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1985 /* Shrink buffer */
1986 new_buffer = realloc(*buffer, *length);
1987 if (new_buffer) *buffer = new_buffer;
1989 leave:
1990 if (err) {
1991 zfree(*buffer);
1992 *length = 0;
1995 xmlSaveClose(save_ctx);
1996 xmlBufferFree(xml_buffer);
1997 return err;
1999 #endif
2002 #if HAVE_LIBCURL
2003 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
2004 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
2005 if (!string || !type) return IE_INVAL;
2007 if (!xmlStrcmp(string, BAD_CAST "FO"))
2008 *type = DBTYPE_FO;
2009 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
2010 *type = DBTYPE_PFO;
2011 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
2012 *type = DBTYPE_PFO_ADVOK;
2013 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
2014 *type = DBTYPE_PFO_DANPOR;
2015 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
2016 *type = DBTYPE_PFO_INSSPR;
2017 else if (!xmlStrcmp(string, BAD_CAST "PO"))
2018 *type = DBTYPE_PO;
2019 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
2020 *type = DBTYPE_PO_ZAK;
2021 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
2022 *type = DBTYPE_PO_REQ;
2023 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
2024 *type = DBTYPE_OVM;
2025 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
2026 *type = DBTYPE_OVM_NOTAR;
2027 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
2028 *type = DBTYPE_OVM_EXEKUT;
2029 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
2030 *type = DBTYPE_OVM_REQ;
2031 else
2032 return IE_ENUM;
2033 return IE_SUCCESS;
2037 /* Convert ISDS dbType enum @type to UTF-8 string.
2038 * @Return pointer to static string, or NULL if unknown enum value */
2039 static const xmlChar *isds_DbType2string(const isds_DbType type) {
2040 switch(type) {
2041 /* DBTYPE_SYSTEM and DBTYPE_OVM_MAIN are invalid values from point
2042 * of view of generic public SOAP interface. */
2043 case DBTYPE_FO: return(BAD_CAST "FO"); break;
2044 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
2045 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
2046 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
2047 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
2048 case DBTYPE_PO: return(BAD_CAST "PO"); break;
2049 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
2050 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
2051 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
2052 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
2053 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
2054 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
2055 default: return NULL; break;
2060 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2061 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
2062 if (!string || !type) return IE_INVAL;
2064 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2065 *type = USERTYPE_PRIMARY;
2066 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2067 *type = USERTYPE_ENTRUSTED;
2068 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2069 *type = USERTYPE_ADMINISTRATOR;
2070 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2071 *type = USERTYPE_OFFICIAL;
2072 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2073 *type = USERTYPE_OFFICIAL_CERT;
2074 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2075 *type = USERTYPE_LIQUIDATOR;
2076 else
2077 return IE_ENUM;
2078 return IE_SUCCESS;
2082 /* Convert ISDS userType enum @type to UTF-8 string.
2083 * @Return pointer to static string, or NULL if unknown enum value */
2084 static const xmlChar *isds_UserType2string(const isds_UserType type) {
2085 switch(type) {
2086 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
2087 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
2088 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
2089 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
2090 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
2091 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
2092 default: return NULL; break;
2097 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2098 static isds_error string2isds_sender_type(const xmlChar *string,
2099 isds_sender_type *type) {
2100 if (!string || !type) return IE_INVAL;
2102 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2103 *type = SENDERTYPE_PRIMARY;
2104 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2105 *type = SENDERTYPE_ENTRUSTED;
2106 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2107 *type = SENDERTYPE_ADMINISTRATOR;
2108 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2109 *type = SENDERTYPE_OFFICIAL;
2110 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2111 *type = SENDERTYPE_VIRTUAL;
2112 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2113 *type = SENDERTYPE_OFFICIAL_CERT;
2114 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2115 *type = SENDERTYPE_LIQUIDATOR;
2116 else
2117 return IE_ENUM;
2118 return IE_SUCCESS;
2122 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2123 static isds_error string2isds_payment_type(const xmlChar *string,
2124 isds_payment_type *type) {
2125 if (!string || !type) return IE_INVAL;
2127 if (!xmlStrcmp(string, BAD_CAST "K"))
2128 *type = PAYMENT_SENDER;
2129 else if (!xmlStrcmp(string, BAD_CAST "O"))
2130 *type = PAYMENT_RESPONSE;
2131 else if (!xmlStrcmp(string, BAD_CAST "G"))
2132 *type = PAYMENT_SPONSOR;
2133 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2134 *type = PAYMENT_SPONSOR_LIMITED;
2135 else if (!xmlStrcmp(string, BAD_CAST "D"))
2136 *type = PAYMENT_SPONSOR_EXTERNAL;
2137 else if (!xmlStrcmp(string, BAD_CAST "E"))
2138 *type = PAYMENT_STAMP;
2139 else
2140 return IE_ENUM;
2141 return IE_SUCCESS;
2145 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2146 * ciEventType is integer but we convert it from string representation
2147 * directly. */
2148 static isds_error string2isds_credit_event_type(const xmlChar *string,
2149 isds_credit_event_type *type) {
2150 if (!string || !type) return IE_INVAL;
2152 if (!xmlStrcmp(string, BAD_CAST "1"))
2153 *type = ISDS_CREDIT_CHARGED;
2154 else if (!xmlStrcmp(string, BAD_CAST "2"))
2155 *type = ISDS_CREDIT_DISCHARGED;
2156 else if (!xmlStrcmp(string, BAD_CAST "3"))
2157 *type = ISDS_CREDIT_MESSAGE_SENT;
2158 else if (!xmlStrcmp(string, BAD_CAST "4"))
2159 *type = ISDS_CREDIT_STORAGE_SET;
2160 else if (!xmlStrcmp(string, BAD_CAST "5"))
2161 *type = ISDS_CREDIT_EXPIRED;
2162 else
2163 return IE_ENUM;
2164 return IE_SUCCESS;
2168 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2169 * @Return pointer to static string, or NULL if unknown enum value */
2170 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2171 switch(type) {
2172 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2173 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2174 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2175 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2176 default: return NULL; break;
2181 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2182 * ISDSSearch2/searchType value.
2183 * @Return pointer to static string, or NULL if unknown enum value */
2184 static const xmlChar *isds_fulltext_target2string(
2185 const isds_fulltext_target type) {
2186 switch(type) {
2187 case FULLTEXT_ALL: return(BAD_CAST "GENERAL"); break;
2188 case FULLTEXT_ADDRESS: return(BAD_CAST "ADDRESS"); break;
2189 case FULLTEXT_IC: return(BAD_CAST "ICO"); break;
2190 case FULLTEXT_BOX_ID: return(BAD_CAST "DBID"); break;
2191 default: return NULL; break;
2194 #endif /* HAVE_LIBCURL */
2197 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2198 * @Return IE_ENUM if @string is not valid enum member */
2199 static isds_error string2isds_FileMetaType(const xmlChar *string,
2200 isds_FileMetaType *type) {
2201 if (!string || !type) return IE_INVAL;
2203 if (!xmlStrcmp(string, BAD_CAST "main"))
2204 *type = FILEMETATYPE_MAIN;
2205 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2206 *type = FILEMETATYPE_ENCLOSURE;
2207 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2208 *type = FILEMETATYPE_SIGNATURE;
2209 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2210 *type = FILEMETATYPE_META;
2211 else
2212 return IE_ENUM;
2213 return IE_SUCCESS;
2217 /* Convert UTF-8 @string to ISDS hash @algorithm.
2218 * @Return IE_ENUM if @string is not valid enum member */
2219 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2220 isds_hash_algorithm *algorithm) {
2221 if (!string || !algorithm) return IE_INVAL;
2223 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2224 *algorithm = HASH_ALGORITHM_MD5;
2225 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2226 *algorithm = HASH_ALGORITHM_SHA_1;
2227 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2228 *algorithm = HASH_ALGORITHM_SHA_224;
2229 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2230 *algorithm = HASH_ALGORITHM_SHA_256;
2231 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2232 *algorithm = HASH_ALGORITHM_SHA_384;
2233 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2234 *algorithm = HASH_ALGORITHM_SHA_512;
2235 else
2236 return IE_ENUM;
2237 return IE_SUCCESS;
2241 #if HAVE_LIBCURL
2242 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2243 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2244 if (!time || !string) return IE_INVAL;
2246 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2247 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2248 return IE_ERROR;
2250 return IE_SUCCESS;
2254 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2255 * respects the @time microseconds too. */
2256 static isds_error timeval2timestring(const struct timeval *time,
2257 xmlChar **string) {
2258 struct tm broken;
2259 time_t seconds_as_time_t;
2261 if (!time || !string) return IE_INVAL;
2263 /* MinGW32 GCC 4.8+ uses 64-bit time_t but time->tv_sec is defined as
2264 * 32-bit long in Microsoft API. Convert value to the type expected by
2265 * gmtime_r(). */
2266 seconds_as_time_t = time->tv_sec;
2267 if (!gmtime_r(&seconds_as_time_t, &broken)) return IE_DATE;
2268 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2270 /* TODO: small negative year should be formatted as "-0012". This is not
2271 * true for glibc "%04d". We should implement it.
2272 * time->tv_usec type is su_seconds_t which is required to be signed
2273 * integer to accomodate values from range [-1, 1000000].
2274 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2275 /* XXX: Do not format time->tv_usec as intmax_t because %jd is not
2276 * supported and PRIdMAX is broken on MingGW. We can use int32_t because
2277 * of the range check above. */
2278 if (-1 == isds_asprintf((char **) string,
2279 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRId32,
2280 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2281 broken.tm_hour, broken.tm_min, broken.tm_sec,
2282 (int32_t)time->tv_usec))
2283 return IE_ERROR;
2285 return IE_SUCCESS;
2287 #endif /* HAVE_LIBCURL */
2290 /* Convert UTF-8 ISO 8601 date-time @string to static struct timeval.
2291 * It respects microseconds too. Microseconds are rounded half up.
2292 * In case of error, @time will be undefined. */
2293 static isds_error timestring2static_timeval(const xmlChar *string,
2294 struct timeval *time) {
2295 struct tm broken;
2296 char *offset, *delim, *endptr;
2297 const int subsecond_resolution = 6;
2298 char subseconds[subsecond_resolution + 1];
2299 _Bool round_up = 0;
2300 int offset_hours, offset_minutes;
2301 int i;
2302 long int long_number;
2303 #ifdef _WIN32
2304 int tmp;
2305 #endif
2307 if (!time) return IE_INVAL;
2308 if (!string) {
2309 return IE_INVAL;
2312 memset(&broken, 0, sizeof(broken));
2313 memset(time, 0, sizeof(*time));
2316 /* xsd:date is ISO 8601 string, thus ASCII */
2317 /*TODO: negative year */
2319 #ifdef _WIN32
2320 i = 0;
2321 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2322 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2323 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2324 &i)) < 6) {
2325 return IE_DATE;
2328 broken.tm_year -= 1900;
2329 broken.tm_mon--;
2330 broken.tm_isdst = -1;
2331 offset = (char*)string + i;
2332 #else
2333 /* Parse date and time without subseconds and offset */
2334 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2335 if (!offset) {
2336 return IE_DATE;
2338 #endif
2340 /* Get subseconds */
2341 if (*offset == '.' ) {
2342 offset++;
2344 /* Copy first 6 digits, pad it with zeros.
2345 * Current server implementation uses only millisecond resolution. */
2346 /* TODO: isdigit() is locale sensitive */
2347 for (i = 0;
2348 i < subsecond_resolution && isdigit(*offset);
2349 i++, offset++) {
2350 subseconds[i] = *offset;
2352 if (subsecond_resolution == i && isdigit(*offset)) {
2353 /* Check 7th digit for rounding */
2354 if (*offset >= '5') round_up = 1;
2355 offset++;
2357 for (; i < subsecond_resolution; i++) {
2358 subseconds[i] = '0';
2360 subseconds[subsecond_resolution] = '\0';
2362 /* Convert it into integer */
2363 long_number = strtol(subseconds, &endptr, 10);
2364 if (*endptr != '\0' || long_number == LONG_MIN ||
2365 long_number == LONG_MAX) {
2366 return IE_DATE;
2368 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2369 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2370 * microseconds" and "the type shall be a signed integer capable of
2371 * storing values at least in the range [-1, 1000000]. */
2372 if (long_number < -1 || long_number >= 1000000) {
2373 return IE_DATE;
2375 time->tv_usec = long_number;
2377 /* Round the subseconds */
2378 if (round_up) {
2379 if (999999 == time->tv_usec) {
2380 time->tv_usec = 0;
2381 broken.tm_sec++;
2382 } else {
2383 time->tv_usec++;
2387 /* move to the zone offset delimiter or signal NULL*/
2388 delim = strchr(offset, '-');
2389 if (!delim)
2390 delim = strchr(offset, '+');
2391 if (!delim)
2392 delim = strchr(offset, 'Z');
2393 offset = delim;
2396 /* Get zone offset */
2397 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2398 * "" equals to "Z" and it means UTC zone. */
2399 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2400 * colon separator */
2401 if (offset && (*offset == '-' || *offset == '+')) {
2402 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2403 return IE_DATE;
2405 if (*offset == '+') {
2406 broken.tm_hour -= offset_hours;
2407 broken.tm_min -= offset_minutes;
2408 } else {
2409 broken.tm_hour += offset_hours;
2410 broken.tm_min += offset_minutes;
2414 /* Convert to time_t */
2415 time->tv_sec = _isds_timegm(&broken);
2416 if (time->tv_sec == (time_t) -1) {
2417 return IE_DATE;
2420 return IE_SUCCESS;
2424 /* Convert UTF-8 ISO 8601 date-time @string to reallocated struct timeval.
2425 * It respects microseconds too. Microseconds are rounded half up.
2426 * In case of error, @time will be freed. */
2427 static isds_error timestring2timeval(const xmlChar *string,
2428 struct timeval **time) {
2429 isds_error error;
2431 if (!time) return IE_INVAL;
2432 if (!string) {
2433 zfree(*time);
2434 return IE_INVAL;
2437 if (!*time) {
2438 *time = calloc(1, sizeof(**time));
2439 if (!*time) return IE_NOMEM;
2440 } else {
2441 memset(*time, 0, sizeof(**time));
2444 error = timestring2static_timeval(string, *time);
2445 if (error) {
2446 zfree(*time);
2449 return error;
2453 /* Convert unsigned int into isds_message_status.
2454 * @context is session context
2455 * @number is pointer to number value. NULL will be treated as invalid value.
2456 * @status is automatically reallocated status
2457 * @return IE_SUCCESS, or error code and free status */
2458 static isds_error uint2isds_message_status(struct isds_ctx *context,
2459 const unsigned long int *number, isds_message_status **status) {
2460 if (!context) return IE_INVALID_CONTEXT;
2461 if (!status) return IE_INVAL;
2463 free(*status); *status = NULL;
2464 if (!number) return IE_INVAL;
2466 if (*number < 1 || *number > 10) {
2467 isds_printf_message(context, _("Invalid message status value: %lu"),
2468 *number);
2469 return IE_ENUM;
2472 *status = malloc(sizeof(**status));
2473 if (!*status) return IE_NOMEM;
2475 **status = 1 << *number;
2476 return IE_SUCCESS;
2480 /* Convert event description string into isds_event members type and
2481 * description
2482 * @string is raw event description starting with event prefix
2483 * @event is structure where to store type and stripped description to
2484 * @return standard error code, unknown prefix is not classified as an error.
2485 * */
2486 static isds_error eventstring2event(const xmlChar *string,
2487 struct isds_event* event) {
2488 const xmlChar *known_prefixes[] = {
2489 BAD_CAST "EV0:",
2490 BAD_CAST "EV1:",
2491 BAD_CAST "EV2:",
2492 BAD_CAST "EV3:",
2493 BAD_CAST "EV4:",
2494 BAD_CAST "EV5:",
2495 BAD_CAST "EV11:",
2496 BAD_CAST "EV12:",
2497 BAD_CAST "EV13:"
2499 const isds_event_type types[] = {
2500 EVENT_ENTERED_SYSTEM,
2501 EVENT_ACCEPTED_BY_RECIPIENT,
2502 EVENT_ACCEPTED_BY_FICTION,
2503 EVENT_UNDELIVERABLE,
2504 EVENT_COMMERCIAL_ACCEPTED,
2505 EVENT_DELIVERED,
2506 EVENT_PRIMARY_LOGIN,
2507 EVENT_ENTRUSTED_LOGIN,
2508 EVENT_SYSCERT_LOGIN
2510 unsigned int index;
2511 size_t length;
2513 if (!string || !event) return IE_INVAL;
2515 if (!event->type) {
2516 event->type = malloc(sizeof(*event->type));
2517 if (!(event->type)) return IE_NOMEM;
2519 zfree(event->description);
2521 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2522 index++) {
2523 length = xmlUTF8Strlen(known_prefixes[index]);
2525 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2526 /* Prefix is known */
2527 *event->type = types[index];
2529 /* Strip prefix from description and spaces */
2530 /* TODO: Recognize all white spaces from UCS blank class and
2531 * operate on UTF-8 chars. */
2532 for (; string[length] != '\0' && string[length] == ' '; length++);
2533 event->description = strdup((char *) (string + length));
2534 if (!(event->description)) return IE_NOMEM;
2536 return IE_SUCCESS;
2540 /* Unknown event prefix.
2541 * XSD allows any string */
2542 char *string_locale = _isds_utf82locale((char *) string);
2543 isds_log(ILF_ISDS, ILL_WARNING,
2544 _("Unknown delivery info event prefix: %s\n"), string_locale);
2545 free(string_locale);
2547 *event->type = EVENT_UKNOWN;
2548 event->description = strdup((char *) string);
2549 if (!(event->description)) return IE_NOMEM;
2551 return IE_SUCCESS;
2555 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2556 * and leave label */
2557 #define EXTRACT_STRING(element, string) { \
2558 xmlXPathFreeObject(result); \
2559 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2560 if (NULL == (result)) { \
2561 err = IE_ERROR; \
2562 goto leave; \
2564 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2565 if (result->nodesetval->nodeNr > 1) { \
2566 isds_printf_message(context, _("Multiple %s element"), element); \
2567 err = IE_ERROR; \
2568 goto leave; \
2570 (string) = (char *) \
2571 xmlXPathCastNodeSetToString(result->nodesetval); \
2572 if (NULL == (string)) { \
2573 err = IE_ERROR; \
2574 goto leave; \
2579 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2581 char *string = NULL; \
2582 EXTRACT_STRING(element, string); \
2584 if (string) { \
2585 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2586 if (!(booleanPtr)) { \
2587 free(string); \
2588 err = IE_NOMEM; \
2589 goto leave; \
2592 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2593 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2594 *(booleanPtr) = 1; \
2595 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2596 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2597 *(booleanPtr) = 0; \
2598 else { \
2599 char *string_locale = _isds_utf82locale((char*)string); \
2600 isds_printf_message(context, \
2601 _("%s value is not valid boolean: %s"), \
2602 element, string_locale); \
2603 free(string_locale); \
2604 free(string); \
2605 err = IE_ERROR; \
2606 goto leave; \
2609 free(string); \
2613 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2615 char *string = NULL; \
2616 EXTRACT_STRING(element, string); \
2618 if (NULL == string) { \
2619 isds_printf_message(context, _("%s element is empty"), element); \
2620 err = IE_ERROR; \
2621 goto leave; \
2623 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2624 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2625 (boolean) = 1; \
2626 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2627 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2628 (boolean) = 0; \
2629 else { \
2630 char *string_locale = _isds_utf82locale((char*)string); \
2631 isds_printf_message(context, \
2632 _("%s value is not valid boolean: %s"), \
2633 element, string_locale); \
2634 free(string_locale); \
2635 free(string); \
2636 err = IE_ERROR; \
2637 goto leave; \
2640 free(string); \
2643 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2645 char *string = NULL; \
2646 EXTRACT_STRING(element, string); \
2647 if (string) { \
2648 long int number; \
2649 char *endptr; \
2651 number = strtol((char*)string, &endptr, 10); \
2653 if (*endptr != '\0') { \
2654 char *string_locale = _isds_utf82locale((char *)string); \
2655 isds_printf_message(context, \
2656 _("%s is not valid integer: %s"), \
2657 element, string_locale); \
2658 free(string_locale); \
2659 free(string); \
2660 err = IE_ISDS; \
2661 goto leave; \
2664 if (number == LONG_MIN || number == LONG_MAX) { \
2665 char *string_locale = _isds_utf82locale((char *)string); \
2666 isds_printf_message(context, \
2667 _("%s value out of range of long int: %s"), \
2668 element, string_locale); \
2669 free(string_locale); \
2670 free(string); \
2671 err = IE_ERROR; \
2672 goto leave; \
2675 free(string); string = NULL; \
2677 if (!(preallocated)) { \
2678 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2679 if (!(longintPtr)) { \
2680 err = IE_NOMEM; \
2681 goto leave; \
2684 *(longintPtr) = number; \
2688 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2690 char *string = NULL; \
2691 EXTRACT_STRING(element, string); \
2692 if (string) { \
2693 long int number; \
2694 char *endptr; \
2696 number = strtol((char*)string, &endptr, 10); \
2698 if (*endptr != '\0') { \
2699 char *string_locale = _isds_utf82locale((char *)string); \
2700 isds_printf_message(context, \
2701 _("%s is not valid integer: %s"), \
2702 element, string_locale); \
2703 free(string_locale); \
2704 free(string); \
2705 err = IE_ISDS; \
2706 goto leave; \
2709 if (number == LONG_MIN || number == LONG_MAX) { \
2710 char *string_locale = _isds_utf82locale((char *)string); \
2711 isds_printf_message(context, \
2712 _("%s value out of range of long int: %s"), \
2713 element, string_locale); \
2714 free(string_locale); \
2715 free(string); \
2716 err = IE_ERROR; \
2717 goto leave; \
2720 free(string); string = NULL; \
2721 if (number < 0) { \
2722 isds_printf_message(context, \
2723 _("%s value is negative: %ld"), element, number); \
2724 err = IE_ERROR; \
2725 goto leave; \
2728 if (!(preallocated)) { \
2729 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2730 if (!(ulongintPtr)) { \
2731 err = IE_NOMEM; \
2732 goto leave; \
2735 *(ulongintPtr) = number; \
2739 #define EXTRACT_DATE(element, tmPtr) { \
2740 char *string = NULL; \
2741 EXTRACT_STRING(element, string); \
2742 if (NULL != string) { \
2743 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2744 if (NULL == (tmPtr)) { \
2745 free(string); \
2746 err = IE_NOMEM; \
2747 goto leave; \
2749 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2750 if (err) { \
2751 if (err == IE_NOTSUP) { \
2752 err = IE_ISDS; \
2753 char *string_locale = _isds_utf82locale(string); \
2754 char *element_locale = _isds_utf82locale(element); \
2755 isds_printf_message(context, _("Invalid %s value: %s"), \
2756 element_locale, string_locale); \
2757 free(string_locale); \
2758 free(element_locale); \
2760 free(string); \
2761 goto leave; \
2763 free(string); \
2767 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2768 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2769 NULL); \
2770 if ((required) && (!string)) { \
2771 char *attribute_locale = _isds_utf82locale(attribute); \
2772 char *element_locale = \
2773 _isds_utf82locale((char *)xpath_ctx->node->name); \
2774 isds_printf_message(context, \
2775 _("Could not extract required %s attribute value from " \
2776 "%s element"), attribute_locale, element_locale); \
2777 free(element_locale); \
2778 free(attribute_locale); \
2779 err = IE_ERROR; \
2780 goto leave; \
2785 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2787 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2788 (xmlChar *) (string)); \
2789 if (!node) { \
2790 isds_printf_message(context, \
2791 _("Could not add %s child to %s element"), \
2792 element, (parent)->name); \
2793 err = IE_ERROR; \
2794 goto leave; \
2798 #define INSERT_STRING(parent, element, string) \
2799 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2801 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2803 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2804 else { INSERT_STRING(parent, element, "false"); } \
2807 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2809 if (booleanPtr) { \
2810 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2811 } else { \
2812 INSERT_STRING(parent, element, NULL); \
2816 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2817 if ((longintPtr)) { \
2818 /* FIXME: locale sensitive */ \
2819 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2820 err = IE_NOMEM; \
2821 goto leave; \
2823 INSERT_STRING(parent, element, buffer) \
2824 free(buffer); (buffer) = NULL; \
2825 } else { INSERT_STRING(parent, element, NULL) } \
2828 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2829 if ((ulongintPtr)) { \
2830 /* FIXME: locale sensitive */ \
2831 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2832 err = IE_NOMEM; \
2833 goto leave; \
2835 INSERT_STRING(parent, element, buffer) \
2836 free(buffer); (buffer) = NULL; \
2837 } else { INSERT_STRING(parent, element, NULL) } \
2840 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2842 /* FIXME: locale sensitive */ \
2843 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2844 err = IE_NOMEM; \
2845 goto leave; \
2847 INSERT_STRING(parent, element, buffer) \
2848 free(buffer); (buffer) = NULL; \
2851 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2852 * new attribute. */
2853 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2855 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2856 (xmlChar *) (string)); \
2857 if (!attribute_node) { \
2858 isds_printf_message(context, _("Could not add %s " \
2859 "attribute to %s element"), \
2860 (attribute), (parent)->name); \
2861 err = IE_ERROR; \
2862 goto leave; \
2866 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2867 if (string) { \
2868 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2869 if (length > (maximum)) { \
2870 isds_printf_message(context, \
2871 ngettext("%s has more than %d characters", \
2872 "%s has more than %d characters", (maximum)), \
2873 (name), (maximum)); \
2874 err = IE_2BIG; \
2875 goto leave; \
2877 if (length < (minimum)) { \
2878 isds_printf_message(context, \
2879 ngettext("%s has less than %d characters", \
2880 "%s has less than %d characters", (minimum)), \
2881 (name), (minimum)); \
2882 err = IE_2SMALL; \
2883 goto leave; \
2888 #define INSERT_ELEMENT(child, parent, element) \
2890 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2891 if (!(child)) { \
2892 isds_printf_message(context, \
2893 _("Could not add %s child to %s element"), \
2894 (element), (parent)->name); \
2895 err = IE_ERROR; \
2896 goto leave; \
2901 /* Find child element by name in given XPath context and switch context onto
2902 * it. The child must be uniq and must exist. Otherwise fails.
2903 * @context is ISDS context
2904 * @child is child element name
2905 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2906 * into it child. In error case, the @xpath_ctx keeps original value. */
2907 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2908 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2909 isds_error err = IE_SUCCESS;
2910 xmlXPathObjectPtr result = NULL;
2912 if (!context) return IE_INVALID_CONTEXT;
2913 if (!child || !xpath_ctx) return IE_INVAL;
2915 /* Find child */
2916 result = xmlXPathEvalExpression(child, xpath_ctx);
2917 if (!result) {
2918 err = IE_XML;
2919 goto leave;
2922 /* No match */
2923 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2924 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2925 char *child_locale = _isds_utf82locale((char*) child);
2926 isds_printf_message(context,
2927 _("%s element does not contain %s child"),
2928 parent_locale, child_locale);
2929 free(child_locale);
2930 free(parent_locale);
2931 err = IE_NOEXIST;
2932 goto leave;
2935 /* More matches */
2936 if (result->nodesetval->nodeNr > 1) {
2937 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2938 char *child_locale = _isds_utf82locale((char*) child);
2939 isds_printf_message(context,
2940 _("%s element contains multiple %s children"),
2941 parent_locale, child_locale);
2942 free(child_locale);
2943 free(parent_locale);
2944 err = IE_NOTUNIQ;
2945 goto leave;
2948 /* Switch context */
2949 xpath_ctx->node = result->nodesetval->nodeTab[0];
2951 leave:
2952 xmlXPathFreeObject(result);
2953 return err;
2958 #if HAVE_LIBCURL
2959 /* Find and convert XSD:gPersonName group in current node into structure
2960 * @context is ISDS context
2961 * @personName is automatically reallocated person name structure. If no member
2962 * value is found, will be freed.
2963 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2964 * elements
2965 * In case of error @personName will be freed. */
2966 static isds_error extract_gPersonName(struct isds_ctx *context,
2967 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2968 isds_error err = IE_SUCCESS;
2969 xmlXPathObjectPtr result = NULL;
2971 if (!context) return IE_INVALID_CONTEXT;
2972 if (!personName) return IE_INVAL;
2973 isds_PersonName_free(personName);
2974 if (!xpath_ctx) return IE_INVAL;
2977 *personName = calloc(1, sizeof(**personName));
2978 if (!*personName) {
2979 err = IE_NOMEM;
2980 goto leave;
2983 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2984 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2985 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2986 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2988 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2989 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2990 isds_PersonName_free(personName);
2992 leave:
2993 if (err) isds_PersonName_free(personName);
2994 xmlXPathFreeObject(result);
2995 return err;
2999 /* Find and convert XSD:gAddress group extended with relevant
3000 * tdbPersonalOwnerinfo members in current node into structure
3001 * @context is ISDS context
3002 * @address is automatically reallocated address structure. If no member
3003 * value is found, will be freed.
3004 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
3005 * elements
3006 * In case of error @address will be freed. */
3007 static isds_error extract_gAddress(struct isds_ctx *context,
3008 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
3009 isds_error err = IE_SUCCESS;
3010 xmlXPathObjectPtr result = NULL;
3012 if (!context) return IE_INVALID_CONTEXT;
3013 if (!address) return IE_INVAL;
3014 isds_Address_free(address);
3015 if (!xpath_ctx) return IE_INVAL;
3018 *address = calloc(1, sizeof(**address));
3019 if (!*address) {
3020 err = IE_NOMEM;
3021 goto leave;
3024 EXTRACT_LONGINT("isds:adCode", (*address)->adCode, 0);
3025 EXTRACT_STRING("isds:adCity", (*address)->adCity);
3026 EXTRACT_STRING("isds:adDistrict", (*address)->adDistrict);
3027 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
3028 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
3029 EXTRACT_STRING("isds:adNumberInMunicipality",
3030 (*address)->adNumberInMunicipality);
3031 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
3032 EXTRACT_STRING("isds:adState", (*address)->adState);
3034 if (!(*address)->adCity && !(*address)->adStreet &&
3035 !(*address)->adNumberInStreet &&
3036 !(*address)->adNumberInMunicipality &&
3037 !(*address)->adZipCode && !(*address)->adState)
3038 isds_Address_free(address);
3040 leave:
3041 if (err) isds_Address_free(address);
3042 xmlXPathFreeObject(result);
3043 return err;
3047 /* Find and convert isds:biDate element in current node into structure
3048 * @context is ISDS context
3049 * @biDate is automatically reallocated birth date structure. If no member
3050 * value is found, will be freed.
3051 * @xpath_ctx is XPath context with current node as parent for isds:biDate
3052 * element
3053 * In case of error @biDate will be freed. */
3054 static isds_error extract_BiDate(struct isds_ctx *context,
3055 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
3056 isds_error err = IE_SUCCESS;
3057 xmlXPathObjectPtr result = NULL;
3058 char *string = NULL;
3060 if (!context) return IE_INVALID_CONTEXT;
3061 if (!biDate) return IE_INVAL;
3062 zfree(*biDate);
3063 if (!xpath_ctx) return IE_INVAL;
3065 EXTRACT_STRING("isds:biDate", string);
3066 if (string) {
3067 *biDate = calloc(1, sizeof(**biDate));
3068 if (!*biDate) {
3069 err = IE_NOMEM;
3070 goto leave;
3072 err = _isds_datestring2tm((xmlChar *)string, *biDate);
3073 if (err) {
3074 if (err == IE_NOTSUP) {
3075 err = IE_ISDS;
3076 char *string_locale = _isds_utf82locale(string);
3077 isds_printf_message(context,
3078 _("Invalid isds:biDate value: %s"), string_locale);
3079 free(string_locale);
3081 goto leave;
3085 leave:
3086 if (err) zfree(*biDate);
3087 free(string);
3088 xmlXPathFreeObject(result);
3089 return err;
3093 /* Convert XSD:tDbOwnerInfo or XSD:tdbPersonalOwenerInfo XML tree into structure
3094 * @context is ISDS context
3095 * @db_owner_info is automatically reallocated box owner info structure
3096 * @xpath_ctx is XPath context with current node as XSD:tDbOwnerInfo or
3097 * XSD:tdbPersonalOwenerInfo element
3098 * In case of error @db_owner_info will be freed. */
3099 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
3100 struct isds_DbOwnerInfo **db_owner_info,
3101 xmlXPathContextPtr xpath_ctx) {
3102 isds_error err = IE_SUCCESS;
3103 xmlXPathObjectPtr result = NULL;
3104 char *string = NULL;
3106 if (!context) return IE_INVALID_CONTEXT;
3107 if (!db_owner_info) return IE_INVAL;
3108 isds_DbOwnerInfo_free(db_owner_info);
3109 if (!xpath_ctx) return IE_INVAL;
3112 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3113 if (!*db_owner_info) {
3114 err = IE_NOMEM;
3115 goto leave;
3118 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
3120 EXTRACT_BOOLEAN("isds:aifoIsds", (*db_owner_info)->aifoIsds);
3122 EXTRACT_STRING("isds:dbType", string);
3123 if (string) {
3124 (*db_owner_info)->dbType =
3125 calloc(1, sizeof(*((*db_owner_info)->dbType)));
3126 if (!(*db_owner_info)->dbType) {
3127 err = IE_NOMEM;
3128 goto leave;
3130 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
3131 if (err) {
3132 zfree((*db_owner_info)->dbType);
3133 if (err == IE_ENUM) {
3134 err = IE_ISDS;
3135 char *string_locale = _isds_utf82locale(string);
3136 isds_printf_message(context, _("Unknown isds:dbType: %s"),
3137 string_locale);
3138 free(string_locale);
3140 goto leave;
3142 zfree(string);
3145 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
3147 err = extract_gPersonName(context, &(*db_owner_info)->personName,
3148 xpath_ctx);
3149 if (err) goto leave;
3151 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
3153 (*db_owner_info)->birthInfo =
3154 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
3155 if (!(*db_owner_info)->birthInfo) {
3156 err = IE_NOMEM;
3157 goto leave;
3159 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
3160 xpath_ctx);
3161 if (err) goto leave;
3162 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3163 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3164 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3165 if (!(*db_owner_info)->birthInfo->biDate &&
3166 !(*db_owner_info)->birthInfo->biCity &&
3167 !(*db_owner_info)->birthInfo->biCounty &&
3168 !(*db_owner_info)->birthInfo->biState)
3169 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3171 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3172 if (err) goto leave;
3174 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3175 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3176 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3177 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3178 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3180 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3182 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3183 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3184 (*db_owner_info)->dbOpenAddressing);
3186 leave:
3187 if (err) isds_DbOwnerInfo_free(db_owner_info);
3188 free(string);
3189 xmlXPathFreeObject(result);
3190 return err;
3194 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3195 * @context is session context
3196 * @owner is libisds structure with box description.
3197 * If @pfo_subtype is false, aifoIsds, address->adCode, address->adDistrict
3198 * members will be ignored. If @pfo_subtype is true, dbType, ic,
3199 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
3200 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
3201 * ignored.
3202 * @pfo_subtype is false if tDbOwnerInfo tree should be built from the @owner.
3203 * It is true if tDbPersonalOwnerInfo tree should be built from the @owner.
3204 * The tree differs in subset of significant isds_DbOwnerInfo structure members.
3205 * @db_owner_info is XML element of XSD:tDbOwnerInfo or XSD:tdbPersonalOnwerInfo
3206 * type. */
3207 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3208 const struct isds_DbOwnerInfo *owner, _Bool pfo_subtype,
3209 xmlNodePtr db_owner_info) {
3211 isds_error err = IE_SUCCESS;
3212 xmlNodePtr node;
3213 xmlChar *string = NULL;
3214 const xmlChar *type_string = NULL;
3216 if (!context) return IE_INVALID_CONTEXT;
3217 if (!owner || !db_owner_info) return IE_INVAL;
3220 /* XXX: All the elements except email and telNumber are mandatory. */
3221 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3222 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3224 if (pfo_subtype) {
3225 INSERT_BOOLEAN(db_owner_info, "aifoIsds", owner->aifoIsds);
3228 if (!pfo_subtype) {
3229 /* dbType */
3230 if (owner->dbType) {
3231 type_string = isds_DbType2string(*(owner->dbType));
3232 if (!type_string) {
3233 isds_printf_message(context, _("Invalid dbType value: %d"),
3234 *(owner->dbType));
3235 err = IE_ENUM;
3236 goto leave;
3239 INSERT_STRING(db_owner_info, "dbType", type_string);
3241 INSERT_STRING(db_owner_info, "ic", owner->ic);
3244 INSERT_STRING(db_owner_info, "pnFirstName",
3245 (NULL == owner->personName) ? NULL: owner->personName->pnFirstName);
3246 INSERT_STRING(db_owner_info, "pnMiddleName",
3247 (NULL == owner->personName) ? NULL: owner->personName->pnMiddleName);
3248 INSERT_STRING(db_owner_info, "pnLastName",
3249 (NULL == owner->personName) ? NULL: owner->personName->pnLastName);
3250 if (!pfo_subtype) {
3251 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3252 (NULL == owner->personName) ? NULL:
3253 owner->personName->pnLastNameAtBirth);
3255 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3258 if (NULL != owner->birthInfo && NULL != owner->birthInfo->biDate) {
3259 err = tm2datestring(owner->birthInfo->biDate, &string);
3260 if (err) goto leave;
3262 INSERT_STRING(db_owner_info, "biDate", string);
3263 zfree(string);
3265 INSERT_STRING(db_owner_info, "biCity",
3266 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biCity);
3267 INSERT_STRING(db_owner_info, "biCounty",
3268 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biCounty);
3269 INSERT_STRING(db_owner_info, "biState",
3270 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biState);
3272 if (pfo_subtype) {
3273 INSERT_LONGINT(db_owner_info, "adCode",
3274 (NULL == owner->address) ? NULL : owner->address->adCode,
3275 string);
3277 INSERT_STRING(db_owner_info, "adCity",
3278 (NULL == owner->address) ? NULL: owner->address->adCity);
3279 if (pfo_subtype) {
3280 INSERT_STRING(db_owner_info, "adDistrict",
3281 (NULL == owner->address) ? NULL: owner->address->adDistrict);
3283 INSERT_STRING(db_owner_info, "adStreet",
3284 (NULL == owner->address) ? NULL: owner->address->adStreet);
3285 INSERT_STRING(db_owner_info, "adNumberInStreet",
3286 (NULL == owner->address) ? NULL: owner->address->adNumberInStreet);
3287 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3288 (NULL == owner->address) ? NULL: owner->address->adNumberInMunicipality);
3289 INSERT_STRING(db_owner_info, "adZipCode",
3290 (NULL == owner->address) ? NULL: owner->address->adZipCode);
3291 INSERT_STRING(db_owner_info, "adState",
3292 (NULL == owner->address) ? NULL: owner->address->adState);
3294 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3296 if (!pfo_subtype) {
3297 INSERT_STRING(db_owner_info, "email", owner->email);
3298 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3300 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3301 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3303 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3304 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3306 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3308 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3309 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3310 owner->dbOpenAddressing);
3313 leave:
3314 free(string);
3315 return err;
3319 /* Convert XSD:tDbUserInfo XML tree into structure
3320 * @context is ISDS context
3321 * @db_user_info is automatically reallocated user info structure
3322 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3323 * In case of error @db_user_info will be freed. */
3324 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3325 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3326 isds_error err = IE_SUCCESS;
3327 xmlXPathObjectPtr result = NULL;
3328 char *string = NULL;
3330 if (!context) return IE_INVALID_CONTEXT;
3331 if (!db_user_info) return IE_INVAL;
3332 isds_DbUserInfo_free(db_user_info);
3333 if (!xpath_ctx) return IE_INVAL;
3336 *db_user_info = calloc(1, sizeof(**db_user_info));
3337 if (!*db_user_info) {
3338 err = IE_NOMEM;
3339 goto leave;
3342 EXTRACT_STRING_ATTRIBUTE("AIFOTicket", (*db_user_info)->aifo_ticket, 0);
3344 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3346 EXTRACT_STRING("isds:userType", string);
3347 if (string) {
3348 (*db_user_info)->userType =
3349 calloc(1, sizeof(*((*db_user_info)->userType)));
3350 if (!(*db_user_info)->userType) {
3351 err = IE_NOMEM;
3352 goto leave;
3354 err = string2isds_UserType((xmlChar *)string,
3355 (*db_user_info)->userType);
3356 if (err) {
3357 zfree((*db_user_info)->userType);
3358 if (err == IE_ENUM) {
3359 err = IE_ISDS;
3360 char *string_locale = _isds_utf82locale(string);
3361 isds_printf_message(context,
3362 _("Unknown isds:userType value: %s"), string_locale);
3363 free(string_locale);
3365 goto leave;
3367 zfree(string);
3370 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3372 (*db_user_info)->personName =
3373 calloc(1, sizeof(*((*db_user_info)->personName)));
3374 if (!(*db_user_info)->personName) {
3375 err = IE_NOMEM;
3376 goto leave;
3379 err = extract_gPersonName(context, &(*db_user_info)->personName,
3380 xpath_ctx);
3381 if (err) goto leave;
3383 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3384 if (err) goto leave;
3386 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3387 if (err) goto leave;
3389 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3390 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3392 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3393 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3394 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3396 /* ???: Default value is "CZ" according specification. Should we provide
3397 * it? */
3398 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3400 leave:
3401 if (err) isds_DbUserInfo_free(db_user_info);
3402 free(string);
3403 xmlXPathFreeObject(result);
3404 return err;
3408 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3409 * @context is session context
3410 * @user is libisds structure with user description
3411 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
3412 * @db_user_info is XML element of XSD:tDbUserInfo */
3413 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3414 const struct isds_DbUserInfo *user, _Bool honor_aifo_ticket,
3415 xmlNodePtr db_user_info) {
3417 isds_error err = IE_SUCCESS;
3418 xmlNodePtr node;
3419 xmlAttrPtr attribute_node;
3420 xmlChar *string = NULL;
3422 if (!context) return IE_INVALID_CONTEXT;
3423 if (!user || !db_user_info) return IE_INVAL;
3425 /* Build XSD:tDbUserInfo */
3427 /* XXX: Insert AIFOTicket attribute only when allowed. XML schema does not
3428 * allow it everywhere. */
3429 if (honor_aifo_ticket && user->aifo_ticket) {
3430 INSERT_STRING_ATTRIBUTE(db_user_info, "AIFOTicket", user->aifo_ticket);
3433 if (user->personName) {
3434 INSERT_STRING(db_user_info, "pnFirstName",
3435 user->personName->pnFirstName);
3436 INSERT_STRING(db_user_info, "pnMiddleName",
3437 user->personName->pnMiddleName);
3438 INSERT_STRING(db_user_info, "pnLastName",
3439 user->personName->pnLastName);
3440 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3441 user->personName->pnLastNameAtBirth);
3443 if (user->address) {
3444 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3445 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3446 INSERT_STRING(db_user_info, "adNumberInStreet",
3447 user->address->adNumberInStreet);
3448 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3449 user->address->adNumberInMunicipality);
3450 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3451 INSERT_STRING(db_user_info, "adState", user->address->adState);
3453 if (user->biDate) {
3454 if (!tm2datestring(user->biDate, &string))
3455 INSERT_STRING(db_user_info, "biDate", string);
3456 zfree(string);
3458 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3459 INSERT_STRING(db_user_info, "userID", user->userID);
3461 /* userType */
3462 if (user->userType) {
3463 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3464 if (!type_string) {
3465 isds_printf_message(context, _("Invalid userType value: %d"),
3466 *(user->userType));
3467 err = IE_ENUM;
3468 goto leave;
3470 INSERT_STRING(db_user_info, "userType", type_string);
3473 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3474 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3475 INSERT_STRING(db_user_info, "ic", user->ic);
3476 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3477 INSERT_STRING(db_user_info, "firmName", user->firmName);
3478 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3479 INSERT_STRING(db_user_info, "caCity", user->caCity);
3480 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3481 INSERT_STRING(db_user_info, "caState", user->caState);
3483 leave:
3484 free(string);
3485 return err;
3489 /* Convert XSD:tPDZRec XML tree into structure
3490 * @context is ISDS context
3491 * @permission is automatically reallocated commercial permission structure
3492 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3493 * In case of error @permission will be freed. */
3494 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3495 struct isds_commercial_permission **permission,
3496 xmlXPathContextPtr xpath_ctx) {
3497 isds_error err = IE_SUCCESS;
3498 xmlXPathObjectPtr result = NULL;
3499 char *string = NULL;
3501 if (!context) return IE_INVALID_CONTEXT;
3502 if (!permission) return IE_INVAL;
3503 isds_commercial_permission_free(permission);
3504 if (!xpath_ctx) return IE_INVAL;
3507 *permission = calloc(1, sizeof(**permission));
3508 if (!*permission) {
3509 err = IE_NOMEM;
3510 goto leave;
3513 EXTRACT_STRING("isds:PDZType", string);
3514 if (string) {
3515 err = string2isds_payment_type((xmlChar *)string,
3516 &(*permission)->type);
3517 if (err) {
3518 if (err == IE_ENUM) {
3519 err = IE_ISDS;
3520 char *string_locale = _isds_utf82locale(string);
3521 isds_printf_message(context,
3522 _("Unknown isds:PDZType value: %s"), string_locale);
3523 free(string_locale);
3525 goto leave;
3527 zfree(string);
3530 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3531 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3533 EXTRACT_STRING("isds:PDZExpire", string);
3534 if (string) {
3535 err = timestring2timeval((xmlChar *) string,
3536 &((*permission)->expiration));
3537 if (err) {
3538 char *string_locale = _isds_utf82locale(string);
3539 if (err == IE_DATE) err = IE_ISDS;
3540 isds_printf_message(context,
3541 _("Could not convert PDZExpire as ISO time: %s"),
3542 string_locale);
3543 free(string_locale);
3544 goto leave;
3546 zfree(string);
3549 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3550 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3552 leave:
3553 if (err) isds_commercial_permission_free(permission);
3554 free(string);
3555 xmlXPathFreeObject(result);
3556 return err;
3560 /* Convert XSD:tCiRecord XML tree into structure
3561 * @context is ISDS context
3562 * @event is automatically reallocated commercial credit event structure
3563 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3564 * In case of error @event will be freed. */
3565 static isds_error extract_CiRecord(struct isds_ctx *context,
3566 struct isds_credit_event **event,
3567 xmlXPathContextPtr xpath_ctx) {
3568 isds_error err = IE_SUCCESS;
3569 xmlXPathObjectPtr result = NULL;
3570 char *string = NULL;
3571 long int *number_ptr;
3573 if (!context) return IE_INVALID_CONTEXT;
3574 if (!event) return IE_INVAL;
3575 isds_credit_event_free(event);
3576 if (!xpath_ctx) return IE_INVAL;
3579 *event = calloc(1, sizeof(**event));
3580 if (!*event) {
3581 err = IE_NOMEM;
3582 goto leave;
3585 EXTRACT_STRING("isds:ciEventTime", string);
3586 if (string) {
3587 err = timestring2timeval((xmlChar *) string,
3588 &(*event)->time);
3589 if (err) {
3590 char *string_locale = _isds_utf82locale(string);
3591 if (err == IE_DATE) err = IE_ISDS;
3592 isds_printf_message(context,
3593 _("Could not convert ciEventTime as ISO time: %s"),
3594 string_locale);
3595 free(string_locale);
3596 goto leave;
3598 zfree(string);
3601 EXTRACT_STRING("isds:ciEventType", string);
3602 if (string) {
3603 err = string2isds_credit_event_type((xmlChar *)string,
3604 &(*event)->type);
3605 if (err) {
3606 if (err == IE_ENUM) {
3607 err = IE_ISDS;
3608 char *string_locale = _isds_utf82locale(string);
3609 isds_printf_message(context,
3610 _("Unknown isds:ciEventType value: %s"), string_locale);
3611 free(string_locale);
3613 goto leave;
3615 zfree(string);
3618 number_ptr = &((*event)->credit_change);
3619 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3620 number_ptr = &(*event)->new_credit;
3621 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3623 switch((*event)->type) {
3624 case ISDS_CREDIT_CHARGED:
3625 EXTRACT_STRING("isds:ciTransID",
3626 (*event)->details.charged.transaction);
3627 break;
3628 case ISDS_CREDIT_DISCHARGED:
3629 EXTRACT_STRING("isds:ciTransID",
3630 (*event)->details.discharged.transaction);
3631 break;
3632 case ISDS_CREDIT_MESSAGE_SENT:
3633 EXTRACT_STRING("isds:ciRecipientID",
3634 (*event)->details.message_sent.recipient);
3635 EXTRACT_STRING("isds:ciPDZID",
3636 (*event)->details.message_sent.message_id);
3637 break;
3638 case ISDS_CREDIT_STORAGE_SET:
3639 number_ptr = &((*event)->details.storage_set.new_capacity);
3640 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3641 EXTRACT_DATE("isds:ciNewFrom",
3642 (*event)->details.storage_set.new_valid_from);
3643 EXTRACT_DATE("isds:ciNewTo",
3644 (*event)->details.storage_set.new_valid_to);
3645 EXTRACT_LONGINT("isds:ciOldCapacity",
3646 (*event)->details.storage_set.old_capacity, 0);
3647 EXTRACT_DATE("isds:ciOldFrom",
3648 (*event)->details.storage_set.old_valid_from);
3649 EXTRACT_DATE("isds:ciOldTo",
3650 (*event)->details.storage_set.old_valid_to);
3651 EXTRACT_STRING("isds:ciDoneBy",
3652 (*event)->details.storage_set.initiator);
3653 break;
3654 case ISDS_CREDIT_EXPIRED:
3655 break;
3658 leave:
3659 if (err) isds_credit_event_free(event);
3660 free(string);
3661 xmlXPathFreeObject(result);
3662 return err;
3666 #endif /* HAVE_LIBCURL */
3669 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3670 * isds_envelope structure. The envelope is automatically allocated but not
3671 * reallocated. The date are just appended into envelope structure.
3672 * @context is ISDS context
3673 * @envelope is automatically allocated message envelope structure
3674 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3675 * In case of error @envelope will be freed. */
3676 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3677 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3678 isds_error err = IE_SUCCESS;
3679 xmlXPathObjectPtr result = NULL;
3681 if (!context) return IE_INVALID_CONTEXT;
3682 if (!envelope) return IE_INVAL;
3683 if (!xpath_ctx) return IE_INVAL;
3686 if (!*envelope) {
3687 /* Allocate envelope */
3688 *envelope = calloc(1, sizeof(**envelope));
3689 if (!*envelope) {
3690 err = IE_NOMEM;
3691 goto leave;
3693 } else {
3694 /* Else free former data */
3695 zfree((*envelope)->dmSenderOrgUnit);
3696 zfree((*envelope)->dmSenderOrgUnitNum);
3697 zfree((*envelope)->dbIDRecipient);
3698 zfree((*envelope)->dmRecipientOrgUnit);
3699 zfree((*envelope)->dmRecipientOrgUnitNum);
3700 zfree((*envelope)->dmToHands);
3701 zfree((*envelope)->dmAnnotation);
3702 zfree((*envelope)->dmRecipientRefNumber);
3703 zfree((*envelope)->dmSenderRefNumber);
3704 zfree((*envelope)->dmRecipientIdent);
3705 zfree((*envelope)->dmSenderIdent);
3706 zfree((*envelope)->dmLegalTitleLaw);
3707 zfree((*envelope)->dmLegalTitleYear);
3708 zfree((*envelope)->dmLegalTitleSect);
3709 zfree((*envelope)->dmLegalTitlePar);
3710 zfree((*envelope)->dmLegalTitlePoint);
3711 zfree((*envelope)->dmPersonalDelivery);
3712 zfree((*envelope)->dmAllowSubstDelivery);
3715 /* Extract envelope elements added by sender or ISDS
3716 * (XSD: gMessageEnvelopeSub type) */
3717 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3718 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3719 (*envelope)->dmSenderOrgUnitNum, 0);
3720 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3721 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3722 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3723 (*envelope)->dmRecipientOrgUnitNum, 0);
3724 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3725 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3726 EXTRACT_STRING("isds:dmRecipientRefNumber",
3727 (*envelope)->dmRecipientRefNumber);
3728 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3729 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3730 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3732 /* Extract envelope elements regarding law reference */
3733 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3734 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3735 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3736 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3737 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3739 /* Extract envelope other elements */
3740 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3741 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3742 (*envelope)->dmAllowSubstDelivery);
3744 leave:
3745 if (err) isds_envelope_free(envelope);
3746 xmlXPathFreeObject(result);
3747 return err;
3752 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3753 * isds_envelope structure. The envelope is automatically allocated but not
3754 * reallocated. The date are just appended into envelope structure.
3755 * @context is ISDS context
3756 * @envelope is automatically allocated message envelope structure
3757 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3758 * In case of error @envelope will be freed. */
3759 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3760 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3761 isds_error err = IE_SUCCESS;
3762 xmlXPathObjectPtr result = NULL;
3764 if (!context) return IE_INVALID_CONTEXT;
3765 if (!envelope) return IE_INVAL;
3766 if (!xpath_ctx) return IE_INVAL;
3769 if (!*envelope) {
3770 /* Allocate envelope */
3771 *envelope = calloc(1, sizeof(**envelope));
3772 if (!*envelope) {
3773 err = IE_NOMEM;
3774 goto leave;
3776 } else {
3777 /* Else free former data */
3778 zfree((*envelope)->dmID);
3779 zfree((*envelope)->dbIDSender);
3780 zfree((*envelope)->dmSender);
3781 zfree((*envelope)->dmSenderAddress);
3782 zfree((*envelope)->dmSenderType);
3783 zfree((*envelope)->dmRecipient);
3784 zfree((*envelope)->dmRecipientAddress);
3785 zfree((*envelope)->dmAmbiguousRecipient);
3788 /* Extract envelope elements added by ISDS
3789 * (XSD: gMessageEnvelope type) */
3790 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3791 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3792 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3793 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3794 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3795 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3796 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3797 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3798 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3799 (*envelope)->dmAmbiguousRecipient);
3801 /* Extract envelope elements added by sender and ISDS
3802 * (XSD: gMessageEnvelope type) */
3803 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3804 if (err) goto leave;
3806 leave:
3807 if (err) isds_envelope_free(envelope);
3808 xmlXPathFreeObject(result);
3809 return err;
3813 /* Convert other envelope elements from XML tree into isds_envelope structure:
3814 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3815 * The envelope is automatically allocated but not reallocated.
3816 * The data are just appended into envelope structure.
3817 * @context is ISDS context
3818 * @envelope is automatically allocated message envelope structure
3819 * @xpath_ctx is XPath context with current node as parent desired elements
3820 * In case of error @envelope will be freed. */
3821 static isds_error append_status_size_times(struct isds_ctx *context,
3822 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3823 isds_error err = IE_SUCCESS;
3824 xmlXPathObjectPtr result = NULL;
3825 char *string = NULL;
3826 unsigned long int *unumber = NULL;
3828 if (!context) return IE_INVALID_CONTEXT;
3829 if (!envelope) return IE_INVAL;
3830 if (!xpath_ctx) return IE_INVAL;
3833 if (!*envelope) {
3834 /* Allocate new */
3835 *envelope = calloc(1, sizeof(**envelope));
3836 if (!*envelope) {
3837 err = IE_NOMEM;
3838 goto leave;
3840 } else {
3841 /* Free old data */
3842 zfree((*envelope)->dmMessageStatus);
3843 zfree((*envelope)->dmAttachmentSize);
3844 zfree((*envelope)->dmDeliveryTime);
3845 zfree((*envelope)->dmAcceptanceTime);
3849 /* dmMessageStatus element is mandatory */
3850 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3851 if (!unumber) {
3852 isds_log_message(context,
3853 _("Missing mandatory sisds:dmMessageStatus integer"));
3854 err = IE_ISDS;
3855 goto leave;
3857 err = uint2isds_message_status(context, unumber,
3858 &((*envelope)->dmMessageStatus));
3859 if (err) {
3860 if (err == IE_ENUM) err = IE_ISDS;
3861 goto leave;
3863 free(unumber); unumber = NULL;
3865 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3868 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3869 if (string) {
3870 err = timestring2timeval((xmlChar *) string,
3871 &((*envelope)->dmDeliveryTime));
3872 if (err) {
3873 char *string_locale = _isds_utf82locale(string);
3874 if (err == IE_DATE) err = IE_ISDS;
3875 isds_printf_message(context,
3876 _("Could not convert dmDeliveryTime as ISO time: %s"),
3877 string_locale);
3878 free(string_locale);
3879 goto leave;
3881 zfree(string);
3884 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3885 if (string) {
3886 err = timestring2timeval((xmlChar *) string,
3887 &((*envelope)->dmAcceptanceTime));
3888 if (err) {
3889 char *string_locale = _isds_utf82locale(string);
3890 if (err == IE_DATE) err = IE_ISDS;
3891 isds_printf_message(context,
3892 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3893 string_locale);
3894 free(string_locale);
3895 goto leave;
3897 zfree(string);
3900 leave:
3901 if (err) isds_envelope_free(envelope);
3902 free(unumber);
3903 free(string);
3904 xmlXPathFreeObject(result);
3905 return err;
3909 /* Convert message type attribute of current element into isds_envelope
3910 * structure.
3911 * TODO: This function can be incorporated into append_status_size_times() as
3912 * they are called always together.
3913 * The envelope is automatically allocated but not reallocated.
3914 * The data are just appended into envelope structure.
3915 * @context is ISDS context
3916 * @envelope is automatically allocated message envelope structure
3917 * @xpath_ctx is XPath context with current node as parent of attribute
3918 * carrying message type
3919 * In case of error @envelope will be freed. */
3920 static isds_error append_message_type(struct isds_ctx *context,
3921 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3922 isds_error err = IE_SUCCESS;
3924 if (!context) return IE_INVALID_CONTEXT;
3925 if (!envelope) return IE_INVAL;
3926 if (!xpath_ctx) return IE_INVAL;
3929 if (!*envelope) {
3930 /* Allocate new */
3931 *envelope = calloc(1, sizeof(**envelope));
3932 if (!*envelope) {
3933 err = IE_NOMEM;
3934 goto leave;
3936 } else {
3937 /* Free old data */
3938 zfree((*envelope)->dmType);
3942 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3944 if (!(*envelope)->dmType) {
3945 /* Use default value */
3946 (*envelope)->dmType = strdup("V");
3947 if (!(*envelope)->dmType) {
3948 err = IE_NOMEM;
3949 goto leave;
3951 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3952 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3953 isds_printf_message(context,
3954 _("Message type in dmType attribute is not 1 character long: "
3955 "%s"),
3956 type_locale);
3957 free(type_locale);
3958 err = IE_ISDS;
3959 goto leave;
3962 leave:
3963 if (err) isds_envelope_free(envelope);
3964 return err;
3968 #if HAVE_LIBCURL
3969 /* Convert dmType isds_envelope member into XML attribute and append it to
3970 * current node.
3971 * @context is ISDS context
3972 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3973 * @dm_envelope is XML element the resulting attribute will be appended to.
3974 * @return error code, in case of error context' message is filled. */
3975 static isds_error insert_message_type(struct isds_ctx *context,
3976 const char *type, xmlNodePtr dm_envelope) {
3977 isds_error err = IE_SUCCESS;
3978 xmlAttrPtr attribute_node;
3980 if (!context) return IE_INVALID_CONTEXT;
3981 if (!dm_envelope) return IE_INVAL;
3983 /* Insert optional message type */
3984 if (type) {
3985 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3986 char *type_locale = _isds_utf82locale(type);
3987 isds_printf_message(context,
3988 _("Message type in envelope is not 1 character long: %s"),
3989 type_locale);
3990 free(type_locale);
3991 err = IE_INVAL;
3992 goto leave;
3994 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3997 leave:
3998 return err;
4000 #endif /* HAVE_LIBCURL */
4003 /* Extract message document into reallocated document structure
4004 * @context is ISDS context
4005 * @document is automatically reallocated message documents structure
4006 * @xpath_ctx is XPath context with current node as isds:dmFile
4007 * In case of error @document will be freed. */
4008 static isds_error extract_document(struct isds_ctx *context,
4009 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
4010 isds_error err = IE_SUCCESS;
4011 xmlXPathObjectPtr result = NULL;
4012 xmlNodePtr file_node;
4013 char *string = NULL;
4015 if (!context) return IE_INVALID_CONTEXT;
4016 if (!document) return IE_INVAL;
4017 isds_document_free(document);
4018 if (!xpath_ctx) return IE_INVAL;
4019 file_node = xpath_ctx->node;
4021 *document = calloc(1, sizeof(**document));
4022 if (!*document) {
4023 err = IE_NOMEM;
4024 goto leave;
4027 /* Extract document meta data */
4028 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
4029 if (context->normalize_mime_type) {
4030 const char *normalized_type =
4031 isds_normalize_mime_type((*document)->dmMimeType);
4032 if (NULL != normalized_type &&
4033 normalized_type != (*document)->dmMimeType) {
4034 char *new_type = strdup(normalized_type);
4035 if (NULL == new_type) {
4036 isds_printf_message(context,
4037 _("Not enough memory to normalize document MIME type"));
4038 err = IE_NOMEM;
4039 goto leave;
4041 free((*document)->dmMimeType);
4042 (*document)->dmMimeType = new_type;
4046 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
4047 err = string2isds_FileMetaType((xmlChar*)string,
4048 &((*document)->dmFileMetaType));
4049 if (err) {
4050 char *meta_type_locale = _isds_utf82locale(string);
4051 isds_printf_message(context,
4052 _("Document has invalid dmFileMetaType attribute value: %s"),
4053 meta_type_locale);
4054 free(meta_type_locale);
4055 err = IE_ISDS;
4056 goto leave;
4058 zfree(string);
4060 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
4061 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
4062 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
4063 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
4066 /* Extract document data.
4067 * Base64 encoded blob or XML subtree must be presented. */
4069 /* Check for dmEncodedContent */
4070 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
4071 xpath_ctx);
4072 if (!result) {
4073 err = IE_XML;
4074 goto leave;
4077 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4078 /* Here we have Base64 blob */
4079 (*document)->is_xml = 0;
4081 if (result->nodesetval->nodeNr > 1) {
4082 isds_printf_message(context,
4083 _("Document has more dmEncodedContent elements"));
4084 err = IE_ISDS;
4085 goto leave;
4088 xmlXPathFreeObject(result); result = NULL;
4089 EXTRACT_STRING("isds:dmEncodedContent", string);
4091 /* Decode non-empty document */
4092 if (string && string[0] != '\0') {
4093 (*document)->data_length =
4094 _isds_b64decode(string, &((*document)->data));
4095 if ((*document)->data_length == (size_t) -1) {
4096 isds_printf_message(context,
4097 _("Error while Base64-decoding document content"));
4098 err = IE_ERROR;
4099 goto leave;
4102 } else {
4103 /* No Base64 blob, try XML document */
4104 xmlXPathFreeObject(result); result = NULL;
4105 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
4106 xpath_ctx);
4107 if (!result) {
4108 err = IE_XML;
4109 goto leave;
4112 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4113 /* Here we have XML document */
4114 (*document)->is_xml = 1;
4116 if (result->nodesetval->nodeNr > 1) {
4117 isds_printf_message(context,
4118 _("Document has more dmXMLContent elements"));
4119 err = IE_ISDS;
4120 goto leave;
4123 /* XXX: We cannot serialize the content simply because:
4124 * - XML document may point out of its scope (e.g. to message
4125 * envelope)
4126 * - isds:dmXMLContent can contain more elements, no element,
4127 * a text node only
4128 * - it's not the XML way
4129 * Thus we provide the only right solution: XML DOM. Let's
4130 * application to cope with this hot potato :) */
4131 (*document)->xml_node_list =
4132 result->nodesetval->nodeTab[0]->children;
4133 } else {
4134 /* No base64 blob, nor XML document */
4135 isds_printf_message(context,
4136 _("Document has no dmEncodedContent, nor dmXMLContent "
4137 "element"));
4138 err = IE_ISDS;
4139 goto leave;
4144 leave:
4145 if (err) isds_document_free(document);
4146 free(string);
4147 xmlXPathFreeObject(result);
4148 xpath_ctx->node = file_node;
4149 return err;
4154 /* Extract message documents into reallocated list of documents
4155 * @context is ISDS context
4156 * @documents is automatically reallocated message documents list structure
4157 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4158 * In case of error @documents will be freed. */
4159 static isds_error extract_documents(struct isds_ctx *context,
4160 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
4161 isds_error err = IE_SUCCESS;
4162 xmlXPathObjectPtr result = NULL;
4163 xmlNodePtr files_node;
4164 struct isds_list *document, *prev_document = NULL;
4166 if (!context) return IE_INVALID_CONTEXT;
4167 if (!documents) return IE_INVAL;
4168 isds_list_free(documents);
4169 if (!xpath_ctx) return IE_INVAL;
4170 files_node = xpath_ctx->node;
4172 /* Find documents */
4173 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
4174 if (!result) {
4175 err = IE_XML;
4176 goto leave;
4179 /* No match */
4180 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4181 isds_printf_message(context,
4182 _("Message does not contain any document"));
4183 err = IE_ISDS;
4184 goto leave;
4188 /* Iterate over documents */
4189 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4191 /* Allocate and append list item */
4192 document = calloc(1, sizeof(*document));
4193 if (!document) {
4194 err = IE_NOMEM;
4195 goto leave;
4197 document->destructor = (void (*)(void **))isds_document_free;
4198 if (i == 0) *documents = document;
4199 else prev_document->next = document;
4200 prev_document = document;
4202 /* Extract document */
4203 xpath_ctx->node = result->nodesetval->nodeTab[i];
4204 err = extract_document(context,
4205 (struct isds_document **) &(document->data), xpath_ctx);
4206 if (err) goto leave;
4210 leave:
4211 if (err) isds_list_free(documents);
4212 xmlXPathFreeObject(result);
4213 xpath_ctx->node = files_node;
4214 return err;
4218 #if HAVE_LIBCURL
4219 /* Convert isds:dmRecord XML tree into structure
4220 * @context is ISDS context
4221 * @envelope is automatically reallocated message envelope structure
4222 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4223 * In case of error @envelope will be freed. */
4224 static isds_error extract_DmRecord(struct isds_ctx *context,
4225 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4226 isds_error err = IE_SUCCESS;
4227 xmlXPathObjectPtr result = NULL;
4229 if (!context) return IE_INVALID_CONTEXT;
4230 if (!envelope) return IE_INVAL;
4231 isds_envelope_free(envelope);
4232 if (!xpath_ctx) return IE_INVAL;
4235 *envelope = calloc(1, sizeof(**envelope));
4236 if (!*envelope) {
4237 err = IE_NOMEM;
4238 goto leave;
4242 /* Extract tRecord data */
4243 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4245 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4246 * dmAcceptanceTime. */
4247 err = append_status_size_times(context, envelope, xpath_ctx);
4248 if (err) goto leave;
4250 /* Extract envelope elements added by sender and ISDS
4251 * (XSD: gMessageEnvelope type) */
4252 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4253 if (err) goto leave;
4255 /* Get message type */
4256 err = append_message_type(context, envelope, xpath_ctx);
4257 if (err) goto leave;
4260 leave:
4261 if (err) isds_envelope_free(envelope);
4262 xmlXPathFreeObject(result);
4263 return err;
4267 /* Convert XSD:tStateChangesRecord type XML tree into structure
4268 * @context is ISDS context
4269 * @changed_status is automatically reallocated message state change structure
4270 * @xpath_ctx is XPath context with current node as element of
4271 * XSD:tStateChangesRecord type
4272 * In case of error @changed_status will be freed. */
4273 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4274 struct isds_message_status_change **changed_status,
4275 xmlXPathContextPtr xpath_ctx) {
4276 isds_error err = IE_SUCCESS;
4277 xmlXPathObjectPtr result = NULL;
4278 unsigned long int *unumber = NULL;
4279 char *string = NULL;
4281 if (!context) return IE_INVALID_CONTEXT;
4282 if (!changed_status) return IE_INVAL;
4283 isds_message_status_change_free(changed_status);
4284 if (!xpath_ctx) return IE_INVAL;
4287 *changed_status = calloc(1, sizeof(**changed_status));
4288 if (!*changed_status) {
4289 err = IE_NOMEM;
4290 goto leave;
4294 /* Extract tGetStateChangesInput data */
4295 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4297 /* dmEventTime is mandatory */
4298 EXTRACT_STRING("isds:dmEventTime", string);
4299 if (string) {
4300 err = timestring2timeval((xmlChar *) string,
4301 &((*changed_status)->time));
4302 if (err) {
4303 char *string_locale = _isds_utf82locale(string);
4304 if (err == IE_DATE) err = IE_ISDS;
4305 isds_printf_message(context,
4306 _("Could not convert dmEventTime as ISO time: %s"),
4307 string_locale);
4308 free(string_locale);
4309 goto leave;
4311 zfree(string);
4314 /* dmMessageStatus element is mandatory */
4315 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4316 if (!unumber) {
4317 isds_log_message(context,
4318 _("Missing mandatory isds:dmMessageStatus integer"));
4319 err = IE_ISDS;
4320 goto leave;
4322 err = uint2isds_message_status(context, unumber,
4323 &((*changed_status)->dmMessageStatus));
4324 if (err) {
4325 if (err == IE_ENUM) err = IE_ISDS;
4326 goto leave;
4328 zfree(unumber);
4331 leave:
4332 free(unumber);
4333 free(string);
4334 if (err) isds_message_status_change_free(changed_status);
4335 xmlXPathFreeObject(result);
4336 return err;
4338 #endif /* HAVE_LIBCURL */
4341 /* Find and convert isds:dmHash XML tree into structure
4342 * @context is ISDS context
4343 * @envelope is automatically reallocated message hash structure
4344 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4345 * In case of error @hash will be freed. */
4346 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4347 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4348 isds_error err = IE_SUCCESS;
4349 xmlNodePtr old_ctx_node;
4350 xmlXPathObjectPtr result = NULL;
4351 char *string = NULL;
4353 if (!context) return IE_INVALID_CONTEXT;
4354 if (!hash) return IE_INVAL;
4355 isds_hash_free(hash);
4356 if (!xpath_ctx) return IE_INVAL;
4358 old_ctx_node = xpath_ctx->node;
4360 *hash = calloc(1, sizeof(**hash));
4361 if (!*hash) {
4362 err = IE_NOMEM;
4363 goto leave;
4366 /* Locate dmHash */
4367 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4368 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4369 err = IE_ISDS;
4370 goto leave;
4372 if (err) {
4373 err = IE_ERROR;
4374 goto leave;
4377 /* Get hash algorithm */
4378 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4379 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4380 if (err) {
4381 if (err == IE_ENUM) {
4382 char *string_locale = _isds_utf82locale(string);
4383 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4384 string_locale);
4385 free(string_locale);
4387 goto leave;
4389 zfree(string);
4391 /* Get hash value */
4392 EXTRACT_STRING(".", string);
4393 if (!string) {
4394 isds_printf_message(context,
4395 _("sisds:dmHash element is missing hash value"));
4396 err = IE_ISDS;
4397 goto leave;
4399 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4400 if ((*hash)->length == (size_t) -1) {
4401 isds_printf_message(context,
4402 _("Error while Base64-decoding hash value"));
4403 err = IE_ERROR;
4404 goto leave;
4407 leave:
4408 if (err) isds_hash_free(hash);
4409 free(string);
4410 xmlXPathFreeObject(result);
4411 xpath_ctx->node = old_ctx_node;
4412 return err;
4416 /* Find and append isds:dmQTimestamp XML tree into envelope.
4417 * Because one service is allowed to miss time-stamp content, and we think
4418 * other could too (flaw in specification), this function is deliberated and
4419 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4420 * @context is ISDS context
4421 * @envelope is automatically allocated envelope structure
4422 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4423 * child
4424 * In case of error @envelope will be freed. */
4425 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4426 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4427 isds_error err = IE_SUCCESS;
4428 xmlXPathObjectPtr result = NULL;
4429 char *string = NULL;
4431 if (!context) return IE_INVALID_CONTEXT;
4432 if (!envelope) return IE_INVAL;
4433 if (!xpath_ctx) {
4434 isds_envelope_free(envelope);
4435 return IE_INVAL;
4438 if (!*envelope) {
4439 *envelope = calloc(1, sizeof(**envelope));
4440 if (!*envelope) {
4441 err = IE_NOMEM;
4442 goto leave;
4444 } else {
4445 zfree((*envelope)->timestamp);
4446 (*envelope)->timestamp_length = 0;
4449 /* Get dmQTimestamp */
4450 EXTRACT_STRING("sisds:dmQTimestamp", string);
4451 if (!string) {
4452 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4453 goto leave;
4455 (*envelope)->timestamp_length =
4456 _isds_b64decode(string, &((*envelope)->timestamp));
4457 if ((*envelope)->timestamp_length == (size_t) -1) {
4458 isds_printf_message(context,
4459 _("Error while Base64-decoding time stamp value"));
4460 err = IE_ERROR;
4461 goto leave;
4464 leave:
4465 if (err) isds_envelope_free(envelope);
4466 free(string);
4467 xmlXPathFreeObject(result);
4468 return err;
4472 /* Convert XSD tReturnedMessage XML tree into message structure.
4473 * It does not store serialized XML tree into message->raw.
4474 * It does store (pointer to) parsed XML tree into message->xml if needed.
4475 * @context is ISDS context
4476 * @include_documents Use true if documents must be extracted
4477 * (tReturnedMessage XSD type), use false if documents shall be omitted
4478 * (tReturnedMessageEnvelope).
4479 * @message is automatically reallocated message structure
4480 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4481 * type
4482 * In case of error @message will be freed. */
4483 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4484 const _Bool include_documents, struct isds_message **message,
4485 xmlXPathContextPtr xpath_ctx) {
4486 isds_error err = IE_SUCCESS;
4487 xmlNodePtr message_node;
4489 if (!context) return IE_INVALID_CONTEXT;
4490 if (!message) return IE_INVAL;
4491 isds_message_free(message);
4492 if (!xpath_ctx) return IE_INVAL;
4495 *message = calloc(1, sizeof(**message));
4496 if (!*message) {
4497 err = IE_NOMEM;
4498 goto leave;
4501 /* Save message XPATH context node */
4502 message_node = xpath_ctx->node;
4505 /* Extract dmDM */
4506 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4507 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4508 if (err) { err = IE_ERROR; goto leave; }
4509 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4510 if (err) goto leave;
4512 if (include_documents) {
4513 struct isds_list *item;
4515 /* Extract dmFiles */
4516 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4517 xpath_ctx);
4518 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4519 err = IE_ISDS; goto leave;
4521 if (err) { err = IE_ERROR; goto leave; }
4522 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4523 if (err) goto leave;
4525 /* Store xmlDoc of this message if needed */
4526 /* Only if we got a XML document in all the documents. */
4527 for (item = (*message)->documents; item; item = item->next) {
4528 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4529 (*message)->xml = xpath_ctx->doc;
4530 break;
4536 /* Restore context to message */
4537 xpath_ctx->node = message_node;
4539 /* Extract dmHash */
4540 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4541 xpath_ctx);
4542 if (err) goto leave;
4544 /* Extract dmQTimestamp, */
4545 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4546 xpath_ctx);
4547 if (err) goto leave;
4549 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4550 * dmAcceptanceTime. */
4551 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4552 if (err) goto leave;
4554 /* Get message type */
4555 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4556 if (err) goto leave;
4558 leave:
4559 if (err) isds_message_free(message);
4560 return err;
4564 /* Extract message event into reallocated isds_event structure
4565 * @context is ISDS context
4566 * @event is automatically reallocated message event structure
4567 * @xpath_ctx is XPath context with current node as isds:dmEvent
4568 * In case of error @event will be freed. */
4569 static isds_error extract_event(struct isds_ctx *context,
4570 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4571 isds_error err = IE_SUCCESS;
4572 xmlXPathObjectPtr result = NULL;
4573 xmlNodePtr event_node;
4574 char *string = NULL;
4576 if (!context) return IE_INVALID_CONTEXT;
4577 if (!event) return IE_INVAL;
4578 isds_event_free(event);
4579 if (!xpath_ctx) return IE_INVAL;
4580 event_node = xpath_ctx->node;
4582 *event = calloc(1, sizeof(**event));
4583 if (!*event) {
4584 err = IE_NOMEM;
4585 goto leave;
4588 /* Extract event data.
4589 * All elements are optional according XSD. That's funny. */
4590 EXTRACT_STRING("sisds:dmEventTime", string);
4591 if (string) {
4592 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4593 if (err) {
4594 char *string_locale = _isds_utf82locale(string);
4595 if (err == IE_DATE) err = IE_ISDS;
4596 isds_printf_message(context,
4597 _("Could not convert dmEventTime as ISO time: %s"),
4598 string_locale);
4599 free(string_locale);
4600 goto leave;
4602 zfree(string);
4605 /* dmEventDescr element has prefix and the rest */
4606 EXTRACT_STRING("sisds:dmEventDescr", string);
4607 if (string) {
4608 err = eventstring2event((xmlChar *) string, *event);
4609 if (err) goto leave;
4610 zfree(string);
4613 leave:
4614 if (err) isds_event_free(event);
4615 free(string);
4616 xmlXPathFreeObject(result);
4617 xpath_ctx->node = event_node;
4618 return err;
4622 /* Convert element of XSD tEventsArray type from XML tree into
4623 * isds_list of isds_event's structure. The list is automatically reallocated.
4624 * @context is ISDS context
4625 * @events is automatically reallocated list of event structures
4626 * @xpath_ctx is XPath context with current node as tEventsArray
4627 * In case of error @events will be freed. */
4628 static isds_error extract_events(struct isds_ctx *context,
4629 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4630 isds_error err = IE_SUCCESS;
4631 xmlXPathObjectPtr result = NULL;
4632 xmlNodePtr events_node;
4633 struct isds_list *event, *prev_event = NULL;
4635 if (!context) return IE_INVALID_CONTEXT;
4636 if (!events) return IE_INVAL;
4637 if (!xpath_ctx) return IE_INVAL;
4638 events_node = xpath_ctx->node;
4640 /* Free old list */
4641 isds_list_free(events);
4643 /* Find events */
4644 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4645 if (!result) {
4646 err = IE_XML;
4647 goto leave;
4650 /* No match */
4651 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4652 isds_printf_message(context,
4653 _("Delivery info does not contain any event"));
4654 err = IE_ISDS;
4655 goto leave;
4659 /* Iterate over events */
4660 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4662 /* Allocate and append list item */
4663 event = calloc(1, sizeof(*event));
4664 if (!event) {
4665 err = IE_NOMEM;
4666 goto leave;
4668 event->destructor = (void (*)(void **))isds_event_free;
4669 if (i == 0) *events = event;
4670 else prev_event->next = event;
4671 prev_event = event;
4673 /* Extract event */
4674 xpath_ctx->node = result->nodesetval->nodeTab[i];
4675 err = extract_event(context,
4676 (struct isds_event **) &(event->data), xpath_ctx);
4677 if (err) goto leave;
4681 leave:
4682 if (err) isds_list_free(events);
4683 xmlXPathFreeObject(result);
4684 xpath_ctx->node = events_node;
4685 return err;
4689 #if HAVE_LIBCURL
4690 /* Insert Base64 encoded data as element with text child.
4691 * @context is session context
4692 * @parent is XML node to append @element with @data as child
4693 * @ns is XML namespace of @element, use NULL to inherit from @parent
4694 * @element is UTF-8 encoded name of new element
4695 * @data is bit stream to encode into @element
4696 * @length is size of @data in bytes
4697 * @return standard error code and fill long error message if needed */
4698 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4699 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4700 const void *data, size_t length) {
4701 isds_error err = IE_SUCCESS;
4702 xmlNodePtr node;
4704 if (!context) return IE_INVALID_CONTEXT;
4705 if (!data && length > 0) return IE_INVAL;
4706 if (!parent || !element) return IE_INVAL;
4708 xmlChar *base64data = NULL;
4709 base64data = (xmlChar *) _isds_b64encode(data, length);
4710 if (!base64data) {
4711 isds_printf_message(context,
4712 ngettext("Not enough memory to encode %zd byte into Base64",
4713 "Not enough memory to encode %zd bytes into Base64",
4714 length),
4715 length);
4716 err = IE_NOMEM;
4717 goto leave;
4719 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4721 leave:
4722 free(base64data);
4723 return err;
4727 /* Convert isds_document structure into XML tree and append to dmFiles node.
4728 * @context is session context
4729 * @document is ISDS document
4730 * @dm_files is XML element the resulting tree will be appended to as a child.
4731 * @return error code, in case of error context' message is filled. */
4732 static isds_error insert_document(struct isds_ctx *context,
4733 struct isds_document *document, xmlNodePtr dm_files) {
4734 isds_error err = IE_SUCCESS;
4735 xmlNodePtr new_file = NULL, file = NULL, node;
4736 xmlAttrPtr attribute_node;
4738 if (!context) return IE_INVALID_CONTEXT;
4739 if (!document || !dm_files) return IE_INVAL;
4741 /* Allocate new dmFile */
4742 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4743 if (!new_file) {
4744 isds_printf_message(context, _("Could not allocate main dmFile"));
4745 err = IE_ERROR;
4746 goto leave;
4748 /* Append the new dmFile.
4749 * XXX: Main document must go first */
4750 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4751 file = xmlAddPrevSibling(dm_files->children, new_file);
4752 else
4753 file = xmlAddChild(dm_files, new_file);
4755 if (!file) {
4756 xmlFreeNode(new_file); new_file = NULL;
4757 isds_printf_message(context, _("Could not add dmFile child to "
4758 "%s element"), dm_files->name);
4759 err = IE_ERROR;
4760 goto leave;
4763 /* @dmMimeType is required */
4764 if (!document->dmMimeType) {
4765 isds_log_message(context,
4766 _("Document is missing mandatory MIME type definition"));
4767 err = IE_INVAL;
4768 goto leave;
4770 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4772 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4773 if (!string) {
4774 isds_printf_message(context,
4775 _("Document has unknown dmFileMetaType: %ld"),
4776 document->dmFileMetaType);
4777 err = IE_ENUM;
4778 goto leave;
4780 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4782 if (document->dmFileGuid) {
4783 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4785 if (document->dmUpFileGuid) {
4786 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4789 /* @dmFileDescr is required */
4790 if (!document->dmFileDescr) {
4791 isds_log_message(context,
4792 _("Document is missing mandatory description (title)"));
4793 err = IE_INVAL;
4794 goto leave;
4796 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4798 if (document->dmFormat) {
4799 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4803 /* Insert content (body) of the document. */
4804 if (document->is_xml) {
4805 /* XML document requested */
4807 /* Allocate new dmXMLContent */
4808 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4809 if (!xmlcontent) {
4810 isds_printf_message(context,
4811 _("Could not allocate dmXMLContent element"));
4812 err = IE_ERROR;
4813 goto leave;
4815 /* Append it */
4816 node = xmlAddChild(file, xmlcontent);
4817 if (!node) {
4818 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4819 isds_printf_message(context,
4820 _("Could not add dmXMLContent child to %s element"),
4821 file->name);
4822 err = IE_ERROR;
4823 goto leave;
4826 /* Copy non-empty node list */
4827 if (document->xml_node_list) {
4828 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4829 document->xml_node_list);
4830 if (!content) {
4831 isds_printf_message(context,
4832 _("Not enough memory to copy XML document"));
4833 err = IE_NOMEM;
4834 goto leave;
4837 if (!xmlAddChildList(node, content)) {
4838 xmlFreeNodeList(content);
4839 isds_printf_message(context,
4840 _("Error while adding XML document into dmXMLContent"));
4841 err = IE_XML;
4842 goto leave;
4844 /* XXX: We cannot free the content here because it's part of node's
4845 * document since now. It will be freed with it automatically. */
4847 } else {
4848 /* Binary document requested */
4849 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4850 document->data, document->data_length);
4851 if (err) goto leave;
4854 leave:
4855 return err;
4859 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4860 * The copy must be preallocated, the date are just appended into structure.
4861 * @context is ISDS context
4862 * @copy is message copy structure
4863 * @xpath_ctx is XPath context with current node as tMStatus */
4864 static isds_error append_TMStatus(struct isds_ctx *context,
4865 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4866 isds_error err = IE_SUCCESS;
4867 xmlXPathObjectPtr result = NULL;
4868 char *code = NULL, *message = NULL;
4870 if (!context) return IE_INVALID_CONTEXT;
4871 if (!copy || !xpath_ctx) return IE_INVAL;
4873 /* Free old values */
4874 zfree(copy->dmStatus);
4875 zfree(copy->dmID);
4877 /* Get error specific to this copy */
4878 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4879 if (!code) {
4880 isds_log_message(context,
4881 _("Missing isds:dmStatusCode under "
4882 "XSD:tMStatus type element"));
4883 err = IE_ISDS;
4884 goto leave;
4887 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4888 /* This copy failed */
4889 copy->error = IE_ISDS;
4890 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4891 if (message) {
4892 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4893 if (!copy->dmStatus) {
4894 copy->dmStatus = code;
4895 code = NULL;
4897 } else {
4898 copy->dmStatus = code;
4899 code = NULL;
4901 } else {
4902 /* This copy succeeded. In this case only, message ID is valid */
4903 copy->error = IE_SUCCESS;
4905 EXTRACT_STRING("isds:dmID", copy->dmID);
4906 if (!copy->dmID) {
4907 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4908 "but did not returned assigned message ID\n"));
4909 err = IE_ISDS;
4913 leave:
4914 free(code);
4915 free(message);
4916 xmlXPathFreeObject(result);
4917 return err;
4921 /* Insert struct isds_approval data (box approval) into XML tree
4922 * @context is session context
4923 * @approval is libisds structure with approval description. NULL is
4924 * acceptable.
4925 * @parent is XML element to append @approval to */
4926 static isds_error insert_GExtApproval(struct isds_ctx *context,
4927 const struct isds_approval *approval, xmlNodePtr parent) {
4929 isds_error err = IE_SUCCESS;
4930 xmlNodePtr node;
4932 if (!context) return IE_INVALID_CONTEXT;
4933 if (!parent) return IE_INVAL;
4935 if (!approval) return IE_SUCCESS;
4937 /* Build XSD:gExtApproval */
4938 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4939 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4941 leave:
4942 return err;
4946 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4947 * code
4948 * @context is session context
4949 * @service_name is name of SERVICE_DB_ACCESS
4950 * @response is reallocated server SOAP body response as XML document
4951 * @raw_response is reallocated bit stream with response body. Use
4952 * NULL if you don't care
4953 * @raw_response_length is size of @raw_response in bytes
4954 * @code is reallocated ISDS status code
4955 * @status_message is reallocated ISDS status message
4956 * @return error coded from lower layer, context message will be set up
4957 * appropriately. */
4958 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4959 const xmlChar *service_name,
4960 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4961 xmlChar **code, xmlChar **status_message) {
4963 isds_error err = IE_SUCCESS;
4964 char *service_name_locale = NULL;
4965 xmlNodePtr request = NULL, node;
4966 xmlNsPtr isds_ns = NULL;
4968 if (!context) return IE_INVALID_CONTEXT;
4969 if (!service_name) return IE_INVAL;
4970 if (!response || !code || !status_message) return IE_INVAL;
4971 if (!raw_response_length && raw_response) return IE_INVAL;
4973 /* Free output argument */
4974 xmlFreeDoc(*response); *response = NULL;
4975 if (raw_response) zfree(*raw_response);
4976 zfree(*code);
4977 zfree(*status_message);
4980 /* Check if connection is established
4981 * TODO: This check should be done downstairs. */
4982 if (!context->curl) return IE_CONNECTION_CLOSED;
4984 service_name_locale = _isds_utf82locale((char*)service_name);
4985 if (!service_name_locale) {
4986 err = IE_NOMEM;
4987 goto leave;
4990 /* Build request */
4991 request = xmlNewNode(NULL, service_name);
4992 if (!request) {
4993 isds_printf_message(context,
4994 _("Could not build %s request"), service_name_locale);
4995 err = IE_ERROR;
4996 goto leave;
4998 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4999 if(!isds_ns) {
5000 isds_log_message(context, _("Could not create ISDS name space"));
5001 err = IE_ERROR;
5002 goto leave;
5004 xmlSetNs(request, isds_ns);
5007 /* Add XSD:tDummyInput child */
5008 INSERT_STRING(request, "dbDummy", NULL);
5011 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5012 service_name_locale);
5014 /* Send request */
5015 err = _isds(context, SERVICE_DB_ACCESS, request, response,
5016 raw_response, raw_response_length);
5017 xmlFreeNode(request); request = NULL;
5019 if (err) {
5020 isds_log(ILF_ISDS, ILL_DEBUG,
5021 _("Processing ISDS response on %s request failed\n"),
5022 service_name_locale);
5023 goto leave;
5026 /* Check for response status */
5027 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
5028 code, status_message, NULL);
5029 if (err) {
5030 isds_log(ILF_ISDS, ILL_DEBUG,
5031 _("ISDS response on %s request is missing status\n"),
5032 service_name_locale);
5033 goto leave;
5036 /* Request processed, but nothing found */
5037 if (xmlStrcmp(*code, BAD_CAST "0000")) {
5038 char *code_locale = _isds_utf82locale((char*) *code);
5039 char *status_message_locale =
5040 _isds_utf82locale((char*) *status_message);
5041 isds_log(ILF_ISDS, ILL_DEBUG,
5042 _("Server refused %s request (code=%s, message=%s)\n"),
5043 service_name_locale, code_locale, status_message_locale);
5044 isds_log_message(context, status_message_locale);
5045 free(code_locale);
5046 free(status_message_locale);
5047 err = IE_ISDS;
5048 goto leave;
5051 leave:
5052 free(service_name_locale);
5053 xmlFreeNode(request);
5054 return err;
5056 #endif
5059 /* Get data about logged in user and his box.
5060 * @context is session context
5061 * @db_owner_info is reallocated box owner description. It will be freed on
5062 * error.
5063 * @return error code from lower layer, context message will be set up
5064 * appropriately. */
5065 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
5066 struct isds_DbOwnerInfo **db_owner_info) {
5067 isds_error err = IE_SUCCESS;
5068 #if HAVE_LIBCURL
5069 xmlDocPtr response = NULL;
5070 xmlChar *code = NULL, *message = NULL;
5071 xmlXPathContextPtr xpath_ctx = NULL;
5072 xmlXPathObjectPtr result = NULL;
5073 char *string = NULL;
5074 #endif
5076 if (!context) return IE_INVALID_CONTEXT;
5077 zfree(context->long_message);
5078 if (!db_owner_info) return IE_INVAL;
5079 isds_DbOwnerInfo_free(db_owner_info);
5081 #if HAVE_LIBCURL
5082 /* Check if connection is established */
5083 if (!context->curl) return IE_CONNECTION_CLOSED;
5086 /* Do request and check for success */
5087 err = build_send_check_dbdummy_request(context,
5088 BAD_CAST "GetOwnerInfoFromLogin",
5089 &response, NULL, NULL, &code, &message);
5090 if (err) goto leave;
5093 /* Extract data */
5094 /* Prepare structure */
5095 *db_owner_info = calloc(1, sizeof(**db_owner_info));
5096 if (!*db_owner_info) {
5097 err = IE_NOMEM;
5098 goto leave;
5100 xpath_ctx = xmlXPathNewContext(response);
5101 if (!xpath_ctx) {
5102 err = IE_ERROR;
5103 goto leave;
5105 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5106 err = IE_ERROR;
5107 goto leave;
5110 /* Set context node */
5111 result = xmlXPathEvalExpression(BAD_CAST
5112 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
5113 if (!result) {
5114 err = IE_ERROR;
5115 goto leave;
5117 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5118 isds_log_message(context, _("Missing dbOwnerInfo element"));
5119 err = IE_ISDS;
5120 goto leave;
5122 if (result->nodesetval->nodeNr > 1) {
5123 isds_log_message(context, _("Multiple dbOwnerInfo element"));
5124 err = IE_ISDS;
5125 goto leave;
5127 xpath_ctx->node = result->nodesetval->nodeTab[0];
5128 xmlXPathFreeObject(result); result = NULL;
5130 /* Extract it */
5131 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
5134 leave:
5135 if (err) {
5136 isds_DbOwnerInfo_free(db_owner_info);
5139 free(string);
5140 xmlXPathFreeObject(result);
5141 xmlXPathFreeContext(xpath_ctx);
5143 free(code);
5144 free(message);
5145 xmlFreeDoc(response);
5147 if (!err)
5148 isds_log(ILF_ISDS, ILL_DEBUG,
5149 _("GetOwnerInfoFromLogin request processed by server "
5150 "successfully.\n"));
5151 #else /* not HAVE_LIBCURL */
5152 err = IE_NOTSUP;
5153 #endif
5155 return err;
5159 /* Get data about logged in user. */
5160 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
5161 struct isds_DbUserInfo **db_user_info) {
5162 isds_error err = IE_SUCCESS;
5163 #if HAVE_LIBCURL
5164 xmlDocPtr response = NULL;
5165 xmlChar *code = NULL, *message = NULL;
5166 xmlXPathContextPtr xpath_ctx = NULL;
5167 xmlXPathObjectPtr result = NULL;
5168 #endif
5170 if (!context) return IE_INVALID_CONTEXT;
5171 zfree(context->long_message);
5172 if (!db_user_info) return IE_INVAL;
5173 isds_DbUserInfo_free(db_user_info);
5175 #if HAVE_LIBCURL
5176 /* Check if connection is established */
5177 if (!context->curl) return IE_CONNECTION_CLOSED;
5180 /* Do request and check for success */
5181 err = build_send_check_dbdummy_request(context,
5182 BAD_CAST "GetUserInfoFromLogin",
5183 &response, NULL, NULL, &code, &message);
5184 if (err) goto leave;
5187 /* Extract data */
5188 /* Prepare structure */
5189 *db_user_info = calloc(1, sizeof(**db_user_info));
5190 if (!*db_user_info) {
5191 err = IE_NOMEM;
5192 goto leave;
5194 xpath_ctx = xmlXPathNewContext(response);
5195 if (!xpath_ctx) {
5196 err = IE_ERROR;
5197 goto leave;
5199 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5200 err = IE_ERROR;
5201 goto leave;
5204 /* Set context node */
5205 result = xmlXPathEvalExpression(BAD_CAST
5206 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
5207 if (!result) {
5208 err = IE_ERROR;
5209 goto leave;
5211 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5212 isds_log_message(context, _("Missing dbUserInfo element"));
5213 err = IE_ISDS;
5214 goto leave;
5216 if (result->nodesetval->nodeNr > 1) {
5217 isds_log_message(context, _("Multiple dbUserInfo element"));
5218 err = IE_ISDS;
5219 goto leave;
5221 xpath_ctx->node = result->nodesetval->nodeTab[0];
5222 xmlXPathFreeObject(result); result = NULL;
5224 /* Extract it */
5225 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5227 leave:
5228 if (err) {
5229 isds_DbUserInfo_free(db_user_info);
5232 xmlXPathFreeObject(result);
5233 xmlXPathFreeContext(xpath_ctx);
5235 free(code);
5236 free(message);
5237 xmlFreeDoc(response);
5239 if (!err)
5240 isds_log(ILF_ISDS, ILL_DEBUG,
5241 _("GetUserInfoFromLogin request processed by server "
5242 "successfully.\n"));
5243 #else /* not HAVE_LIBCURL */
5244 err = IE_NOTSUP;
5245 #endif
5247 return err;
5251 /* Get expiration time of current password
5252 * @context is session context
5253 * @expiration is automatically reallocated time when password expires. If
5254 * password expiration is disabled, NULL will be returned. In case of error
5255 * it will be nulled too. */
5256 isds_error isds_get_password_expiration(struct isds_ctx *context,
5257 struct timeval **expiration) {
5258 isds_error err = IE_SUCCESS;
5259 #if HAVE_LIBCURL
5260 xmlDocPtr response = NULL;
5261 xmlChar *code = NULL, *message = NULL;
5262 xmlXPathContextPtr xpath_ctx = NULL;
5263 xmlXPathObjectPtr result = NULL;
5264 char *string = NULL;
5265 #endif
5267 if (!context) return IE_INVALID_CONTEXT;
5268 zfree(context->long_message);
5269 if (!expiration) return IE_INVAL;
5270 zfree(*expiration);
5272 #if HAVE_LIBCURL
5273 /* Check if connection is established */
5274 if (!context->curl) return IE_CONNECTION_CLOSED;
5277 /* Do request and check for success */
5278 err = build_send_check_dbdummy_request(context,
5279 BAD_CAST "GetPasswordInfo",
5280 &response, NULL, NULL, &code, &message);
5281 if (err) goto leave;
5284 /* Extract data */
5285 xpath_ctx = xmlXPathNewContext(response);
5286 if (!xpath_ctx) {
5287 err = IE_ERROR;
5288 goto leave;
5290 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5291 err = IE_ERROR;
5292 goto leave;
5295 /* Set context node */
5296 result = xmlXPathEvalExpression(BAD_CAST
5297 "/isds:GetPasswordInfoResponse", xpath_ctx);
5298 if (!result) {
5299 err = IE_ERROR;
5300 goto leave;
5302 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5303 isds_log_message(context,
5304 _("Missing GetPasswordInfoResponse element"));
5305 err = IE_ISDS;
5306 goto leave;
5308 if (result->nodesetval->nodeNr > 1) {
5309 isds_log_message(context,
5310 _("Multiple GetPasswordInfoResponse element"));
5311 err = IE_ISDS;
5312 goto leave;
5314 xpath_ctx->node = result->nodesetval->nodeTab[0];
5315 xmlXPathFreeObject(result); result = NULL;
5317 /* Extract expiration date */
5318 EXTRACT_STRING("isds:pswExpDate", string);
5319 if (string) {
5320 /* And convert it if any returned. Otherwise expiration is disabled. */
5321 err = timestring2timeval((xmlChar *) string, expiration);
5322 if (err) {
5323 char *string_locale = _isds_utf82locale(string);
5324 if (err == IE_DATE) err = IE_ISDS;
5325 isds_printf_message(context,
5326 _("Could not convert pswExpDate as ISO time: %s"),
5327 string_locale);
5328 free(string_locale);
5329 goto leave;
5333 leave:
5334 if (err) {
5335 if (*expiration) {
5336 zfree(*expiration);
5340 free(string);
5341 xmlXPathFreeObject(result);
5342 xmlXPathFreeContext(xpath_ctx);
5344 free(code);
5345 free(message);
5346 xmlFreeDoc(response);
5348 if (!err)
5349 isds_log(ILF_ISDS, ILL_DEBUG,
5350 _("GetPasswordInfo request processed by server "
5351 "successfully.\n"));
5352 #else /* not HAVE_LIBCURL */
5353 err = IE_NOTSUP;
5354 #endif
5356 return err;
5360 #if HAVE_LIBCURL
5361 /* Request delivering new TOTP code from ISDS through side channel before
5362 * changing password.
5363 * @context is session context
5364 * @password is current password.
5365 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5366 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5367 * function for more details.
5368 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5369 * NULL, if you don't care.
5370 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5371 * error code. */
5372 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5373 const char *password, struct isds_otp *otp, char **refnumber) {
5374 isds_error err = IE_SUCCESS;
5375 char *saved_url = NULL; /* No copy */
5376 #if HAVE_CURL_REAUTHORIZATION_BUG
5377 CURL *saved_curl = NULL; /* No copy */
5378 #endif
5379 xmlNsPtr isds_ns = NULL;
5380 xmlNodePtr request = NULL;
5381 xmlDocPtr response = NULL;
5382 xmlChar *code = NULL, *message = NULL;
5383 const xmlChar *codes[] = {
5384 BAD_CAST "2300",
5385 BAD_CAST "2301",
5386 BAD_CAST "2302"
5388 const char *meanings[] = {
5389 N_("Unexpected error"),
5390 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5391 N_("One-time code could not been sent. Try later again.")
5393 const isds_otp_resolution resolutions[] = {
5394 OTP_RESOLUTION_UNKNOWN,
5395 OTP_RESOLUTION_TO_FAST,
5396 OTP_RESOLUTION_TOTP_NOT_SENT
5399 if (NULL == context) return IE_INVALID_CONTEXT;
5400 zfree(context->long_message);
5401 if (NULL == password) {
5402 isds_log_message(context,
5403 _("Second argument (password) of isds_change_password() "
5404 "is NULL"));
5405 return IE_INVAL;
5408 /* Check if connection is established
5409 * TODO: This check should be done downstairs. */
5410 if (!context->curl) return IE_CONNECTION_CLOSED;
5412 if (!context->otp) {
5413 isds_log_message(context, _("This function requires OTP-authenticated "
5414 "context"));
5415 return IE_INVALID_CONTEXT;
5417 if (NULL == otp) {
5418 isds_log_message(context, _("If one-time password authentication "
5419 "method is in use, requesting new OTP code requires "
5420 "one-time credentials argument either"));
5421 return IE_INVAL;
5423 if (otp->method != OTP_TIME) {
5424 isds_log_message(context, _("Requesting new time-based OTP code from "
5425 "server requires one-time password authentication "
5426 "method"));
5427 return IE_INVAL;
5429 if (otp->otp_code != NULL) {
5430 isds_log_message(context, _("Requesting new time-based OTP code from "
5431 "server requires undefined OTP code member in "
5432 "one-time credentials argument"));
5433 return IE_INVAL;
5437 /* Build request */
5438 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5439 if (!request) {
5440 isds_log_message(context, _("Could not build SendSMSCode request"));
5441 return IE_ERROR;
5443 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5444 if(!isds_ns) {
5445 isds_log_message(context, _("Could not create ISDS name space"));
5446 xmlFreeNode(request);
5447 return IE_ERROR;
5449 xmlSetNs(request, isds_ns);
5451 /* Change URL temporarily for sending this request only */
5453 char *new_url = NULL;
5454 if ((err = _isds_build_url_from_context(context,
5455 "%.*sasws/changePassword", &new_url))) {
5456 goto leave;
5458 saved_url = context->url;
5459 context->url = new_url;
5462 /* Store credentials for sending this request only */
5463 context->otp_credentials = otp;
5464 _isds_discard_credentials(context, 0);
5465 if ((err = _isds_store_credentials(context, context->saved_username,
5466 password, NULL))) {
5467 _isds_discard_credentials(context, 0);
5468 goto leave;
5470 #if HAVE_CURL_REAUTHORIZATION_BUG
5471 saved_curl = context->curl;
5472 context->curl = curl_easy_init();
5473 if (NULL == context->curl) {
5474 err = IE_ERROR;
5475 goto leave;
5477 if (context->timeout) {
5478 err = isds_set_timeout(context, context->timeout);
5479 if (err) goto leave;
5481 #endif
5483 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5485 /* Sent request */
5486 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5488 /* Remove temporal credentials */
5489 _isds_discard_credentials(context, 0);
5490 /* Detach pointer to OTP credentials from context */
5491 context->otp_credentials = NULL;
5492 /* Keep context->otp true to keep signaling this is OTP session */
5494 /* Destroy request */
5495 xmlFreeNode(request); request = NULL;
5497 if (err) {
5498 isds_log(ILF_ISDS, ILL_DEBUG,
5499 _("Processing ISDS response on SendSMSCode request failed\n"));
5500 goto leave;
5503 /* Check for response status */
5504 err = isds_response_status(context, SERVICE_ASWS, response,
5505 &code, &message, (xmlChar **)refnumber);
5506 if (err) {
5507 isds_log(ILF_ISDS, ILL_DEBUG,
5508 _("ISDS response on SendSMSCode request is missing "
5509 "status\n"));
5510 goto leave;
5513 /* Check for error */
5514 if (xmlStrcmp(code, BAD_CAST "0000")) {
5515 char *code_locale = _isds_utf82locale((char*)code);
5516 char *message_locale = _isds_utf82locale((char*)message);
5517 size_t i;
5518 isds_log(ILF_ISDS, ILL_DEBUG,
5519 _("Server refused to send new code on SendSMSCode "
5520 "request (code=%s, message=%s)\n"),
5521 code_locale, message_locale);
5523 /* Check for known error codes */
5524 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5525 if (!xmlStrcmp(code, codes[i])) break;
5527 if (i < sizeof(codes)/sizeof(*codes)) {
5528 isds_log_message(context, _(meanings[i]));
5529 /* Mimic otp->resolution according to the code, specification does
5530 * prescribe OTP header to be available. */
5531 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5532 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5533 otp->resolution = resolutions[i];
5534 } else
5535 isds_log_message(context, message_locale);
5537 free(code_locale);
5538 free(message_locale);
5540 err = IE_ISDS;
5541 goto leave;
5544 /* Otherwise new code sent successfully */
5545 /* Mimic otp->resolution according to the code, specification does
5546 * prescribe OTP header to be available. */
5547 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5548 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5550 leave:
5551 if (NULL != saved_url) {
5552 /* Revert URL to original one */
5553 zfree(context->url);
5554 context->url = saved_url;
5556 #if HAVE_CURL_REAUTHORIZATION_BUG
5557 if (NULL != saved_curl) {
5558 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5559 context->curl = saved_curl;
5561 #endif
5563 free(code);
5564 free(message);
5565 xmlFreeDoc(response);
5566 xmlFreeNode(request);
5568 if (!err)
5569 isds_log(ILF_ISDS, ILL_DEBUG,
5570 _("New OTP code has been sent successfully on SendSMSCode "
5571 "request.\n"));
5572 return err;
5576 /* Convert response status code to isds_error code and set long message
5577 * @context is context to save long message to
5578 * @map is mapping from codes to errors and messages. Pass NULL for generic
5579 * handling.
5580 * @code is status code to translate
5581 * @message is non-localized status message to put into long message in case
5582 * of uknown error. It can be NULL if server did not provide any.
5583 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5584 * invalid invocation. */
5585 static isds_error statuscode2isds_error(struct isds_ctx *context,
5586 const struct code_map_isds_error *map,
5587 const xmlChar *code, const xmlChar *message) {
5588 if (NULL == code) {
5589 isds_log_message(context,
5590 _("NULL status code passed to statuscode2isds_error()"));
5591 return IE_INVAL;
5594 if (NULL != map) {
5595 /* Check for known error codes */
5596 for (int i=0; map->codes[i] != NULL; i++) {
5597 if (!xmlStrcmp(code, map->codes[i])) {
5598 isds_log_message(context, _(map->meanings[i]));
5599 return map->errors[i];
5604 /* Other error */
5605 if (xmlStrcmp(code, BAD_CAST "0000")) {
5606 char *message_locale = _isds_utf82locale((char*)message);
5607 if (NULL == message_locale)
5608 isds_log_message(context, _("ISDS server returned unknown error"));
5609 else
5610 isds_log_message(context, message_locale);
5611 free(message_locale);
5612 return IE_ISDS;
5615 return IE_SUCCESS;
5617 #endif
5620 /* Change user password in ISDS.
5621 * User must supply old password, new password will takes effect after some
5622 * time, current session can continue. Password must fulfill some constraints.
5623 * @context is session context
5624 * @old_password is current password.
5625 * @new_password is requested new password
5626 * @otp auxiliary data required if one-time password authentication is in use,
5627 * defines OTP code (if known) and returns fine grade resolution of OTP
5628 * procedure. Pass NULL, if one-time password authentication is not needed.
5629 * Please note the @otp argument must match OTP method used at log-in time. See
5630 * isds_login() function for more details.
5631 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5632 * NULL, if you don't care.
5633 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5634 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5635 * awaiting OTP code that has been delivered by side channel to the user. */
5636 isds_error isds_change_password(struct isds_ctx *context,
5637 const char *old_password, const char *new_password,
5638 struct isds_otp *otp, char **refnumber) {
5639 isds_error err = IE_SUCCESS;
5640 #if HAVE_LIBCURL
5641 char *saved_url = NULL; /* No copy */
5642 #if HAVE_CURL_REAUTHORIZATION_BUG
5643 CURL *saved_curl = NULL; /* No copy */
5644 #endif
5645 xmlNsPtr isds_ns = NULL;
5646 xmlNodePtr request = NULL, node;
5647 xmlDocPtr response = NULL;
5648 xmlChar *code = NULL, *message = NULL;
5649 const xmlChar *codes[] = {
5650 BAD_CAST "1066",
5651 BAD_CAST "1067",
5652 BAD_CAST "1079",
5653 BAD_CAST "1080",
5654 BAD_CAST "1081",
5655 BAD_CAST "1082",
5656 BAD_CAST "1083",
5657 BAD_CAST "1090",
5658 BAD_CAST "1091",
5659 BAD_CAST "2300",
5660 BAD_CAST "9204"
5662 const char *meanings[] = {
5663 N_("Password length must be between 8 and 32 characters"),
5664 N_("Password cannot be reused"), /* Server does not distinguish 1067
5665 and 1091 on ChangePasswordOTP */
5666 N_("Password contains forbidden character"),
5667 N_("Password must contain at least one upper-case letter, "
5668 "one lower-case, and one digit"),
5669 N_("Password cannot contain sequence of three identical characters"),
5670 N_("Password cannot contain user identifier"),
5671 N_("Password is too simmple"),
5672 N_("Old password is not valid"),
5673 N_("Password cannot be reused"),
5674 N_("Unexpected error"),
5675 N_("LDAP update error")
5677 #endif
5679 if (!context) return IE_INVALID_CONTEXT;
5680 zfree(context->long_message);
5681 if (NULL != refnumber)
5682 zfree(*refnumber);
5683 if (NULL == old_password) {
5684 isds_log_message(context,
5685 _("Second argument (old password) of isds_change_password() "
5686 "is NULL"));
5687 return IE_INVAL;
5689 if (NULL == otp && NULL == new_password) {
5690 isds_log_message(context,
5691 _("Third argument (new password) of isds_change_password() "
5692 "is NULL"));
5693 return IE_INVAL;
5696 #if HAVE_LIBCURL
5697 /* Check if connection is established
5698 * TODO: This check should be done downstairs. */
5699 if (!context->curl) return IE_CONNECTION_CLOSED;
5701 if (context->otp && NULL == otp) {
5702 isds_log_message(context, _("If one-time password authentication "
5703 "method is in use, changing password requires one-time "
5704 "credentials either"));
5705 return IE_INVAL;
5708 /* Build ChangeISDSPassword request */
5709 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5710 BAD_CAST "ChangePasswordOTP");
5711 if (!request) {
5712 isds_log_message(context, (NULL == otp) ?
5713 _("Could not build ChangeISDSPassword request") :
5714 _("Could not build ChangePasswordOTP request"));
5715 return IE_ERROR;
5717 isds_ns = xmlNewNs(request,
5718 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5719 NULL);
5720 if(!isds_ns) {
5721 isds_log_message(context, _("Could not create ISDS name space"));
5722 xmlFreeNode(request);
5723 return IE_ERROR;
5725 xmlSetNs(request, isds_ns);
5727 INSERT_STRING(request, "dbOldPassword", old_password);
5728 INSERT_STRING(request, "dbNewPassword", new_password);
5730 if (NULL != otp) {
5731 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5732 switch (otp->method) {
5733 case OTP_HMAC:
5734 isds_log(ILF_SEC, ILL_INFO,
5735 _("Selected authentication method: "
5736 "HMAC-based one-time password\n"));
5737 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5738 break;
5739 case OTP_TIME:
5740 isds_log(ILF_SEC, ILL_INFO,
5741 _("Selected authentication method: "
5742 "Time-based one-time password\n"));
5743 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5744 if (otp->otp_code == NULL) {
5745 isds_log(ILF_SEC, ILL_INFO,
5746 _("OTP code has not been provided by "
5747 "application, requesting server for "
5748 "new one.\n"));
5749 err = _isds_request_totp_code(context, old_password, otp,
5750 refnumber);
5751 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5752 goto leave;
5754 } else {
5755 isds_log(ILF_SEC, ILL_INFO,
5756 _("OTP code has been provided by "
5757 "application, not requesting server "
5758 "for new one.\n"));
5760 break;
5761 default:
5762 isds_log_message(context,
5763 _("Unknown one-time password authentication "
5764 "method requested by application"));
5765 err = IE_ENUM;
5766 goto leave;
5769 /* Change URL temporarily for sending this request only */
5771 char *new_url = NULL;
5772 if ((err = _isds_build_url_from_context(context,
5773 "%.*sasws/changePassword", &new_url))) {
5774 goto leave;
5776 saved_url = context->url;
5777 context->url = new_url;
5780 /* Store credentials for sending this request only */
5781 context->otp_credentials = otp;
5782 _isds_discard_credentials(context, 0);
5783 if ((err = _isds_store_credentials(context, context->saved_username,
5784 old_password, NULL))) {
5785 _isds_discard_credentials(context, 0);
5786 goto leave;
5788 #if HAVE_CURL_REAUTHORIZATION_BUG
5789 saved_curl = context->curl;
5790 context->curl = curl_easy_init();
5791 if (NULL == context->curl) {
5792 err = IE_ERROR;
5793 goto leave;
5795 if (context->timeout) {
5796 err = isds_set_timeout(context, context->timeout);
5797 if (err) goto leave;
5799 #endif
5802 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5803 _("Sending ChangeISDSPassword request to ISDS\n") :
5804 _("Sending ChangePasswordOTP request to ISDS\n"));
5806 /* Sent request */
5807 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5808 request, &response, NULL, NULL);
5810 if (otp) {
5811 /* Remove temporal credentials */
5812 _isds_discard_credentials(context, 0);
5813 /* Detach pointer to OTP credentials from context */
5814 context->otp_credentials = NULL;
5815 /* Keep context->otp true to keep signaling this is OTP session */
5818 /* Destroy request */
5819 xmlFreeNode(request); request = NULL;
5821 if (err) {
5822 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5823 _("Processing ISDS response on ChangeISDSPassword "
5824 "request failed\n") :
5825 _("Processing ISDS response on ChangePasswordOTP "
5826 "request failed\n"));
5827 goto leave;
5830 /* Check for response status */
5831 err = isds_response_status(context,
5832 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5833 &code, &message, (xmlChar **)refnumber);
5834 if (err) {
5835 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5836 _("ISDS response on ChangeISDSPassword request is missing "
5837 "status\n") :
5838 _("ISDS response on ChangePasswordOTP request is missing "
5839 "status\n"));
5840 goto leave;
5843 /* Check for known error codes */
5844 for (size_t i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5845 if (!xmlStrcmp(code, codes[i])) {
5846 char *code_locale = _isds_utf82locale((char*)code);
5847 char *message_locale = _isds_utf82locale((char*)message);
5848 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5849 _("Server refused to change password on ChangeISDSPassword "
5850 "request (code=%s, message=%s)\n") :
5851 _("Server refused to change password on ChangePasswordOTP "
5852 "request (code=%s, message=%s)\n"),
5853 code_locale, message_locale);
5854 free(code_locale);
5855 free(message_locale);
5856 isds_log_message(context, _(meanings[i]));
5857 err = IE_INVAL;
5858 goto leave;
5862 /* Other error */
5863 if (xmlStrcmp(code, BAD_CAST "0000")) {
5864 char *code_locale = _isds_utf82locale((char*)code);
5865 char *message_locale = _isds_utf82locale((char*)message);
5866 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5867 _("Server refused to change password on ChangeISDSPassword "
5868 "request (code=%s, message=%s)\n") :
5869 _("Server refused to change password on ChangePasswordOTP "
5870 "request (code=%s, message=%s)\n"),
5871 code_locale, message_locale);
5872 isds_log_message(context, message_locale);
5873 free(code_locale);
5874 free(message_locale);
5875 err = IE_ISDS;
5876 goto leave;
5879 /* Otherwise password changed successfully */
5881 leave:
5882 if (NULL != saved_url) {
5883 /* Revert URL to original one */
5884 zfree(context->url);
5885 context->url = saved_url;
5887 #if HAVE_CURL_REAUTHORIZATION_BUG
5888 if (NULL != saved_curl) {
5889 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5890 context->curl = saved_curl;
5892 #endif
5894 free(code);
5895 free(message);
5896 xmlFreeDoc(response);
5897 xmlFreeNode(request);
5899 if (!err)
5900 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5901 _("Password changed successfully on ChangeISDSPassword "
5902 "request.\n") :
5903 _("Password changed successfully on ChangePasswordOTP "
5904 "request.\n"));
5905 #else /* not HAVE_LIBCURL */
5906 err = IE_NOTSUP;
5907 #endif
5909 return err;
5913 #if HAVE_LIBCURL
5914 /* Generic middle part with request sending and response check.
5915 * It sends prepared request and checks for error code.
5916 * @context is ISDS session context.
5917 * @service is ISDS service handler
5918 * @service_name is name in scope of given @service
5919 * @request is XML tree with request. Will be freed to save memory.
5920 * @response is XML document outputting ISDS response.
5921 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5922 * @map is mapping from status code to library error. Pass NULL if no special
5923 * handling is requested.
5924 * NULL, if you don't care. */
5925 static isds_error send_destroy_request_check_response(
5926 struct isds_ctx *context,
5927 const isds_service service, const xmlChar *service_name,
5928 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5929 const struct code_map_isds_error *map) {
5930 isds_error err = IE_SUCCESS;
5931 char *service_name_locale = NULL;
5932 xmlChar *code = NULL, *message = NULL;
5935 if (!context) return IE_INVALID_CONTEXT;
5936 if (!service_name || *service_name == '\0' || !request || !*request ||
5937 !response)
5938 return IE_INVAL;
5940 /* Check if connection is established
5941 * TODO: This check should be done downstairs. */
5942 if (!context->curl) return IE_CONNECTION_CLOSED;
5944 service_name_locale = _isds_utf82locale((char*) service_name);
5945 if (!service_name_locale) {
5946 err = IE_NOMEM;
5947 goto leave;
5950 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5951 service_name_locale);
5953 /* Send request */
5954 err = _isds(context, service, *request, response, NULL, NULL);
5955 xmlFreeNode(*request); *request = NULL;
5957 if (err) {
5958 isds_log(ILF_ISDS, ILL_DEBUG,
5959 _("Processing ISDS response on %s request failed\n"),
5960 service_name_locale);
5961 goto leave;
5964 /* Check for response status */
5965 err = isds_response_status(context, service, *response,
5966 &code, &message, refnumber);
5967 if (err) {
5968 isds_log(ILF_ISDS, ILL_DEBUG,
5969 _("ISDS response on %s request is missing status\n"),
5970 service_name_locale);
5971 goto leave;
5974 err = statuscode2isds_error(context, map, code, message);
5976 /* Request processed, but server failed */
5977 if (xmlStrcmp(code, BAD_CAST "0000")) {
5978 char *code_locale = _isds_utf82locale((char*) code);
5979 char *message_locale = _isds_utf82locale((char*) message);
5980 isds_log(ILF_ISDS, ILL_DEBUG,
5981 _("Server refused %s request (code=%s, message=%s)\n"),
5982 service_name_locale, code_locale, message_locale);
5983 free(code_locale);
5984 free(message_locale);
5985 goto leave;
5989 leave:
5990 free(code);
5991 free(message);
5992 if (err && *response) {
5993 xmlFreeDoc(*response);
5994 *response = NULL;
5996 if (*request) {
5997 xmlFreeNode(*request);
5998 *request = NULL;
6000 free(service_name_locale);
6002 return err;
6006 /* Generic bottom half with request sending.
6007 * It sends prepared request, checks for error code, destroys response and
6008 * request and log success or failure.
6009 * @context is ISDS session context.
6010 * @service is ISDS service handler
6011 * @service_name is name in scope of given @service
6012 * @request is XML tree with request. Will be freed to save memory.
6013 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6014 * NULL, if you don't care. */
6015 static isds_error send_request_check_drop_response(
6016 struct isds_ctx *context,
6017 const isds_service service, const xmlChar *service_name,
6018 xmlNodePtr *request, xmlChar **refnumber) {
6019 isds_error err = IE_SUCCESS;
6020 xmlDocPtr response = NULL;
6023 if (!context) return IE_INVALID_CONTEXT;
6024 if (!service_name || *service_name == '\0' || !request || !*request)
6025 return IE_INVAL;
6027 /* Send request and check response*/
6028 err = send_destroy_request_check_response(context,
6029 service, service_name, request, &response, refnumber, NULL);
6031 xmlFreeDoc(response);
6033 if (*request) {
6034 xmlFreeNode(*request);
6035 *request = NULL;
6038 if (!err) {
6039 char *service_name_locale = _isds_utf82locale((char *) service_name);
6040 isds_log(ILF_ISDS, ILL_DEBUG,
6041 _("%s request processed by server successfully.\n"),
6042 service_name_locale);
6043 free(service_name_locale);
6046 return err;
6050 /* Insert isds_credentials_delivery structure into XML request if not NULL
6051 * @context is session context
6052 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
6053 * credentials delivery. The email field is passed.
6054 * @parent is XML element where to insert */
6055 static isds_error insert_credentials_delivery(struct isds_ctx *context,
6056 const struct isds_credentials_delivery *credentials_delivery,
6057 xmlNodePtr parent) {
6058 isds_error err = IE_SUCCESS;
6059 xmlNodePtr node;
6061 if (!context) return IE_INVALID_CONTEXT;
6062 if (!parent) return IE_INVAL;
6064 if (credentials_delivery) {
6065 /* Following elements are valid only for services:
6066 * NewAccessData, AddDataBoxUser, CreateDataBox */
6067 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
6068 INSERT_STRING(parent, "email", credentials_delivery->email);
6071 leave:
6072 return err;
6076 /* Extract credentials delivery from ISDS response.
6077 * @context is session context
6078 * @credentials_delivery is pointer to valid structure to fill in returned
6079 * user's password (and new log-in name). If NULL, do not extract the data.
6080 * @response is pointer to XML document with ISDS response
6081 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
6082 * @return IE_SUCCESS even if new user name has not been found because it's not
6083 * clear whether it's returned always. */
6084 static isds_error extract_credentials_delivery(struct isds_ctx *context,
6085 struct isds_credentials_delivery *credentials_delivery,
6086 xmlDocPtr response, const char *request_name) {
6087 isds_error err = IE_SUCCESS;
6088 xmlXPathContextPtr xpath_ctx = NULL;
6089 xmlXPathObjectPtr result = NULL;
6090 char *xpath_query = NULL;
6092 if (!context) return IE_INVALID_CONTEXT;
6093 if (credentials_delivery) {
6094 zfree(credentials_delivery->token);
6095 zfree(credentials_delivery->new_user_name);
6097 if (!response || !request_name || !*request_name) return IE_INVAL;
6100 /* Extract optional token */
6101 if (credentials_delivery) {
6102 xpath_ctx = xmlXPathNewContext(response);
6103 if (!xpath_ctx) {
6104 err = IE_ERROR;
6105 goto leave;
6107 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6108 err = IE_ERROR;
6109 goto leave;
6112 /* Verify root element */
6113 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
6114 request_name)) {
6115 err = IE_NOMEM;
6116 goto leave;
6118 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
6119 if (!result) {
6120 err = IE_ERROR;
6121 goto leave;
6123 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6124 char *request_name_locale = _isds_utf82locale(request_name);
6125 isds_log(ILF_ISDS, ILL_WARNING,
6126 _("Wrong element in ISDS response for %s request "
6127 "while extracting credentials delivery details\n"),
6128 request_name_locale);
6129 free(request_name_locale);
6130 err = IE_ERROR;
6131 goto leave;
6133 xpath_ctx->node = result->nodesetval->nodeTab[0];
6136 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6137 * optional. */
6138 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
6140 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
6141 if (!credentials_delivery->token) {
6142 char *request_name_locale = _isds_utf82locale(request_name);
6143 isds_log(ILF_ISDS, ILL_ERR,
6144 _("ISDS did not return token on %s request "
6145 "even if requested\n"), request_name_locale);
6146 free(request_name_locale);
6147 err = IE_ERROR;
6151 leave:
6152 free(xpath_query);
6153 xmlXPathFreeObject(result);
6154 xmlXPathFreeContext(xpath_ctx);
6156 return err;
6160 /* Build XSD:tCreateDBInput request type for box creating.
6161 * @context is session context
6162 * @request outputs built XML tree
6163 * @service_name is request name of SERVICE_DB_MANIPULATION service
6164 * @box is box description to create including single primary user (in case of
6165 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6166 * ignored.
6167 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6168 * box, or contact address of PFO box owner)
6169 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6170 * @upper_box_id is optional ID of supper box if currently created box is
6171 * subordinated.
6172 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6173 * don't care.
6174 * @credentials_delivery is valid pointer if ISDS should return token that box
6175 * owner can use to obtain his new credentials in on-line way. Then valid email
6176 * member value should be supplied.
6177 * @approval is optional external approval of box manipulation */
6178 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
6179 xmlNodePtr *request, const xmlChar *service_name,
6180 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6181 const xmlChar *former_names, const xmlChar *upper_box_id,
6182 const xmlChar *ceo_label,
6183 const struct isds_credentials_delivery *credentials_delivery,
6184 const struct isds_approval *approval) {
6185 isds_error err = IE_SUCCESS;
6186 xmlNsPtr isds_ns = NULL;
6187 xmlNodePtr node, dbPrimaryUsers;
6188 xmlChar *string = NULL;
6189 const struct isds_list *item;
6192 if (!context) return IE_INVALID_CONTEXT;
6193 if (!request || !service_name || service_name[0] == '\0' || !box)
6194 return IE_INVAL;
6197 /* Build CreateDataBox-similar request */
6198 *request = xmlNewNode(NULL, service_name);
6199 if (!*request) {
6200 char *service_name_locale = _isds_utf82locale((char*) service_name);
6201 isds_printf_message(context, _("Could build %s request"),
6202 service_name_locale);
6203 free(service_name_locale);
6204 return IE_ERROR;
6206 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
6207 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
6208 if (!isds_ns) {
6209 isds_log_message(context, _("Could not create ISDS1 name space"));
6210 xmlFreeNode(*request);
6211 return IE_ERROR;
6213 } else {
6214 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6215 if (!isds_ns) {
6216 isds_log_message(context, _("Could not create ISDS name space"));
6217 xmlFreeNode(*request);
6218 return IE_ERROR;
6221 xmlSetNs(*request, isds_ns);
6223 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6224 err = insert_DbOwnerInfo(context, box, 0, node);
6225 if (err) goto leave;
6227 /* Insert users */
6228 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6229 * verbose documentation allows none dbUserInfo */
6230 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6231 for (item = users; item; item = item->next) {
6232 if (item->data) {
6233 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6234 err = insert_DbUserInfo(context,
6235 (struct isds_DbUserInfo *) item->data, 1, node);
6236 if (err) goto leave;
6240 INSERT_STRING(*request, "dbFormerNames", former_names);
6241 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6242 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6244 err = insert_credentials_delivery(context, credentials_delivery, *request);
6245 if (err) goto leave;
6247 err = insert_GExtApproval(context, approval, *request);
6248 if (err) goto leave;
6250 leave:
6251 if (err) {
6252 xmlFreeNode(*request);
6253 *request = NULL;
6255 free(string);
6256 return err;
6258 #endif /* HAVE_LIBCURL */
6261 /* Create new box.
6262 * @context is session context
6263 * @box is box description to create including single primary user (in case of
6264 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6265 * ignored. It outputs box ID assigned by ISDS in dbID element.
6266 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6267 * box, or contact address of PFO box owner)
6268 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6269 * @upper_box_id is optional ID of supper box if currently created box is
6270 * subordinated.
6271 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6272 * @credentials_delivery is NULL if new password should be delivered off-line
6273 * to box owner. It is valid pointer if owner should obtain new password on-line
6274 * on dedicated web server. Then input @credentials_delivery.email value is
6275 * his e-mail address he must provide to dedicated web server together
6276 * with output reallocated @credentials_delivery.token member. Output
6277 * member @credentials_delivery.new_user_name is unused up on this call.
6278 * @approval is optional external approval of box manipulation
6279 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6280 * NULL, if you don't care.*/
6281 isds_error isds_add_box(struct isds_ctx *context,
6282 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6283 const char *former_names, const char *upper_box_id,
6284 const char *ceo_label,
6285 struct isds_credentials_delivery *credentials_delivery,
6286 const struct isds_approval *approval, char **refnumber) {
6287 isds_error err = IE_SUCCESS;
6288 #if HAVE_LIBCURL
6289 xmlNodePtr request = NULL;
6290 xmlDocPtr response = NULL;
6291 xmlXPathContextPtr xpath_ctx = NULL;
6292 xmlXPathObjectPtr result = NULL;
6293 #endif
6296 if (!context) return IE_INVALID_CONTEXT;
6297 zfree(context->long_message);
6298 if (credentials_delivery) {
6299 zfree(credentials_delivery->token);
6300 zfree(credentials_delivery->new_user_name);
6302 if (!box) return IE_INVAL;
6304 #if HAVE_LIBCURL
6305 /* Scratch box ID */
6306 zfree(box->dbID);
6308 /* Build CreateDataBox request */
6309 err = build_CreateDBInput_request(context,
6310 &request, BAD_CAST "CreateDataBox",
6311 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6312 (xmlChar *) ceo_label, credentials_delivery, approval);
6313 if (err) goto leave;
6315 /* Send it to server and process response */
6316 err = send_destroy_request_check_response(context,
6317 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6318 &response, (xmlChar **) refnumber, NULL);
6320 /* Extract box ID */
6321 xpath_ctx = xmlXPathNewContext(response);
6322 if (!xpath_ctx) {
6323 err = IE_ERROR;
6324 goto leave;
6326 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6327 err = IE_ERROR;
6328 goto leave;
6330 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6332 /* Extract optional token */
6333 err = extract_credentials_delivery(context, credentials_delivery, response,
6334 "CreateDataBox");
6336 leave:
6337 xmlXPathFreeObject(result);
6338 xmlXPathFreeContext(xpath_ctx);
6339 xmlFreeDoc(response);
6340 xmlFreeNode(request);
6342 if (!err) {
6343 isds_log(ILF_ISDS, ILL_DEBUG,
6344 _("CreateDataBox request processed by server successfully.\n"));
6346 #else /* not HAVE_LIBCURL */
6347 err = IE_NOTSUP;
6348 #endif
6350 return err;
6354 /* Notify ISDS about new PFO entity.
6355 * This function has no real effect.
6356 * @context is session context
6357 * @box is PFO description including single primary user. aifoIsds,
6358 * address->adCode, address->adDistrict members are ignored.
6359 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6360 * @former_names is optional undocumented string. Pass NULL if you don't care.
6361 * @upper_box_id is optional ID of supper box if currently created box is
6362 * subordinated.
6363 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6364 * @approval is optional external approval of box manipulation
6365 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6366 * NULL, if you don't care.*/
6367 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6368 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6369 const char *former_names, const char *upper_box_id,
6370 const char *ceo_label, const struct isds_approval *approval,
6371 char **refnumber) {
6372 isds_error err = IE_SUCCESS;
6373 #if HAVE_LIBCURL
6374 xmlNodePtr request = NULL;
6375 #endif
6377 if (!context) return IE_INVALID_CONTEXT;
6378 zfree(context->long_message);
6379 if (!box) return IE_INVAL;
6381 #if HAVE_LIBCURL
6382 /* Build CreateDataBoxPFOInfo request */
6383 err = build_CreateDBInput_request(context,
6384 &request, BAD_CAST "CreateDataBoxPFOInfo",
6385 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6386 (xmlChar *) ceo_label, NULL, approval);
6387 if (err) goto leave;
6389 /* Send it to server and process response */
6390 err = send_request_check_drop_response(context,
6391 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6392 (xmlChar **) refnumber);
6393 /* XXX: XML Schema names output dbID element but textual documentation
6394 * states no box identifier is returned. */
6395 leave:
6396 xmlFreeNode(request);
6397 #else /* not HAVE_LIBCURL */
6398 err = IE_NOTSUP;
6399 #endif
6400 return err;
6404 /* Common implementation for removing given box.
6405 * @context is session context
6406 * @service_name is UTF-8 encoded name fo ISDS service
6407 * @box is box description to delete. aifoIsds, address->adCode,
6408 * address->adDistrict members are ignored.
6409 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6410 * carry sane value. If NULL, do not inject this information into request.
6411 * @approval is optional external approval of box manipulation
6412 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6413 * NULL, if you don't care.*/
6414 static isds_error _isds_delete_box_common(struct isds_ctx *context,
6415 const xmlChar *service_name,
6416 const struct isds_DbOwnerInfo *box, const struct tm *since,
6417 const struct isds_approval *approval, char **refnumber) {
6418 isds_error err = IE_SUCCESS;
6419 #if HAVE_LIBCURL
6420 xmlNsPtr isds_ns = NULL;
6421 xmlNodePtr request = NULL;
6422 xmlNodePtr node;
6423 xmlChar *string = NULL;
6424 #endif
6427 if (!context) return IE_INVALID_CONTEXT;
6428 zfree(context->long_message);
6429 if (!service_name || !*service_name || !box) return IE_INVAL;
6432 #if HAVE_LIBCURL
6433 /* Build DeleteDataBox(Promptly) request */
6434 request = xmlNewNode(NULL, service_name);
6435 if (!request) {
6436 char *service_name_locale = _isds_utf82locale((char*)service_name);
6437 isds_printf_message(context,
6438 _("Could build %s request"), service_name_locale);
6439 free(service_name_locale);
6440 return IE_ERROR;
6442 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6443 if(!isds_ns) {
6444 isds_log_message(context, _("Could not create ISDS name space"));
6445 xmlFreeNode(request);
6446 return IE_ERROR;
6448 xmlSetNs(request, isds_ns);
6450 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6451 err = insert_DbOwnerInfo(context, box, 0, node);
6452 if (err) goto leave;
6454 if (since) {
6455 err = tm2datestring(since, &string);
6456 if (err) {
6457 isds_log_message(context,
6458 _("Could not convert `since' argument to ISO date string"));
6459 goto leave;
6461 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6462 zfree(string);
6465 err = insert_GExtApproval(context, approval, request);
6466 if (err) goto leave;
6469 /* Send it to server and process response */
6470 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6471 service_name, &request, (xmlChar **) refnumber);
6473 leave:
6474 xmlFreeNode(request);
6475 free(string);
6476 #else /* not HAVE_LIBCURL */
6477 err = IE_NOTSUP;
6478 #endif
6479 return err;
6483 /* Remove given box permanently.
6484 * @context is session context
6485 * @box is box description to delete. aifoIsds, address->adCode,
6486 * address->adDistrict members are ignored.
6487 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6488 * carry sane value.
6489 * @approval is optional external approval of box manipulation
6490 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6491 * NULL, if you don't care.*/
6492 isds_error isds_delete_box(struct isds_ctx *context,
6493 const struct isds_DbOwnerInfo *box, const struct tm *since,
6494 const struct isds_approval *approval, char **refnumber) {
6495 if (!context) return IE_INVALID_CONTEXT;
6496 zfree(context->long_message);
6497 if (!box || !since) return IE_INVAL;
6499 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6500 box, since, approval, refnumber);
6504 /* Undocumented function.
6505 * @context is session context
6506 * @box is box description to delete. aifoIsds, address->adCode,
6507 * address->adDistrict members are ignored.
6508 * @approval is optional external approval of box manipulation
6509 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6510 * NULL, if you don't care.*/
6511 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6512 const struct isds_DbOwnerInfo *box,
6513 const struct isds_approval *approval, char **refnumber) {
6514 if (!context) return IE_INVALID_CONTEXT;
6515 zfree(context->long_message);
6516 if (!box) return IE_INVAL;
6518 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6519 box, NULL, approval, refnumber);
6523 /* Update data about given box.
6524 * @context is session context
6525 * @old_box current box description. aifoIsds, address->adCode,
6526 * address->adDistrict members are ignored.
6527 * @new_box are updated data about @old_box. aifoIsds, address->adCode,
6528 * address->adDistrict members are ignored.
6529 * @approval is optional external approval of box manipulation
6530 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6531 * NULL, if you don't care.*/
6532 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6533 const struct isds_DbOwnerInfo *old_box,
6534 const struct isds_DbOwnerInfo *new_box,
6535 const struct isds_approval *approval, char **refnumber) {
6536 isds_error err = IE_SUCCESS;
6537 #if HAVE_LIBCURL
6538 xmlNsPtr isds_ns = NULL;
6539 xmlNodePtr request = NULL;
6540 xmlNodePtr node;
6541 #endif
6544 if (!context) return IE_INVALID_CONTEXT;
6545 zfree(context->long_message);
6546 if (!old_box || !new_box) return IE_INVAL;
6549 #if HAVE_LIBCURL
6550 /* Build UpdateDataBoxDescr request */
6551 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6552 if (!request) {
6553 isds_log_message(context,
6554 _("Could build UpdateDataBoxDescr request"));
6555 return IE_ERROR;
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 xmlFreeNode(request);
6561 return IE_ERROR;
6563 xmlSetNs(request, isds_ns);
6565 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6566 err = insert_DbOwnerInfo(context, old_box, 0, node);
6567 if (err) goto leave;
6569 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6570 err = insert_DbOwnerInfo(context, new_box, 0, node);
6571 if (err) goto leave;
6573 err = insert_GExtApproval(context, approval, request);
6574 if (err) goto leave;
6577 /* Send it to server and process response */
6578 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6579 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6581 leave:
6582 xmlFreeNode(request);
6583 #else /* not HAVE_LIBCURL */
6584 err = IE_NOTSUP;
6585 #endif
6587 return err;
6591 #if HAVE_LIBCURL
6592 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6593 * code
6594 * @context is session context
6595 * @service is SOAP service
6596 * @service_name is name of request in @service
6597 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6598 * @box_id is box ID of interest
6599 * @approval is optional external approval of box manipulation
6600 * @response is server SOAP body response as XML document
6601 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6602 * NULL, if you don't care.
6603 * @return error coded from lower layer, context message will be set up
6604 * appropriately. */
6605 static isds_error build_send_dbid_request_check_response(
6606 struct isds_ctx *context, const isds_service service,
6607 const xmlChar *service_name, const xmlChar *box_id_element,
6608 const xmlChar *box_id, const struct isds_approval *approval,
6609 xmlDocPtr *response, xmlChar **refnumber) {
6611 isds_error err = IE_SUCCESS;
6612 char *service_name_locale = NULL, *box_id_locale = NULL;
6613 xmlNodePtr request = NULL, node;
6614 xmlNsPtr isds_ns = NULL;
6616 if (!context) return IE_INVALID_CONTEXT;
6617 if (!service_name || !box_id) return IE_INVAL;
6618 if (!response) return IE_INVAL;
6620 /* Free output argument */
6621 xmlFreeDoc(*response); *response = NULL;
6623 /* Prepare strings */
6624 service_name_locale = _isds_utf82locale((char*)service_name);
6625 if (!service_name_locale) {
6626 err = IE_NOMEM;
6627 goto leave;
6629 box_id_locale = _isds_utf82locale((char*)box_id);
6630 if (!box_id_locale) {
6631 err = IE_NOMEM;
6632 goto leave;
6635 /* Build request */
6636 request = xmlNewNode(NULL, service_name);
6637 if (!request) {
6638 isds_printf_message(context,
6639 _("Could not build %s request for %s box"), service_name_locale,
6640 box_id_locale);
6641 err = IE_ERROR;
6642 goto leave;
6644 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6645 if(!isds_ns) {
6646 isds_log_message(context, _("Could not create ISDS name space"));
6647 err = IE_ERROR;
6648 goto leave;
6650 xmlSetNs(request, isds_ns);
6652 /* Add XSD:tIdDbInput children */
6653 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6654 INSERT_STRING(request, box_id_element, box_id);
6655 err = insert_GExtApproval(context, approval, request);
6656 if (err) goto leave;
6658 /* Send request and check response*/
6659 err = send_destroy_request_check_response(context,
6660 service, service_name, &request, response, refnumber, NULL);
6662 leave:
6663 free(service_name_locale);
6664 free(box_id_locale);
6665 xmlFreeNode(request);
6666 return err;
6668 #endif /* HAVE_LIBCURL */
6671 /* Get data about all users assigned to given box.
6672 * @context is session context
6673 * @box_id is box ID
6674 * @users is automatically reallocated list of struct isds_DbUserInfo */
6675 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6676 struct isds_list **users) {
6677 isds_error err = IE_SUCCESS;
6678 #if HAVE_LIBCURL
6679 xmlDocPtr response = NULL;
6680 xmlXPathContextPtr xpath_ctx = NULL;
6681 xmlXPathObjectPtr result = NULL;
6682 int i;
6683 struct isds_list *item, *prev_item = NULL;
6684 #endif
6686 if (!context) return IE_INVALID_CONTEXT;
6687 zfree(context->long_message);
6688 if (!users || !box_id) return IE_INVAL;
6689 isds_list_free(users);
6692 #if HAVE_LIBCURL
6693 /* Do request and check for success */
6694 err = build_send_dbid_request_check_response(context,
6695 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6696 BAD_CAST box_id, NULL, &response, NULL);
6697 if (err) goto leave;
6700 /* Extract data */
6701 /* Prepare structure */
6702 xpath_ctx = xmlXPathNewContext(response);
6703 if (!xpath_ctx) {
6704 err = IE_ERROR;
6705 goto leave;
6707 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6708 err = IE_ERROR;
6709 goto leave;
6712 /* Set context node */
6713 result = xmlXPathEvalExpression(BAD_CAST
6714 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6715 xpath_ctx);
6716 if (!result) {
6717 err = IE_ERROR;
6718 goto leave;
6720 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6721 /* Iterate over all users */
6722 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6724 /* Prepare structure */
6725 item = calloc(1, sizeof(*item));
6726 if (!item) {
6727 err = IE_NOMEM;
6728 goto leave;
6730 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6731 if (i == 0) *users = item;
6732 else prev_item->next = item;
6733 prev_item = item;
6735 /* Extract it */
6736 xpath_ctx->node = result->nodesetval->nodeTab[i];
6737 err = extract_DbUserInfo(context,
6738 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6739 if (err) goto leave;
6743 leave:
6744 if (err) {
6745 isds_list_free(users);
6748 xmlXPathFreeObject(result);
6749 xmlXPathFreeContext(xpath_ctx);
6750 xmlFreeDoc(response);
6752 if (!err)
6753 isds_log(ILF_ISDS, ILL_DEBUG,
6754 _("GetDataBoxUsers request processed by server "
6755 "successfully.\n"));
6756 #else /* not HAVE_LIBCURL */
6757 err = IE_NOTSUP;
6758 #endif
6760 return err;
6764 /* Update data about user assigned to given box.
6765 * @context is session context
6766 * @box is box identification. aifoIsds, address->adCode,
6767 * address->adDistrict members are ignored.
6768 * @old_user identifies user to update, aifo_ticket member is ignored
6769 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6770 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6771 * NULL, if you don't care.*/
6772 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6773 const struct isds_DbOwnerInfo *box,
6774 const struct isds_DbUserInfo *old_user,
6775 const struct isds_DbUserInfo *new_user,
6776 char **refnumber) {
6777 isds_error err = IE_SUCCESS;
6778 #if HAVE_LIBCURL
6779 xmlNsPtr isds_ns = NULL;
6780 xmlNodePtr request = NULL;
6781 xmlNodePtr node;
6782 #endif
6785 if (!context) return IE_INVALID_CONTEXT;
6786 zfree(context->long_message);
6787 if (!box || !old_user || !new_user) return IE_INVAL;
6790 #if HAVE_LIBCURL
6791 /* Build UpdateDataBoxUser request */
6792 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6793 if (!request) {
6794 isds_log_message(context,
6795 _("Could build UpdateDataBoxUser request"));
6796 return IE_ERROR;
6798 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6799 if(!isds_ns) {
6800 isds_log_message(context, _("Could not create ISDS name space"));
6801 xmlFreeNode(request);
6802 return IE_ERROR;
6804 xmlSetNs(request, isds_ns);
6806 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6807 err = insert_DbOwnerInfo(context, box, 0, node);
6808 if (err) goto leave;
6810 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6811 err = insert_DbUserInfo(context, old_user, 0, node);
6812 if (err) goto leave;
6814 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6815 err = insert_DbUserInfo(context, new_user, 0, node);
6816 if (err) goto leave;
6818 /* Send it to server and process response */
6819 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6820 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6822 leave:
6823 xmlFreeNode(request);
6824 #else /* not HAVE_LIBCURL */
6825 err = IE_NOTSUP;
6826 #endif
6828 return err;
6832 /* Undocumented function.
6833 * @context is session context
6834 * @box_id is UTF-8 encoded box identifier
6835 * @token is UTF-8 encoded temporary password
6836 * @user_id outputs UTF-8 encoded reallocated user identifier
6837 * @password outpus UTF-8 encoded reallocated user password
6838 * Output arguments will be nulled in case of error */
6839 isds_error isds_activate(struct isds_ctx *context,
6840 const char *box_id, const char *token,
6841 char **user_id, char **password) {
6842 isds_error err = IE_SUCCESS;
6843 #if HAVE_LIBCURL
6844 xmlNsPtr isds_ns = NULL;
6845 xmlNodePtr request = NULL, node;
6846 xmlDocPtr response = NULL;
6847 xmlXPathContextPtr xpath_ctx = NULL;
6848 xmlXPathObjectPtr result = NULL;
6849 #endif
6852 if (!context) return IE_INVALID_CONTEXT;
6853 zfree(context->long_message);
6855 if (user_id) zfree(*user_id);
6856 if (password) zfree(*password);
6858 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6861 #if HAVE_LIBCURL
6862 /* Build Activate request */
6863 request = xmlNewNode(NULL, BAD_CAST "Activate");
6864 if (!request) {
6865 isds_log_message(context, _("Could build Activate request"));
6866 return IE_ERROR;
6868 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6869 if(!isds_ns) {
6870 isds_log_message(context, _("Could not create ISDS name space"));
6871 xmlFreeNode(request);
6872 return IE_ERROR;
6874 xmlSetNs(request, isds_ns);
6876 INSERT_STRING(request, "dbAccessDataId", token);
6877 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6878 INSERT_STRING(request, "dbID", box_id);
6881 /* Send request and check response*/
6882 err = send_destroy_request_check_response(context,
6883 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6884 &response, NULL, NULL);
6885 if (err) goto leave;
6888 /* Extract data */
6889 xpath_ctx = xmlXPathNewContext(response);
6890 if (!xpath_ctx) {
6891 err = IE_ERROR;
6892 goto leave;
6894 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6895 err = IE_ERROR;
6896 goto leave;
6898 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6899 xpath_ctx);
6900 if (!result) {
6901 err = IE_ERROR;
6902 goto leave;
6904 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6905 isds_log_message(context, _("Missing ActivateResponse element"));
6906 err = IE_ISDS;
6907 goto leave;
6909 if (result->nodesetval->nodeNr > 1) {
6910 isds_log_message(context, _("Multiple ActivateResponse element"));
6911 err = IE_ISDS;
6912 goto leave;
6914 xpath_ctx->node = result->nodesetval->nodeTab[0];
6915 xmlXPathFreeObject(result); result = NULL;
6917 EXTRACT_STRING("isds:userId", *user_id);
6918 if (!*user_id)
6919 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6920 "but did not return `userId' element.\n"));
6922 EXTRACT_STRING("isds:password", *password);
6923 if (!*password)
6924 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6925 "but did not return `password' element.\n"));
6927 leave:
6928 xmlXPathFreeObject(result);
6929 xmlXPathFreeContext(xpath_ctx);
6930 xmlFreeDoc(response);
6931 xmlFreeNode(request);
6933 if (!err)
6934 isds_log(ILF_ISDS, ILL_DEBUG,
6935 _("Activate request processed by server successfully.\n"));
6936 #else /* not HAVE_LIBCURL */
6937 err = IE_NOTSUP;
6938 #endif
6940 return err;
6944 /* Reset credentials of user assigned to given box.
6945 * @context is session context
6946 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
6947 * members are ignored.
6948 * @user identifies user to reset password, aifo_ticket member is ignored
6949 * @fee_paid is true if fee has been paid, false otherwise
6950 * @approval is optional external approval of box manipulation
6951 * @credentials_delivery is NULL if new password should be delivered off-line
6952 * to the user. It is valid pointer if user should obtain new password on-line
6953 * on dedicated web server. Then input @credentials_delivery.email value is
6954 * user's e-mail address user must provide to dedicated web server together
6955 * with @credentials_delivery.token. The output reallocated token user needs
6956 * to use to authorize on the web server to view his new password. Output
6957 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6958 * ISDS changed up on this call. (No reason why server could change the name
6959 * is known now.)
6960 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6961 * NULL, if you don't care.*/
6962 isds_error isds_reset_password(struct isds_ctx *context,
6963 const struct isds_DbOwnerInfo *box,
6964 const struct isds_DbUserInfo *user,
6965 const _Bool fee_paid, const struct isds_approval *approval,
6966 struct isds_credentials_delivery *credentials_delivery,
6967 char **refnumber) {
6968 isds_error err = IE_SUCCESS;
6969 #if HAVE_LIBCURL
6970 xmlNsPtr isds_ns = NULL;
6971 xmlNodePtr request = NULL, node;
6972 xmlDocPtr response = NULL;
6973 #endif
6976 if (!context) return IE_INVALID_CONTEXT;
6977 zfree(context->long_message);
6979 if (credentials_delivery) {
6980 zfree(credentials_delivery->token);
6981 zfree(credentials_delivery->new_user_name);
6983 if (!box || !user) return IE_INVAL;
6986 #if HAVE_LIBCURL
6987 /* Build NewAccessData request */
6988 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6989 if (!request) {
6990 isds_log_message(context,
6991 _("Could build NewAccessData request"));
6992 return IE_ERROR;
6994 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6995 if(!isds_ns) {
6996 isds_log_message(context, _("Could not create ISDS name space"));
6997 xmlFreeNode(request);
6998 return IE_ERROR;
7000 xmlSetNs(request, isds_ns);
7002 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7003 err = insert_DbOwnerInfo(context, box, 0, node);
7004 if (err) goto leave;
7006 INSERT_ELEMENT(node, request, "dbUserInfo");
7007 err = insert_DbUserInfo(context, user, 0, node);
7008 if (err) goto leave;
7010 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
7012 err = insert_credentials_delivery(context, credentials_delivery, request);
7013 if (err) goto leave;
7015 err = insert_GExtApproval(context, approval, request);
7016 if (err) goto leave;
7018 /* Send request and check response*/
7019 err = send_destroy_request_check_response(context,
7020 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
7021 &response, (xmlChar **) refnumber, NULL);
7022 if (err) goto leave;
7025 /* Extract optional token */
7026 err = extract_credentials_delivery(context, credentials_delivery,
7027 response, "NewAccessData");
7029 leave:
7030 xmlFreeDoc(response);
7031 xmlFreeNode(request);
7033 if (!err)
7034 isds_log(ILF_ISDS, ILL_DEBUG,
7035 _("NewAccessData request processed by server "
7036 "successfully.\n"));
7037 #else /* not HAVE_LIBCURL */
7038 err = IE_NOTSUP;
7039 #endif
7041 return err;
7045 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
7046 * code, destroy response and log success.
7047 * @context is ISDS session context.
7048 * @service_name is name of SERVICE_DB_MANIPULATION service
7049 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7050 * members are ignored.
7051 * @user identifies user to remove
7052 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
7053 * @credentials_delivery is NULL if new user's password should be delivered
7054 * off-line to the user. It is valid pointer if user should obtain new
7055 * password on-line on dedicated web server. Then input
7056 * @credentials_delivery.email value is user's e-mail address user must
7057 * provide to dedicated web server together with @credentials_delivery.token.
7058 * The output reallocated token user needs to use to authorize on the web
7059 * server to view his new password. Output reallocated
7060 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7061 * assingned or changed up on this call.
7062 * @approval is optional external approval of box manipulation
7063 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7064 * NULL, if you don't care. */
7065 static isds_error build_send_manipulationboxuser_request_check_drop_response(
7066 struct isds_ctx *context, const xmlChar *service_name,
7067 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7068 _Bool honor_aifo_ticket,
7069 struct isds_credentials_delivery *credentials_delivery,
7070 const struct isds_approval *approval, xmlChar **refnumber) {
7071 isds_error err = IE_SUCCESS;
7072 #if HAVE_LIBCURL
7073 xmlNsPtr isds_ns = NULL;
7074 xmlNodePtr request = NULL, node;
7075 xmlDocPtr response = NULL;
7076 #endif
7079 if (!context) return IE_INVALID_CONTEXT;
7080 zfree(context->long_message);
7081 if (credentials_delivery) {
7082 zfree(credentials_delivery->token);
7083 zfree(credentials_delivery->new_user_name);
7085 if (!service_name || service_name[0] == '\0' || !box || !user)
7086 return IE_INVAL;
7089 #if HAVE_LIBCURL
7090 /* Build NewAccessData or similar request */
7091 request = xmlNewNode(NULL, service_name);
7092 if (!request) {
7093 char *service_name_locale = _isds_utf82locale((char *) service_name);
7094 isds_printf_message(context, _("Could not build %s request"),
7095 service_name_locale);
7096 free(service_name_locale);
7097 return IE_ERROR;
7099 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7100 if(!isds_ns) {
7101 isds_log_message(context, _("Could not create ISDS name space"));
7102 xmlFreeNode(request);
7103 return IE_ERROR;
7105 xmlSetNs(request, isds_ns);
7107 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7108 err = insert_DbOwnerInfo(context, box, 0, node);
7109 if (err) goto leave;
7111 INSERT_ELEMENT(node, request, "dbUserInfo");
7112 err = insert_DbUserInfo(context, user, honor_aifo_ticket, node);
7113 if (err) goto leave;
7115 err = insert_credentials_delivery(context, credentials_delivery, request);
7116 if (err) goto leave;
7118 err = insert_GExtApproval(context, approval, request);
7119 if (err) goto leave;
7122 /* Send request and check response*/
7123 err = send_destroy_request_check_response(context,
7124 SERVICE_DB_MANIPULATION, service_name, &request, &response,
7125 refnumber, NULL);
7127 xmlFreeNode(request);
7128 request = NULL;
7130 /* Pick up credentials_delivery if requested */
7131 err = extract_credentials_delivery(context, credentials_delivery, response,
7132 (char *)service_name);
7134 leave:
7135 xmlFreeDoc(response);
7136 if (request) xmlFreeNode(request);
7138 if (!err) {
7139 char *service_name_locale = _isds_utf82locale((char *) service_name);
7140 isds_log(ILF_ISDS, ILL_DEBUG,
7141 _("%s request processed by server successfully.\n"),
7142 service_name_locale);
7143 free(service_name_locale);
7145 #else /* not HAVE_LIBCURL */
7146 err = IE_NOTSUP;
7147 #endif
7149 return err;
7153 /* Assign new user to given box.
7154 * @context is session context
7155 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7156 * members are ignored.
7157 * @user defines new user to add
7158 * @credentials_delivery is NULL if new user's password should be delivered
7159 * off-line to the user. It is valid pointer if user should obtain new
7160 * password on-line on dedicated web server. Then input
7161 * @credentials_delivery.email value is user's e-mail address user must
7162 * provide to dedicated web server together with @credentials_delivery.token.
7163 * The output reallocated token user needs to use to authorize on the web
7164 * server to view his new password. Output reallocated
7165 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7166 * assingned up on this call.
7167 * @approval is optional external approval of box manipulation
7168 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7169 * NULL, if you don't care.*/
7170 isds_error isds_add_user(struct isds_ctx *context,
7171 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7172 struct isds_credentials_delivery *credentials_delivery,
7173 const struct isds_approval *approval, char **refnumber) {
7174 return build_send_manipulationboxuser_request_check_drop_response(context,
7175 BAD_CAST "AddDataBoxUser", box, user, 1, credentials_delivery,
7176 approval, (xmlChar **) refnumber);
7180 /* Remove user assigned to given box.
7181 * @context is session context
7182 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7183 * members are ignored.
7184 * @user identifies user to remove, aifo_ticket member is ignored
7185 * @approval is optional external approval of box manipulation
7186 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7187 * NULL, if you don't care.*/
7188 isds_error isds_delete_user(struct isds_ctx *context,
7189 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7190 const struct isds_approval *approval, char **refnumber) {
7191 return build_send_manipulationboxuser_request_check_drop_response(context,
7192 BAD_CAST "DeleteDataBoxUser", box, user, 0, NULL, approval,
7193 (xmlChar **) refnumber);
7197 /* Get list of boxes in ZIP archive.
7198 * @context is session context
7199 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7200 * System recognizes following values currently: ALL (all boxes), UPG
7201 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7202 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7203 * commercial messages). This argument is a string because specification
7204 * states new values can appear in the future. Not all list types are
7205 * available to all users.
7206 * @buffer is automatically reallocated memory to store the list of boxes. The
7207 * list is zipped CSV file.
7208 * @buffer_length is size of @buffer data in bytes.
7209 * In case of error @buffer will be freed and @buffer_length will be
7210 * undefined.*/
7211 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7212 const char *list_identifier, void **buffer, size_t *buffer_length) {
7213 isds_error err = IE_SUCCESS;
7214 #if HAVE_LIBCURL
7215 xmlNsPtr isds_ns = NULL;
7216 xmlNodePtr request = NULL, node;
7217 xmlDocPtr response = NULL;
7218 xmlXPathContextPtr xpath_ctx = NULL;
7219 xmlXPathObjectPtr result = NULL;
7220 char *string = NULL;
7221 #endif
7224 if (!context) return IE_INVALID_CONTEXT;
7225 zfree(context->long_message);
7226 if (buffer) zfree(*buffer);
7227 if (!buffer || !buffer_length) return IE_INVAL;
7230 #if HAVE_LIBCURL
7231 /* Check if connection is established
7232 * TODO: This check should be done downstairs. */
7233 if (!context->curl) return IE_CONNECTION_CLOSED;
7236 /* Build AuthenticateMessage request */
7237 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7238 if (!request) {
7239 isds_log_message(context,
7240 _("Could not build GetDataBoxList request"));
7241 return IE_ERROR;
7243 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7244 if(!isds_ns) {
7245 isds_log_message(context, _("Could not create ISDS name space"));
7246 xmlFreeNode(request);
7247 return IE_ERROR;
7249 xmlSetNs(request, isds_ns);
7250 INSERT_STRING(request, "dblType", list_identifier);
7252 /* Send request to server and process response */
7253 err = send_destroy_request_check_response(context,
7254 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7255 &response, NULL, NULL);
7256 if (err) goto leave;
7259 /* Extract Base-64 encoded ZIP file */
7260 xpath_ctx = xmlXPathNewContext(response);
7261 if (!xpath_ctx) {
7262 err = IE_ERROR;
7263 goto leave;
7265 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7266 err = IE_ERROR;
7267 goto leave;
7269 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7271 /* Decode non-empty archive */
7272 if (string && string[0] != '\0') {
7273 *buffer_length = _isds_b64decode(string, buffer);
7274 if (*buffer_length == (size_t) -1) {
7275 isds_printf_message(context,
7276 _("Error while Base64-decoding box list archive"));
7277 err = IE_ERROR;
7278 goto leave;
7283 leave:
7284 free(string);
7285 xmlXPathFreeObject(result);
7286 xmlXPathFreeContext(xpath_ctx);
7287 xmlFreeDoc(response);
7288 xmlFreeNode(request);
7290 if (!err) {
7291 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7292 "processed by server successfully.\n"));
7294 #else /* not HAVE_LIBCURL */
7295 err = IE_NOTSUP;
7296 #endif
7298 return err;
7302 /* Build ISDS request of XSD tDbOwnerInfo or tDbPersonalOwnerInfoRequest type,
7303 * send it, check for error code, extract list of results, destroy response
7304 * and log success.
7305 * @context is ISDS session context.
7306 * @service_name is name of SERVICE_DB_SEARCH service
7307 * @pfo_service is false if tDbOwnerInfo request should be built from
7308 * @criteria and corresponding result extracted. It is true if
7309 * tDbPersonalOwnerInfoRequest request should be built. The request and
7310 * response differ subset of significant isds_DbOwnerInfo structure members.
7311 * @criteria is filter. You should fill in at least some members.
7312 * If @pfo_service is false, aifoIsds, address->adCode, address->adDistrict
7313 * members will be ignored. If @pfo_service is true, dbType, ic,
7314 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7315 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
7316 * ignored.
7317 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7318 * possibly empty. Input NULL or valid old structure. The same memebers as
7319 * in described for @criteria argument will be NULL according to @pfo_service
7320 * switch.
7321 * @return:
7322 * IE_SUCCESS if search succeeded, @boxes contains useful data
7323 * IE_NOEXIST if no such box exists, @boxes will be NULL
7324 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7325 * contains still valid data
7326 * other code if something bad happens. @boxes will be NULL. */
7327 static isds_error build_send_findbox_request_check_parse_drop_response(
7328 struct isds_ctx *context, const xmlChar *service_name,
7329 _Bool pfo_service, const struct isds_DbOwnerInfo *criteria,
7330 struct isds_list **boxes) {
7331 isds_error err = IE_SUCCESS;
7332 #if HAVE_LIBCURL
7333 char *service_name_locale = NULL;
7334 _Bool truncated = 0;
7335 xmlNsPtr isds_ns = NULL;
7336 xmlNodePtr request = NULL;
7337 xmlDocPtr response = NULL;
7338 xmlChar *code = NULL, *message = NULL;
7339 xmlNodePtr db_owner_info;
7340 xmlXPathContextPtr xpath_ctx = NULL;
7341 xmlXPathObjectPtr result = NULL;
7342 xmlChar *string = NULL;
7343 #endif
7346 if (!context) return IE_INVALID_CONTEXT;
7347 zfree(context->long_message);
7348 if (!boxes) return IE_INVAL;
7349 isds_list_free(boxes);
7351 if (!criteria) {
7352 return IE_INVAL;
7355 #if HAVE_LIBCURL
7356 /* Check if connection is established
7357 * TODO: This check should be done downstairs. */
7358 if (!context->curl) return IE_CONNECTION_CLOSED;
7359 service_name_locale = _isds_utf82locale((char *) service_name);
7361 /* Build request */
7362 request = xmlNewNode(NULL, service_name);
7363 if (!request) {
7364 isds_printf_message(context, _("Could not build %s request"),
7365 service_name_locale);
7366 free(service_name_locale);
7367 return IE_ERROR;
7369 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7370 if(!isds_ns) {
7371 isds_log_message(context, _("Could not create ISDS name space"));
7372 free(service_name_locale);
7373 xmlFreeNode(request);
7374 return IE_ERROR;
7376 xmlSetNs(request, isds_ns);
7377 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7378 if (!db_owner_info) {
7379 isds_printf_message(context,
7380 _("Could not add dbOwnerInfo child to %s element"),
7381 service_name_locale);
7382 free(service_name_locale);
7383 xmlFreeNode(request);
7384 return IE_ERROR;
7387 err = insert_DbOwnerInfo(context, criteria, pfo_service, db_owner_info);
7388 if (err) goto leave;
7391 /* Send request */
7392 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
7393 service_name_locale);
7394 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7396 /* Destroy request */
7397 xmlFreeNode(request); request = NULL;
7399 if (err) {
7400 isds_log(ILF_ISDS, ILL_DEBUG,
7401 _("Processing ISDS response on %s request failed\n"),
7402 service_name_locale);
7403 goto leave;
7406 /* Check for response status */
7407 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7408 &code, &message, NULL);
7409 if (err) {
7410 isds_log(ILF_ISDS, ILL_DEBUG,
7411 _("ISDS response on %s request is missing status\n"),
7412 service_name_locale);
7413 goto leave;
7416 /* Request processed, but nothing found */
7417 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7418 !xmlStrcmp(code, BAD_CAST "5001")) {
7419 char *code_locale = _isds_utf82locale((char*)code);
7420 char *message_locale = _isds_utf82locale((char*)message);
7421 isds_log(ILF_ISDS, ILL_DEBUG,
7422 _("Server did not find any box on %s request "
7423 "(code=%s, message=%s)\n"), service_name_locale,
7424 code_locale, message_locale);
7425 isds_log_message(context, message_locale);
7426 free(code_locale);
7427 free(message_locale);
7428 err = IE_NOEXIST;
7429 goto leave;
7432 /* Warning, not an error */
7433 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7434 char *code_locale = _isds_utf82locale((char*)code);
7435 char *message_locale = _isds_utf82locale((char*)message);
7436 isds_log(ILF_ISDS, ILL_DEBUG,
7437 _("Server truncated response on %s request "
7438 "(code=%s, message=%s)\n"), service_name_locale,
7439 code_locale, message_locale);
7440 isds_log_message(context, message_locale);
7441 free(code_locale);
7442 free(message_locale);
7443 truncated = 1;
7446 /* Other error */
7447 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7448 char *code_locale = _isds_utf82locale((char*)code);
7449 char *message_locale = _isds_utf82locale((char*)message);
7450 isds_log(ILF_ISDS, ILL_DEBUG,
7451 _("Server refused %s request (code=%s, message=%s)\n"),
7452 service_name_locale, code_locale, message_locale);
7453 isds_log_message(context, message_locale);
7454 free(code_locale);
7455 free(message_locale);
7456 err = IE_ISDS;
7457 goto leave;
7460 xpath_ctx = xmlXPathNewContext(response);
7461 if (!xpath_ctx) {
7462 err = IE_ERROR;
7463 goto leave;
7465 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7466 err = IE_ERROR;
7467 goto leave;
7470 /* Extract boxes if they present */
7471 if (-1 == isds_asprintf((char **)&string,
7472 "/isds:%sResponse/isds:dbResults/isds:dbOwnerInfo",
7473 service_name)) {
7474 err = IE_NOMEM;
7475 goto leave;
7477 result = xmlXPathEvalExpression(string, xpath_ctx);
7478 zfree(string);
7479 if (!result) {
7480 err = IE_ERROR;
7481 goto leave;
7483 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7484 struct isds_list *item, *prev_item = NULL;
7485 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7486 item = calloc(1, sizeof(*item));
7487 if (!item) {
7488 err = IE_NOMEM;
7489 goto leave;
7492 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7493 if (i == 0) *boxes = item;
7494 else prev_item->next = item;
7495 prev_item = item;
7497 xpath_ctx->node = result->nodesetval->nodeTab[i];
7498 err = extract_DbOwnerInfo(context,
7499 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7500 if (err) goto leave;
7504 leave:
7505 if (err) {
7506 isds_list_free(boxes);
7507 } else {
7508 if (truncated) err = IE_2BIG;
7511 free(string);
7512 xmlFreeNode(request);
7513 xmlXPathFreeObject(result);
7514 xmlXPathFreeContext(xpath_ctx);
7516 free(code);
7517 free(message);
7518 xmlFreeDoc(response);
7520 if (!err)
7521 isds_log(ILF_ISDS, ILL_DEBUG,
7522 _("%s request processed by server successfully.\n"),
7523 service_name_locale);
7524 free(service_name_locale);
7525 #else /* not HAVE_LIBCURL */
7526 err = IE_NOTSUP;
7527 #endif
7529 return err;
7533 /* Find boxes suiting given criteria.
7534 * @criteria is filter. You should fill in at least some members. aifoIsds,
7535 * address->adCode, address->adDistrict members are ignored.
7536 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7537 * possibly empty. Input NULL or valid old structure.
7538 * @return:
7539 * IE_SUCCESS if search succeeded, @boxes contains useful data
7540 * IE_NOEXIST if no such box exists, @boxes will be NULL
7541 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7542 * contains still valid data
7543 * other code if something bad happens. @boxes will be NULL. */
7544 isds_error isds_FindDataBox(struct isds_ctx *context,
7545 const struct isds_DbOwnerInfo *criteria,
7546 struct isds_list **boxes) {
7547 return build_send_findbox_request_check_parse_drop_response(context,
7548 BAD_CAST "FindDataBox", 0, criteria, boxes);
7552 /* Find accessible FO-type boxes suiting given criteria.
7553 * @criteria is filter. You should fill in at least some members. dbType, ic,
7554 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7555 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members are ignored.
7556 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7557 * possibly empty. Input NULL or valid old structure.
7558 * @return:
7559 * IE_SUCCESS if search succeeded, @boxes contains useful data
7560 * IE_NOEXIST if no such box exists, @boxes will be NULL
7561 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7562 * contains still valid data
7563 * other code if something bad happens. @boxes will be NULL. */
7564 isds_error isds_FindPersonalDataBox(struct isds_ctx *context,
7565 const struct isds_DbOwnerInfo *criteria,
7566 struct isds_list **boxes) {
7567 return build_send_findbox_request_check_parse_drop_response(context,
7568 BAD_CAST "FindPersonalDataBox", 1, criteria, boxes);
7572 #if HAVE_LIBCURL
7573 /* Convert a string with match markers into a plain string with list of
7574 * pointers to the matches
7575 * @string is an UTF-8 encoded non-constant string with match markers
7576 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7577 * The markers will be removed from the string.
7578 * @starts is a reallocated list of static pointers into the @string pointing
7579 * to places where match start markers occured.
7580 * @ends is a reallocated list of static pointers into the @string pointing
7581 * to places where match end markers occured.
7582 * @return IE_SUCCESS in case of no failure. */
7583 static isds_error interpret_matches(xmlChar *string,
7584 struct isds_list **starts, struct isds_list **ends) {
7585 isds_error err = IE_SUCCESS;
7586 xmlChar *pointer, *destination, *source;
7587 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7589 isds_list_free(starts);
7590 isds_list_free(ends);
7591 if (NULL == starts || NULL == ends) return IE_INVAL;
7592 if (NULL == string) return IE_SUCCESS;
7594 for (pointer = string; *pointer != '\0';) {
7595 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7596 /* Remove the start marker */
7597 for (source = pointer + 14, destination = pointer;
7598 *source != '\0'; source++, destination++) {
7599 *destination = *source;
7601 *destination = '\0';
7602 /* Append the pointer into the list */
7603 item = calloc(1, sizeof(*item));
7604 if (!item) {
7605 err = IE_NOMEM;
7606 goto leave;
7608 item->destructor = (void (*)(void **))NULL;
7609 item->data = pointer;
7610 if (NULL == prev_start) *starts = item;
7611 else prev_start->next = item;
7612 prev_start = item;
7613 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7614 /* Remove the end marker */
7615 for (source = pointer + 12, destination = pointer;
7616 *source != '\0'; source++, destination++) {
7617 *destination = *source;
7619 *destination = '\0';
7620 /* Append the pointer into the list */
7621 item = calloc(1, sizeof(*item));
7622 if (!item) {
7623 err = IE_NOMEM;
7624 goto leave;
7626 item->destructor = (void (*)(void **))NULL;
7627 item->data = pointer;
7628 if (NULL == prev_end) *ends = item;
7629 else prev_end->next = item;
7630 prev_end = item;
7631 } else {
7632 pointer++;
7636 leave:
7637 if (err) {
7638 isds_list_free(starts);
7639 isds_list_free(ends);
7641 return err;
7645 /* Convert isds:dbResult XML tree into structure
7646 * @context is ISDS context.
7647 * @fulltext_result is automatically reallocated found box structure.
7648 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7649 * @collect_matches is true to interpret match markers.
7650 * In case of error @result will be freed. */
7651 static isds_error extract_dbResult(struct isds_ctx *context,
7652 struct isds_fulltext_result **fulltext_result,
7653 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7654 isds_error err = IE_SUCCESS;
7655 xmlXPathObjectPtr result = NULL;
7656 char *string = NULL;
7658 if (NULL == context) return IE_INVALID_CONTEXT;
7659 if (NULL == fulltext_result) return IE_INVAL;
7660 isds_fulltext_result_free(fulltext_result);
7661 if (!xpath_ctx) return IE_INVAL;
7664 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7665 if (NULL == *fulltext_result) {
7666 err = IE_NOMEM;
7667 goto leave;
7670 /* Extract data */
7671 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7673 EXTRACT_STRING("isds:dbType", string);
7674 if (NULL == string) {
7675 err = IE_ISDS;
7676 isds_log_message(context, _("Empty isds:dbType element"));
7677 goto leave;
7679 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7680 if (err) {
7681 if (err == IE_ENUM) {
7682 err = IE_ISDS;
7683 char *string_locale = _isds_utf82locale(string);
7684 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7685 string_locale);
7686 free(string_locale);
7688 goto leave;
7690 zfree(string);
7692 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7693 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7695 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7696 if (err) goto leave;
7698 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7699 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7700 (*fulltext_result)->dbEffectiveOVM);
7702 EXTRACT_STRING("isds:dbSendOptions", string);
7703 if (NULL == string) {
7704 err = IE_ISDS;
7705 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7706 goto leave;
7708 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7709 (*fulltext_result)->active = 1;
7710 (*fulltext_result)->public_sending = 1;
7711 (*fulltext_result)->commercial_sending = 0;
7712 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7713 (*fulltext_result)->active = 1;
7714 (*fulltext_result)->public_sending = 1;
7715 (*fulltext_result)->commercial_sending = 1;
7716 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7717 (*fulltext_result)->active = 1;
7718 (*fulltext_result)->public_sending = 0;
7719 (*fulltext_result)->commercial_sending = 1;
7720 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7721 (*fulltext_result)->active = 1;
7722 (*fulltext_result)->public_sending = 0;
7723 (*fulltext_result)->commercial_sending = 0;
7724 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7725 (*fulltext_result)->active = 0;
7726 (*fulltext_result)->public_sending = 0;
7727 (*fulltext_result)->commercial_sending = 0;
7728 } else {
7729 err = IE_ISDS;
7730 char *string_locale = _isds_utf82locale(string);
7731 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7732 string_locale);
7733 free(string_locale);
7734 goto leave;
7736 zfree(string);
7738 /* Interpret match marks */
7739 if (collect_matches) {
7740 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7741 &((*fulltext_result)->name_match_start),
7742 &((*fulltext_result)->name_match_end));
7743 if (err) goto leave;
7744 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7745 &((*fulltext_result)->address_match_start),
7746 &((*fulltext_result)->address_match_end));
7747 if (err) goto leave;
7750 leave:
7751 if (err) isds_fulltext_result_free(fulltext_result);
7752 free(string);
7753 xmlXPathFreeObject(result);
7754 return err;
7756 #endif /* HAVE_LIBCURL */
7759 /* Find boxes matching a given full-text criteria.
7760 * @context is a session context
7761 * @query is a non-empty string which consists of words to search
7762 * @target selects box attributes to search for @query words. Pass NULL if you
7763 * don't care.
7764 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7765 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7766 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7767 * which is DBTYPE_SYSTEM.
7768 * @page_size defines count of boxes to constitute a response page. It counts
7769 * from zero. Pass NULL to let server to use a default value (50 now).
7770 * @page_number defines ordinar number of the response page to return. It
7771 * counts from zero. Pass NULL to let server to use a default value (0 now).
7772 * @track_matches points to true for marking @query words found in the box
7773 * attributes. It points to false for not marking. Pass NULL to let the server
7774 * to use default value (false now).
7775 * @total_matching_boxes outputs reallocated number of all boxes matching the
7776 * query. Will be pointer to NULL if server did not provide the value.
7777 * Pass NULL if you don't care.
7778 * @current_page_beginning outputs reallocated ordinar number of the first box
7779 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7780 * server did not provide the value. Pass NULL if you don't care.
7781 * @current_page_size outputs reallocated count of boxes in the this @boxes
7782 * page. It will be pointer to NULL if the server did not provide the value.
7783 * Pass NULL if you don't care.
7784 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7785 * is the last one, false if more boxes match, NULL if the server did not
7786 * provude the value. Pass NULL if you don't care.
7787 * @boxes outputs reallocated list of isds_fulltext_result structures,
7788 * possibly empty.
7789 * @return:
7790 * IE_SUCCESS if search succeeded
7791 * IE_2BIG if @page_size is too large
7792 * other code if something bad happens; output arguments will be NULL. */
7793 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7794 const char *query,
7795 const isds_fulltext_target *target,
7796 const isds_DbType *box_type,
7797 const unsigned long int *page_size,
7798 const unsigned long int *page_number,
7799 const _Bool *track_matches,
7800 unsigned long int **total_matching_boxes,
7801 unsigned long int **current_page_beginning,
7802 unsigned long int **current_page_size,
7803 _Bool **last_page,
7804 struct isds_list **boxes) {
7805 isds_error err = IE_SUCCESS;
7806 #if HAVE_LIBCURL
7807 xmlNsPtr isds_ns = NULL;
7808 xmlNodePtr request = NULL;
7809 xmlDocPtr response = NULL;
7810 xmlNodePtr node;
7811 xmlXPathContextPtr xpath_ctx = NULL;
7812 xmlXPathObjectPtr result = NULL;
7813 const xmlChar *static_string = NULL;
7814 xmlChar *string = NULL;
7816 const xmlChar *codes[] = {
7817 BAD_CAST "1004",
7818 BAD_CAST "1152",
7819 BAD_CAST "1153",
7820 BAD_CAST "1154",
7821 BAD_CAST "1155",
7822 BAD_CAST "1156",
7823 BAD_CAST "9002",
7824 NULL
7826 const char *meanings[] = {
7827 N_("You are not allowed to perform the search"),
7828 N_("The query string is empty"),
7829 N_("Searched box ID is malformed"),
7830 N_("Searched organization ID is malformed"),
7831 N_("Invalid input"),
7832 N_("Requested page size is too large"),
7833 N_("Search engine internal error")
7835 const isds_error errors[] = {
7836 IE_ISDS,
7837 IE_INVAL,
7838 IE_INVAL,
7839 IE_INVAL,
7840 IE_INVAL,
7841 IE_2BIG,
7842 IE_ISDS
7844 struct code_map_isds_error map = {
7845 .codes = codes,
7846 .meanings = meanings,
7847 .errors = errors
7849 #endif
7852 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7853 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7854 if (NULL != current_page_size) zfree(*current_page_size);
7855 if (NULL != last_page) zfree(*last_page);
7856 isds_list_free(boxes);
7858 if (NULL == context) return IE_INVALID_CONTEXT;
7859 zfree(context->long_message);
7861 if (NULL == boxes) return IE_INVAL;
7863 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
7864 isds_log_message(context, _("Query string must be non-empty"));
7865 return IE_INVAL;
7868 #if HAVE_LIBCURL
7869 /* Check if connection is established
7870 * TODO: This check should be done downstairs. */
7871 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7873 /* Build FindDataBox request */
7874 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
7875 if (NULL == request) {
7876 isds_log_message(context,
7877 _("Could not build ISDSSearch2 request"));
7878 return IE_ERROR;
7880 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7881 if(NULL == isds_ns) {
7882 isds_log_message(context, _("Could not create ISDS name space"));
7883 xmlFreeNode(request);
7884 return IE_ERROR;
7886 xmlSetNs(request, isds_ns);
7888 INSERT_STRING(request, "searchText", query);
7890 if (NULL != target) {
7891 static_string = isds_fulltext_target2string(*(target));
7892 if (NULL == static_string) {
7893 isds_printf_message(context, _("Invalid target value: %d"),
7894 *(target));
7895 err = IE_ENUM;
7896 goto leave;
7899 INSERT_STRING(request, "searchType", static_string);
7900 static_string = NULL;
7902 if (NULL != box_type) {
7903 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7904 if (DBTYPE_SYSTEM == *box_type) {
7905 static_string = BAD_CAST "ALL";
7906 } else if (DBTYPE_OVM_MAIN == *box_type) {
7907 static_string = BAD_CAST "OVM_MAIN";
7908 } else {
7909 static_string = isds_DbType2string(*(box_type));
7910 if (NULL == static_string) {
7911 isds_printf_message(context, _("Invalid box type value: %d"),
7912 *(box_type));
7913 err = IE_ENUM;
7914 goto leave;
7918 INSERT_STRING(request, "searchScope", static_string);
7919 static_string = NULL;
7921 INSERT_ULONGINT(request, "page", page_number, string);
7922 INSERT_ULONGINT(request, "pageSize", page_size, string);
7923 INSERT_BOOLEAN(request, "highlighting", track_matches);
7925 /* Send request and check response */
7926 err = send_destroy_request_check_response(context,
7927 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
7928 &request, &response, NULL, &map);
7929 if (err) goto leave;
7931 /* Parse response */
7932 xpath_ctx = xmlXPathNewContext(response);
7933 if (NULL == xpath_ctx) {
7934 err = IE_ERROR;
7935 goto leave;
7937 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7938 err = IE_ERROR;
7939 goto leave;
7941 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
7942 xpath_ctx);
7943 if (!result) {
7944 err = IE_ERROR;
7945 goto leave;
7947 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7948 isds_log_message(context, _("Missing ISDSSearch2 element"));
7949 err = IE_ISDS;
7950 goto leave;
7952 if (result->nodesetval->nodeNr > 1) {
7953 isds_log_message(context, _("Multiple ISDSSearch2 element"));
7954 err = IE_ISDS;
7955 goto leave;
7957 xpath_ctx->node = result->nodesetval->nodeTab[0];
7958 xmlXPathFreeObject(result); result = NULL;
7961 /* Extract counters */
7962 if (NULL != total_matching_boxes) {
7963 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
7965 if (NULL != current_page_size) {
7966 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
7968 if (NULL != current_page_beginning) {
7969 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
7971 if (NULL != last_page) {
7972 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
7974 xmlXPathFreeObject(result); result = NULL;
7976 /* Extract boxes if they present */
7977 result = xmlXPathEvalExpression(BAD_CAST
7978 "isds:dbResults/isds:dbResult", xpath_ctx);
7979 if (NULL == result) {
7980 err = IE_ERROR;
7981 goto leave;
7983 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7984 struct isds_list *item, *prev_item = NULL;
7985 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7986 item = calloc(1, sizeof(*item));
7987 if (!item) {
7988 err = IE_NOMEM;
7989 goto leave;
7992 item->destructor = (void (*)(void **))isds_fulltext_result_free;
7993 if (i == 0) *boxes = item;
7994 else prev_item->next = item;
7995 prev_item = item;
7997 xpath_ctx->node = result->nodesetval->nodeTab[i];
7998 err = extract_dbResult(context,
7999 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
8000 (NULL == track_matches) ? 0 : *track_matches);
8001 if (err) goto leave;
8005 leave:
8006 if (err) {
8007 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
8008 if (NULL != current_page_beginning) zfree(*current_page_beginning);
8009 if (NULL != current_page_size) zfree(*current_page_size);
8010 if (NULL != last_page) zfree(*last_page);
8011 isds_list_free(boxes);
8014 free(string);
8015 xmlFreeNode(request);
8016 xmlXPathFreeObject(result);
8017 xmlXPathFreeContext(xpath_ctx);
8018 xmlFreeDoc(response);
8020 if (!err)
8021 isds_log(ILF_ISDS, ILL_DEBUG,
8022 _("ISDSSearch2 request processed by server successfully.\n"));
8023 #else /* not HAVE_LIBCURL */
8024 err = IE_NOTSUP;
8025 #endif
8027 return err;
8031 /* Get status of a box.
8032 * @context is ISDS session context.
8033 * @box_id is UTF-8 encoded box identifier as zero terminated string
8034 * @box_status is return value of box status.
8035 * @return:
8036 * IE_SUCCESS if box has been found and its status retrieved
8037 * IE_NOEXIST if box is not known to ISDS server
8038 * or other appropriate error.
8039 * You can use isds_DbState to enumerate box status. However out of enum
8040 * range value can be returned too. This is feature because ISDS
8041 * specification leaves the set of values open.
8042 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
8043 * the box has been deleted, but ISDS still lists its former existence. */
8044 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
8045 long int *box_status) {
8046 isds_error err = IE_SUCCESS;
8047 #if HAVE_LIBCURL
8048 xmlNsPtr isds_ns = NULL;
8049 xmlNodePtr request = NULL, db_id;
8050 xmlDocPtr response = NULL;
8051 xmlXPathContextPtr xpath_ctx = NULL;
8052 xmlXPathObjectPtr result = NULL;
8053 xmlChar *string = NULL;
8055 const xmlChar *codes[] = {
8056 BAD_CAST "5001",
8057 BAD_CAST "1007",
8058 BAD_CAST "2011",
8059 NULL
8061 const char *meanings[] = {
8062 "The box does not exist",
8063 "Box ID is malformed",
8064 "Box ID malformed",
8066 const isds_error errors[] = {
8067 IE_NOEXIST,
8068 IE_INVAL,
8069 IE_INVAL,
8071 struct code_map_isds_error map = {
8072 .codes = codes,
8073 .meanings = meanings,
8074 .errors = errors
8076 #endif
8078 if (!context) return IE_INVALID_CONTEXT;
8079 zfree(context->long_message);
8080 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
8082 #if HAVE_LIBCURL
8083 /* Check if connection is established
8084 * TODO: This check should be done downstairs. */
8085 if (!context->curl) return IE_CONNECTION_CLOSED;
8088 /* Build CheckDataBox request */
8089 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
8090 if (!request) {
8091 isds_log_message(context,
8092 _("Could build CheckDataBox request"));
8093 return IE_ERROR;
8095 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8096 if(!isds_ns) {
8097 isds_log_message(context, _("Could not create ISDS name space"));
8098 xmlFreeNode(request);
8099 return IE_ERROR;
8101 xmlSetNs(request, isds_ns);
8102 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
8103 if (!db_id) {
8104 isds_log_message(context, _("Could not add dbID child to "
8105 "CheckDataBox element"));
8106 xmlFreeNode(request);
8107 return IE_ERROR;
8111 /* Send request and check response*/
8112 err = send_destroy_request_check_response(context,
8113 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
8114 &request, &response, NULL, &map);
8115 if (err) goto leave;
8118 /* Extract data */
8119 xpath_ctx = xmlXPathNewContext(response);
8120 if (!xpath_ctx) {
8121 err = IE_ERROR;
8122 goto leave;
8124 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8125 err = IE_ERROR;
8126 goto leave;
8128 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
8129 xpath_ctx);
8130 if (!result) {
8131 err = IE_ERROR;
8132 goto leave;
8134 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8135 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
8136 err = IE_ISDS;
8137 goto leave;
8139 if (result->nodesetval->nodeNr > 1) {
8140 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
8141 err = IE_ISDS;
8142 goto leave;
8144 xpath_ctx->node = result->nodesetval->nodeTab[0];
8145 xmlXPathFreeObject(result); result = NULL;
8147 EXTRACT_LONGINT("isds:dbState", box_status, 1);
8150 leave:
8151 free(string);
8152 xmlXPathFreeObject(result);
8153 xmlXPathFreeContext(xpath_ctx);
8155 xmlFreeDoc(response);
8157 if (!err)
8158 isds_log(ILF_ISDS, ILL_DEBUG,
8159 _("CheckDataBox request processed by server successfully.\n"));
8160 #else /* not HAVE_LIBCURL */
8161 err = IE_NOTSUP;
8162 #endif
8164 return err;
8168 #if HAVE_LIBCURL
8169 /* Convert XSD:tdbPeriod XML tree into structure
8170 * @context is ISDS context.
8171 * @period is automatically reallocated found box status period structure.
8172 * @xpath_ctx is XPath context with current node as element of
8173 * XSD:tDbPeriod type.
8174 * In case of error @period will be freed. */
8175 static isds_error extract_Period(struct isds_ctx *context,
8176 struct isds_box_state_period **period, xmlXPathContextPtr xpath_ctx) {
8177 isds_error err = IE_SUCCESS;
8178 xmlXPathObjectPtr result = NULL;
8179 char *string = NULL;
8180 long int *dbState_ptr;
8182 if (NULL == context) return IE_INVALID_CONTEXT;
8183 if (NULL == period) return IE_INVAL;
8184 isds_box_state_period_free(period);
8185 if (!xpath_ctx) return IE_INVAL;
8188 *period = calloc(1, sizeof(**period));
8189 if (NULL == *period) {
8190 err = IE_NOMEM;
8191 goto leave;
8194 /* Extract data */
8195 EXTRACT_STRING("isds:PeriodFrom", string);
8196 if (NULL == string) {
8197 err = IE_XML;
8198 isds_log_message(context,
8199 _("Could not find PeriodFrom element value"));
8200 goto leave;
8202 err = timestring2static_timeval((xmlChar *) string,
8203 &((*period)->from));
8204 if (err) {
8205 char *string_locale = _isds_utf82locale(string);
8206 if (err == IE_DATE) err = IE_ISDS;
8207 isds_printf_message(context,
8208 _("Could not convert PeriodFrom as ISO time: %s"),
8209 string_locale);
8210 free(string_locale);
8211 goto leave;
8213 zfree(string);
8215 EXTRACT_STRING("isds:PeriodTo", string);
8216 if (NULL == string) {
8217 err = IE_XML;
8218 isds_log_message(context,
8219 _("Could not find PeriodTo element value"));
8220 goto leave;
8222 err = timestring2static_timeval((xmlChar *) string,
8223 &((*period)->to));
8224 if (err) {
8225 char *string_locale = _isds_utf82locale(string);
8226 if (err == IE_DATE) err = IE_ISDS;
8227 isds_printf_message(context,
8228 _("Could not convert PeriodTo as ISO time: %s"),
8229 string_locale);
8230 free(string_locale);
8231 goto leave;
8233 zfree(string);
8235 dbState_ptr = &((*period)->dbState);
8236 EXTRACT_LONGINT("isds:DbState", dbState_ptr, 1);
8238 leave:
8239 if (err) isds_box_state_period_free(period);
8240 free(string);
8241 xmlXPathFreeObject(result);
8242 return err;
8244 #endif /* HAVE_LIBCURL */
8247 /* Get history of box state changes.
8248 * @context is ISDS session context.
8249 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8250 * @from_time is first second of history to return in @history. Server ignores
8251 * subseconds. NULL means time of creating the box.
8252 * @to_time is last second of history to return in @history. Server ignores
8253 * subseconds. It's valid to have the @from_time equaled to the @to_time. The
8254 * interval is closed from both ends. NULL means now.
8255 * @history outputs auto-reallocated list of pointers to struct
8256 * isds_box_state_period. Each item describes a continues time when the box
8257 * was in one state. The state is 1 for accessible box. Otherwise the box
8258 * is inaccessible (priviledged users will get exact box state as enumerated
8259 * in isds_DbState, other users 0).
8260 * @return:
8261 * IE_SUCCESS if the history has been obtained correctly,
8262 * or other appropriate error. Please note that server allows to retrieve
8263 * the history only to some users. */
8264 isds_error isds_get_box_state_history(struct isds_ctx *context,
8265 const char *box_id,
8266 const struct timeval *from_time, const struct timeval *to_time,
8267 struct isds_list **history) {
8268 isds_error err = IE_SUCCESS;
8269 #if HAVE_LIBCURL
8270 char *box_id_locale = NULL;
8271 xmlNodePtr request = NULL, node;
8272 xmlNsPtr isds_ns = NULL;
8273 xmlChar *string = NULL;
8275 xmlDocPtr response = NULL;
8276 xmlXPathContextPtr xpath_ctx = NULL;
8277 xmlXPathObjectPtr result = NULL;
8278 #endif
8280 if (!context) return IE_INVALID_CONTEXT;
8281 zfree(context->long_message);
8283 /* Free output argument */
8284 isds_list_free(history);
8286 #if HAVE_LIBCURL
8287 /* Check if connection is established */
8288 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8290 /* ??? XML schema allows empty box ID, textual documentation
8291 * requries the value. */
8292 /* Allow undefined box_id */
8293 if (NULL != box_id) {
8294 box_id_locale = _isds_utf82locale((char*)box_id);
8295 if (NULL == box_id_locale) {
8296 err = IE_NOMEM;
8297 goto leave;
8301 /* Build request */
8302 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxActivityStatus");
8303 if (NULL == request) {
8304 isds_printf_message(context,
8305 _("Could not build GetDataBoxActivityStatus request "
8306 "for %s box"),
8307 box_id_locale);
8308 err = IE_ERROR;
8309 goto leave;
8311 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8312 if(!isds_ns) {
8313 isds_log_message(context, _("Could not create ISDS name space"));
8314 err = IE_ERROR;
8315 goto leave;
8317 xmlSetNs(request, isds_ns);
8319 /* Add mandatory XSD:tIdDbInput child */
8320 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8321 /* Add times elements only when defined */
8322 /* ???: XML schema requires the values, textual documentation does not. */
8323 if (from_time) {
8324 err = timeval2timestring(from_time, &string);
8325 if (err) {
8326 isds_log_message(context,
8327 _("Could not convert `from_time' argument to ISO time "
8328 "string"));
8329 goto leave;
8331 INSERT_STRING(request, "baFrom", string);
8332 zfree(string);
8334 if (to_time) {
8335 err = timeval2timestring(to_time, &string);
8336 if (err) {
8337 isds_log_message(context,
8338 _("Could not convert `to_time' argument to ISO time "
8339 "string"));
8340 goto leave;
8342 INSERT_STRING(request, "baTo", string);
8343 zfree(string);
8346 /* Send request and check response*/
8347 err = send_destroy_request_check_response(context,
8348 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxActivityStatus",
8349 &request, &response, NULL, NULL);
8350 if (err) goto leave;
8353 /* Extract data */
8354 /* Set context to the root */
8355 xpath_ctx = xmlXPathNewContext(response);
8356 if (!xpath_ctx) {
8357 err = IE_ERROR;
8358 goto leave;
8360 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8361 err = IE_ERROR;
8362 goto leave;
8364 result = xmlXPathEvalExpression(BAD_CAST "/isds:GetDataBoxActivityStatusResponse",
8365 xpath_ctx);
8366 if (!result) {
8367 err = IE_ERROR;
8368 goto leave;
8370 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8371 isds_log_message(context, _("Missing GetDataBoxActivityStatusResponse element"));
8372 err = IE_ISDS;
8373 goto leave;
8375 if (result->nodesetval->nodeNr > 1) {
8376 isds_log_message(context, _("Multiple GetDataBoxActivityStatusResponse element"));
8377 err = IE_ISDS;
8378 goto leave;
8380 xpath_ctx->node = result->nodesetval->nodeTab[0];
8381 xmlXPathFreeObject(result); result = NULL;
8383 /* Ignore dbID, it's the same as the input argument. */
8385 /* Extract records */
8386 if (NULL == history) goto leave;
8387 result = xmlXPathEvalExpression(BAD_CAST "isds:Periods/isds:Period",
8388 xpath_ctx);
8389 if (!result) {
8390 err = IE_ERROR;
8391 goto leave;
8393 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8394 struct isds_list *prev_item = NULL;
8396 /* Iterate over all records */
8397 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8398 struct isds_list *item;
8400 /* Prepare structure */
8401 item = calloc(1, sizeof(*item));
8402 if (!item) {
8403 err = IE_NOMEM;
8404 goto leave;
8406 item->destructor = (void(*)(void**))isds_box_state_period_free;
8407 if (i == 0) *history = item;
8408 else prev_item->next = item;
8409 prev_item = item;
8411 /* Extract it */
8412 xpath_ctx->node = result->nodesetval->nodeTab[i];
8413 err = extract_Period(context,
8414 (struct isds_box_state_period **) (&item->data),
8415 xpath_ctx);
8416 if (err) goto leave;
8420 leave:
8421 if (!err) {
8422 isds_log(ILF_ISDS, ILL_DEBUG,
8423 _("GetDataBoxActivityStatus request for %s box "
8424 "processed by server successfully.\n"), box_id_locale);
8426 if (err) {
8427 isds_list_free(history);
8430 free(box_id_locale);
8431 xmlXPathFreeObject(result);
8432 xmlXPathFreeContext(xpath_ctx);
8433 xmlFreeDoc(response);
8435 #else /* not HAVE_LIBCURL */
8436 err = IE_NOTSUP;
8437 #endif
8439 return err;
8443 /* Get list of permissions to send commercial messages.
8444 * @context is ISDS session context.
8445 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
8446 * @permissions is a reallocated list of permissions (struct
8447 * isds_commercial_permission*) to send commercial messages from @box_id. The
8448 * order of permissions is significant as the server applies the permissions
8449 * and associated pre-paid credits in the order. Empty list means no
8450 * permission.
8451 * @return:
8452 * IE_SUCCESS if the list has been obtained correctly,
8453 * or other appropriate error. */
8454 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
8455 const char *box_id, struct isds_list **permissions) {
8456 isds_error err = IE_SUCCESS;
8457 #if HAVE_LIBCURL
8458 xmlDocPtr response = NULL;
8459 xmlXPathContextPtr xpath_ctx = NULL;
8460 xmlXPathObjectPtr result = NULL;
8461 #endif
8463 if (!context) return IE_INVALID_CONTEXT;
8464 zfree(context->long_message);
8465 if (NULL == permissions) return IE_INVAL;
8466 isds_list_free(permissions);
8467 if (NULL == box_id) return IE_INVAL;
8469 #if HAVE_LIBCURL
8470 /* Check if connection is established */
8471 if (!context->curl) return IE_CONNECTION_CLOSED;
8473 /* Do request and check for success */
8474 err = build_send_dbid_request_check_response(context,
8475 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
8476 BAD_CAST box_id, NULL, &response, NULL);
8477 if (!err) {
8478 isds_log(ILF_ISDS, ILL_DEBUG,
8479 _("PDZInfo request processed by server successfully.\n"));
8482 /* Extract data */
8483 /* Prepare structure */
8484 xpath_ctx = xmlXPathNewContext(response);
8485 if (!xpath_ctx) {
8486 err = IE_ERROR;
8487 goto leave;
8489 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8490 err = IE_ERROR;
8491 goto leave;
8494 /* Set context node */
8495 result = xmlXPathEvalExpression(BAD_CAST
8496 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8497 xpath_ctx);
8498 if (!result) {
8499 err = IE_ERROR;
8500 goto leave;
8502 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8503 struct isds_list *prev_item = NULL;
8505 /* Iterate over all permission records */
8506 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8507 struct isds_list *item;
8509 /* Prepare structure */
8510 item = calloc(1, sizeof(*item));
8511 if (!item) {
8512 err = IE_NOMEM;
8513 goto leave;
8515 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8516 if (i == 0) *permissions = item;
8517 else prev_item->next = item;
8518 prev_item = item;
8520 /* Extract it */
8521 xpath_ctx->node = result->nodesetval->nodeTab[i];
8522 err = extract_DbPDZRecord(context,
8523 (struct isds_commercial_permission **) (&item->data),
8524 xpath_ctx);
8525 if (err) goto leave;
8529 leave:
8530 if (err) {
8531 isds_list_free(permissions);
8534 xmlXPathFreeObject(result);
8535 xmlXPathFreeContext(xpath_ctx);
8536 xmlFreeDoc(response);
8538 #else /* not HAVE_LIBCURL */
8539 err = IE_NOTSUP;
8540 #endif
8542 return err;
8546 /* Get details about credit for sending pre-paid commercial messages.
8547 * @context is ISDS session context.
8548 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8549 * @from_date is first day of credit history to return in @history. Only
8550 * tm_year, tm_mon and tm_mday carry sane value.
8551 * @to_date is last day of credit history to return in @history. Only
8552 * tm_year, tm_mon and tm_mday carry sane value.
8553 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8554 * if you don't care. This and all other credit values are integers in
8555 * hundredths of Czech Crowns.
8556 * @email outputs notification e-mail address where notifications about credit
8557 * are sent. This is automatically reallocated string. Pass NULL if you don't
8558 * care. It can return NULL if no address is defined.
8559 * @history outputs auto-reallocated list of pointers to struct
8560 * isds_credit_event. Events in closed interval @from_time to @to_time are
8561 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8562 * are sorted by time.
8563 * @return:
8564 * IE_SUCCESS if the credit details have been obtained correctly,
8565 * or other appropriate error. Please note that server allows to retrieve
8566 * only limited history of events. */
8567 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8568 const char *box_id,
8569 const struct tm *from_date, const struct tm *to_date,
8570 long int *credit, char **email, struct isds_list **history) {
8571 isds_error err = IE_SUCCESS;
8572 #if HAVE_LIBCURL
8573 char *box_id_locale = NULL;
8574 xmlNodePtr request = NULL, node;
8575 xmlNsPtr isds_ns = NULL;
8576 xmlChar *string = NULL;
8578 xmlDocPtr response = NULL;
8579 xmlXPathContextPtr xpath_ctx = NULL;
8580 xmlXPathObjectPtr result = NULL;
8582 const xmlChar *codes[] = {
8583 BAD_CAST "1004",
8584 BAD_CAST "2011",
8585 BAD_CAST "1093",
8586 BAD_CAST "1137",
8587 BAD_CAST "1058",
8588 NULL
8590 const char *meanings[] = {
8591 "Insufficient priviledges for the box",
8592 "The box does not exist",
8593 "Date is too long (history is not available after 15 months)",
8594 "Interval is too long (limit is 3 months)",
8595 "Invalid date"
8597 const isds_error errors[] = {
8598 IE_ISDS,
8599 IE_NOEXIST,
8600 IE_DATE,
8601 IE_DATE,
8602 IE_DATE,
8604 struct code_map_isds_error map = {
8605 .codes = codes,
8606 .meanings = meanings,
8607 .errors = errors
8609 #endif
8611 if (!context) return IE_INVALID_CONTEXT;
8612 zfree(context->long_message);
8614 /* Free output argument */
8615 if (NULL != credit) *credit = 0;
8616 if (NULL != email) zfree(*email);
8617 isds_list_free(history);
8619 if (NULL == box_id) return IE_INVAL;
8621 #if HAVE_LIBCURL
8622 /* Check if connection is established */
8623 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8625 box_id_locale = _isds_utf82locale((char*)box_id);
8626 if (NULL == box_id_locale) {
8627 err = IE_NOMEM;
8628 goto leave;
8631 /* Build request */
8632 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8633 if (NULL == request) {
8634 isds_printf_message(context,
8635 _("Could not build DataBoxCreditInfo request for %s box"),
8636 box_id_locale);
8637 err = IE_ERROR;
8638 goto leave;
8640 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8641 if(!isds_ns) {
8642 isds_log_message(context, _("Could not create ISDS name space"));
8643 err = IE_ERROR;
8644 goto leave;
8646 xmlSetNs(request, isds_ns);
8648 /* Add mandatory XSD:tIdDbInput child */
8649 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8650 /* Add mandatory dates elements with optional values */
8651 if (from_date) {
8652 err = tm2datestring(from_date, &string);
8653 if (err) {
8654 isds_log_message(context,
8655 _("Could not convert `from_date' argument to ISO date "
8656 "string"));
8657 goto leave;
8659 INSERT_STRING(request, "ciFromDate", string);
8660 zfree(string);
8661 } else {
8662 INSERT_STRING(request, "ciFromDate", NULL);
8664 if (to_date) {
8665 err = tm2datestring(to_date, &string);
8666 if (err) {
8667 isds_log_message(context,
8668 _("Could not convert `to_date' argument to ISO date "
8669 "string"));
8670 goto leave;
8672 INSERT_STRING(request, "ciTodate", string);
8673 zfree(string);
8674 } else {
8675 INSERT_STRING(request, "ciTodate", NULL);
8678 /* Send request and check response*/
8679 err = send_destroy_request_check_response(context,
8680 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8681 &request, &response, NULL, &map);
8682 if (err) goto leave;
8685 /* Extract data */
8686 /* Set context to the root */
8687 xpath_ctx = xmlXPathNewContext(response);
8688 if (!xpath_ctx) {
8689 err = IE_ERROR;
8690 goto leave;
8692 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8693 err = IE_ERROR;
8694 goto leave;
8696 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8697 xpath_ctx);
8698 if (!result) {
8699 err = IE_ERROR;
8700 goto leave;
8702 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8703 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8704 err = IE_ISDS;
8705 goto leave;
8707 if (result->nodesetval->nodeNr > 1) {
8708 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8709 err = IE_ISDS;
8710 goto leave;
8712 xpath_ctx->node = result->nodesetval->nodeTab[0];
8713 xmlXPathFreeObject(result); result = NULL;
8715 /* Extract common data */
8716 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8717 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8719 /* Extract records */
8720 if (NULL == history) goto leave;
8721 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8722 xpath_ctx);
8723 if (!result) {
8724 err = IE_ERROR;
8725 goto leave;
8727 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8728 struct isds_list *prev_item = NULL;
8730 /* Iterate over all records */
8731 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8732 struct isds_list *item;
8734 /* Prepare structure */
8735 item = calloc(1, sizeof(*item));
8736 if (!item) {
8737 err = IE_NOMEM;
8738 goto leave;
8740 item->destructor = (void(*)(void**))isds_credit_event_free;
8741 if (i == 0) *history = item;
8742 else prev_item->next = item;
8743 prev_item = item;
8745 /* Extract it */
8746 xpath_ctx->node = result->nodesetval->nodeTab[i];
8747 err = extract_CiRecord(context,
8748 (struct isds_credit_event **) (&item->data),
8749 xpath_ctx);
8750 if (err) goto leave;
8754 leave:
8755 if (!err) {
8756 isds_log(ILF_ISDS, ILL_DEBUG,
8757 _("DataBoxCreditInfo request processed by server successfully.\n"));
8759 if (err) {
8760 isds_list_free(history);
8761 if (NULL != email) zfree(*email)
8764 free(box_id_locale);
8765 xmlXPathFreeObject(result);
8766 xmlXPathFreeContext(xpath_ctx);
8767 xmlFreeDoc(response);
8769 #else /* not HAVE_LIBCURL */
8770 err = IE_NOTSUP;
8771 #endif
8773 return err;
8777 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8778 * code, destroy response and log success.
8779 * @context is ISDS session context.
8780 * @service_name is name of SERVICE_DB_MANIPULATION service
8781 * @box_id is UTF-8 encoded box identifier as zero terminated string
8782 * @approval is optional external approval of box manipulation
8783 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8784 * NULL, if you don't care. */
8785 static isds_error build_send_manipulationdbid_request_check_drop_response(
8786 struct isds_ctx *context, const xmlChar *service_name,
8787 const xmlChar *box_id, const struct isds_approval *approval,
8788 xmlChar **refnumber) {
8789 isds_error err = IE_SUCCESS;
8790 #if HAVE_LIBCURL
8791 xmlDocPtr response = NULL;
8792 #endif
8794 if (!context) return IE_INVALID_CONTEXT;
8795 zfree(context->long_message);
8796 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8798 #if HAVE_LIBCURL
8799 /* Check if connection is established */
8800 if (!context->curl) return IE_CONNECTION_CLOSED;
8802 /* Do request and check for success */
8803 err = build_send_dbid_request_check_response(context,
8804 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8805 &response, refnumber);
8806 xmlFreeDoc(response);
8808 if (!err) {
8809 char *service_name_locale = _isds_utf82locale((char *) service_name);
8810 isds_log(ILF_ISDS, ILL_DEBUG,
8811 _("%s request processed by server successfully.\n"),
8812 service_name_locale);
8813 free(service_name_locale);
8815 #else /* not HAVE_LIBCURL */
8816 err = IE_NOTSUP;
8817 #endif
8819 return err;
8823 /* Switch box into state where box can receive commercial messages (off by
8824 * default)
8825 * @context is ISDS session context.
8826 * @box_id is UTF-8 encoded box identifier as zero terminated string
8827 * @allow is true for enable, false for disable commercial messages income
8828 * @approval is optional external approval of box manipulation
8829 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8830 * NULL, if you don't care. */
8831 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8832 const char *box_id, const _Bool allow,
8833 const struct isds_approval *approval, char **refnumber) {
8834 return build_send_manipulationdbid_request_check_drop_response(context,
8835 (allow) ? BAD_CAST "SetOpenAddressing" :
8836 BAD_CAST "ClearOpenAddressing",
8837 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8841 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8842 * message acceptance). This is just a box permission. Sender must apply
8843 * such role by sending each message.
8844 * @context is ISDS session context.
8845 * @box_id is UTF-8 encoded box identifier as zero terminated string
8846 * @allow is true for enable, false for disable OVM role permission
8847 * @approval is optional external approval of box manipulation
8848 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8849 * NULL, if you don't care. */
8850 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
8851 const char *box_id, const _Bool allow,
8852 const struct isds_approval *approval, char **refnumber) {
8853 return build_send_manipulationdbid_request_check_drop_response(context,
8854 (allow) ? BAD_CAST "SetEffectiveOVM" :
8855 BAD_CAST "ClearEffectiveOVM",
8856 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8860 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8861 * code, destroy response and log success.
8862 * @context is ISDS session context.
8863 * @service_name is name of SERVICE_DB_MANIPULATION service
8864 * @owner is structure describing box. aifoIsds, address->adCode,
8865 * address->adDistrict members are ignored.
8866 * @approval is optional external approval of box manipulation
8867 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8868 * NULL, if you don't care. */
8869 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8870 struct isds_ctx *context, const xmlChar *service_name,
8871 const struct isds_DbOwnerInfo *owner,
8872 const struct isds_approval *approval, xmlChar **refnumber) {
8873 isds_error err = IE_SUCCESS;
8874 #if HAVE_LIBCURL
8875 char *service_name_locale = NULL;
8876 xmlNodePtr request = NULL, db_owner_info;
8877 xmlNsPtr isds_ns = NULL;
8878 #endif
8881 if (!context) return IE_INVALID_CONTEXT;
8882 zfree(context->long_message);
8883 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8885 #if HAVE_LIBCURL
8886 service_name_locale = _isds_utf82locale((char*)service_name);
8887 if (!service_name_locale) {
8888 err = IE_NOMEM;
8889 goto leave;
8892 /* Build request */
8893 request = xmlNewNode(NULL, service_name);
8894 if (!request) {
8895 isds_printf_message(context,
8896 _("Could not build %s request"), service_name_locale);
8897 err = IE_ERROR;
8898 goto leave;
8900 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8901 if(!isds_ns) {
8902 isds_log_message(context, _("Could not create ISDS name space"));
8903 err = IE_ERROR;
8904 goto leave;
8906 xmlSetNs(request, isds_ns);
8909 /* Add XSD:tOwnerInfoInput child*/
8910 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8911 err = insert_DbOwnerInfo(context, owner, 0, db_owner_info);
8912 if (err) goto leave;
8914 /* Add XSD:gExtApproval*/
8915 err = insert_GExtApproval(context, approval, request);
8916 if (err) goto leave;
8918 /* Send it to server and process response */
8919 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8920 service_name, &request, refnumber);
8922 leave:
8923 xmlFreeNode(request);
8924 free(service_name_locale);
8925 #else /* not HAVE_LIBCURL */
8926 err = IE_NOTSUP;
8927 #endif
8929 return err;
8933 /* Switch box accessibility state on request of box owner.
8934 * Despite the name, owner must do the request off-line. This function is
8935 * designed for such off-line meeting points (e.g. Czech POINT).
8936 * @context is ISDS session context.
8937 * @box identifies box to switch accessibility state. aifoIsds,
8938 * address->adCode, address->adDistrict members are ignored.
8939 * @allow is true for making accessible, false to disallow access.
8940 * @approval is optional external approval of box manipulation
8941 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8942 * NULL, if you don't care. */
8943 isds_error isds_switch_box_accessibility_on_owner_request(
8944 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8945 const _Bool allow, const struct isds_approval *approval,
8946 char **refnumber) {
8947 return build_send_manipulationdbowner_request_check_drop_response(context,
8948 (allow) ? BAD_CAST "EnableOwnDataBox" :
8949 BAD_CAST "DisableOwnDataBox",
8950 box, approval, (xmlChar **) refnumber);
8954 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8955 * date.
8956 * @context is ISDS session context.
8957 * @box identifies box to switch accessibility state. aifoIsds,
8958 * address->adCode, address->adDistrict members are ignored.
8959 * @since is date since accessibility has been denied. This can be past too.
8960 * Only tm_year, tm_mon and tm_mday carry sane value.
8961 * @approval is optional external approval of box manipulation
8962 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8963 * NULL, if you don't care. */
8964 isds_error isds_disable_box_accessibility_externaly(
8965 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8966 const struct tm *since, const struct isds_approval *approval,
8967 char **refnumber) {
8968 isds_error err = IE_SUCCESS;
8969 #if HAVE_LIBCURL
8970 char *service_name_locale = NULL;
8971 xmlNodePtr request = NULL, node;
8972 xmlNsPtr isds_ns = NULL;
8973 xmlChar *string = NULL;
8974 #endif
8977 if (!context) return IE_INVALID_CONTEXT;
8978 zfree(context->long_message);
8979 if (!box || !since) return IE_INVAL;
8981 #if HAVE_LIBCURL
8982 /* Build request */
8983 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
8984 if (!request) {
8985 isds_printf_message(context,
8986 _("Could not build %s request"), "DisableDataBoxExternally");
8987 err = IE_ERROR;
8988 goto leave;
8990 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8991 if(!isds_ns) {
8992 isds_log_message(context, _("Could not create ISDS name space"));
8993 err = IE_ERROR;
8994 goto leave;
8996 xmlSetNs(request, isds_ns);
8999 /* Add @box identification */
9000 INSERT_ELEMENT(node, request, "dbOwnerInfo");
9001 err = insert_DbOwnerInfo(context, box, 0, node);
9002 if (err) goto leave;
9004 /* Add @since date */
9005 err = tm2datestring(since, &string);
9006 if(err) {
9007 isds_log_message(context,
9008 _("Could not convert `since' argument to ISO date string"));
9009 goto leave;
9011 INSERT_STRING(request, "dbOwnerDisableDate", string);
9012 zfree(string);
9014 /* Add @approval */
9015 err = insert_GExtApproval(context, approval, request);
9016 if (err) goto leave;
9018 /* Send it to server and process response */
9019 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
9020 BAD_CAST "DisableDataBoxExternally", &request,
9021 (xmlChar **) refnumber);
9023 leave:
9024 free(string);
9025 xmlFreeNode(request);
9026 free(service_name_locale);
9027 #else /* not HAVE_LIBCURL */
9028 err = IE_NOTSUP;
9029 #endif
9031 return err;
9035 #if HAVE_LIBCURL
9036 /* Insert struct isds_message data (envelope (recipient data optional) and
9037 * documents into XML tree
9038 * @context is session context
9039 * @outgoing_message is libisds structure with message data
9040 * @create_message is XML CreateMessage or CreateMultipleMessage element
9041 * @process_recipient true for recipient data serialization, false for no
9042 * serialization */
9043 static isds_error insert_envelope_files(struct isds_ctx *context,
9044 const struct isds_message *outgoing_message, xmlNodePtr create_message,
9045 const _Bool process_recipient) {
9047 isds_error err = IE_SUCCESS;
9048 xmlNodePtr envelope, dm_files, node;
9049 xmlChar *string = NULL;
9051 if (!context) return IE_INVALID_CONTEXT;
9052 if (!outgoing_message || !create_message) return IE_INVAL;
9055 /* Build envelope */
9056 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
9057 if (!envelope) {
9058 isds_printf_message(context, _("Could not add dmEnvelope child to "
9059 "%s element"), create_message->name);
9060 return IE_ERROR;
9063 if (!outgoing_message->envelope) {
9064 isds_log_message(context, _("Outgoing message is missing envelope"));
9065 err = IE_INVAL;
9066 goto leave;
9069 /* Insert optional message type */
9070 err = insert_message_type(context, outgoing_message->envelope->dmType,
9071 envelope);
9072 if (err) goto leave;
9074 INSERT_STRING(envelope, "dmSenderOrgUnit",
9075 outgoing_message->envelope->dmSenderOrgUnit);
9076 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
9077 outgoing_message->envelope->dmSenderOrgUnitNum, string);
9079 if (process_recipient) {
9080 if (!outgoing_message->envelope->dbIDRecipient) {
9081 isds_log_message(context,
9082 _("Outgoing message is missing recipient box identifier"));
9083 err = IE_INVAL;
9084 goto leave;
9086 INSERT_STRING(envelope, "dbIDRecipient",
9087 outgoing_message->envelope->dbIDRecipient);
9089 INSERT_STRING(envelope, "dmRecipientOrgUnit",
9090 outgoing_message->envelope->dmRecipientOrgUnit);
9091 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
9092 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
9093 INSERT_STRING(envelope, "dmToHands",
9094 outgoing_message->envelope->dmToHands);
9097 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
9098 "dmAnnotation");
9099 INSERT_STRING(envelope, "dmAnnotation",
9100 outgoing_message->envelope->dmAnnotation);
9102 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
9103 0, 50, "dmRecipientRefNumber");
9104 INSERT_STRING(envelope, "dmRecipientRefNumber",
9105 outgoing_message->envelope->dmRecipientRefNumber);
9107 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
9108 0, 50, "dmSenderRefNumber");
9109 INSERT_STRING(envelope, "dmSenderRefNumber",
9110 outgoing_message->envelope->dmSenderRefNumber);
9112 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
9113 0, 50, "dmRecipientIdent");
9114 INSERT_STRING(envelope, "dmRecipientIdent",
9115 outgoing_message->envelope->dmRecipientIdent);
9117 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
9118 0, 50, "dmSenderIdent");
9119 INSERT_STRING(envelope, "dmSenderIdent",
9120 outgoing_message->envelope->dmSenderIdent);
9122 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
9123 outgoing_message->envelope->dmLegalTitleLaw, string);
9124 INSERT_LONGINT(envelope, "dmLegalTitleYear",
9125 outgoing_message->envelope->dmLegalTitleYear, string);
9126 INSERT_STRING(envelope, "dmLegalTitleSect",
9127 outgoing_message->envelope->dmLegalTitleSect);
9128 INSERT_STRING(envelope, "dmLegalTitlePar",
9129 outgoing_message->envelope->dmLegalTitlePar);
9130 INSERT_STRING(envelope, "dmLegalTitlePoint",
9131 outgoing_message->envelope->dmLegalTitlePoint);
9133 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
9134 outgoing_message->envelope->dmPersonalDelivery);
9135 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
9136 outgoing_message->envelope->dmAllowSubstDelivery);
9138 /* ???: Should we require value for dbEffectiveOVM sender?
9139 * ISDS has default as true */
9140 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
9141 INSERT_BOOLEAN(envelope, "dmOVM",
9142 outgoing_message->envelope->dmPublishOwnID);
9145 /* Append dmFiles */
9146 if (!outgoing_message->documents) {
9147 isds_log_message(context,
9148 _("Outgoing message is missing list of documents"));
9149 err = IE_INVAL;
9150 goto leave;
9152 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
9153 if (!dm_files) {
9154 isds_printf_message(context, _("Could not add dmFiles child to "
9155 "%s element"), create_message->name);
9156 err = IE_ERROR;
9157 goto leave;
9160 /* Check for document hierarchy */
9161 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
9162 if (err) goto leave;
9164 /* Process each document */
9165 for (struct isds_list *item =
9166 (struct isds_list *) outgoing_message->documents;
9167 item; item = item->next) {
9168 if (!item->data) {
9169 isds_log_message(context,
9170 _("List of documents contains empty item"));
9171 err = IE_INVAL;
9172 goto leave;
9174 /* FIXME: Check for dmFileMetaType and for document references.
9175 * Only first document can be of MAIN type */
9176 err = insert_document(context, (struct isds_document*) item->data,
9177 dm_files);
9179 if (err) goto leave;
9182 leave:
9183 free(string);
9184 return err;
9186 #endif /* HAVE_LIBCURL */
9189 /* Send a message via ISDS to a recipient
9190 * @context is session context
9191 * @outgoing_message is message to send; Some members are mandatory (like
9192 * dbIDRecipient), some are optional and some are irrelevant (especially data
9193 * about sender). Included pointer to isds_list documents must contain at
9194 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
9195 * members will be filled with valid data from ISDS. Exact list of write
9196 * members is subject to change. Currently dmID is changed.
9197 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
9198 isds_error isds_send_message(struct isds_ctx *context,
9199 struct isds_message *outgoing_message) {
9201 isds_error err = IE_SUCCESS;
9202 #if HAVE_LIBCURL
9203 xmlNsPtr isds_ns = NULL;
9204 xmlNodePtr request = NULL;
9205 xmlDocPtr response = NULL;
9206 xmlChar *code = NULL, *message = NULL;
9207 xmlXPathContextPtr xpath_ctx = NULL;
9208 xmlXPathObjectPtr result = NULL;
9209 /*_Bool message_is_complete = 0;*/
9210 #endif
9212 if (!context) return IE_INVALID_CONTEXT;
9213 zfree(context->long_message);
9214 if (!outgoing_message) return IE_INVAL;
9216 #if HAVE_LIBCURL
9217 /* Check if connection is established
9218 * TODO: This check should be done downstairs. */
9219 if (!context->curl) return IE_CONNECTION_CLOSED;
9222 /* Build CreateMessage request */
9223 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
9224 if (!request) {
9225 isds_log_message(context,
9226 _("Could not build CreateMessage request"));
9227 return IE_ERROR;
9229 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9230 if(!isds_ns) {
9231 isds_log_message(context, _("Could not create ISDS name space"));
9232 xmlFreeNode(request);
9233 return IE_ERROR;
9235 xmlSetNs(request, isds_ns);
9237 /* Append envelope and files */
9238 err = insert_envelope_files(context, outgoing_message, request, 1);
9239 if (err) goto leave;
9242 /* Signal we can serialize message since now */
9243 /*message_is_complete = 1;*/
9246 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
9248 /* Sent request */
9249 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9251 /* Don't' destroy request, we want to provide it to application later */
9253 if (err) {
9254 isds_log(ILF_ISDS, ILL_DEBUG,
9255 _("Processing ISDS response on CreateMessage "
9256 "request failed\n"));
9257 goto leave;
9260 /* Check for response status */
9261 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9262 &code, &message, NULL);
9263 if (err) {
9264 isds_log(ILF_ISDS, ILL_DEBUG,
9265 _("ISDS response on CreateMessage request "
9266 "is missing status\n"));
9267 goto leave;
9270 /* Request processed, but refused by server or server failed */
9271 if (xmlStrcmp(code, BAD_CAST "0000")) {
9272 char *box_id_locale =
9273 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9274 char *code_locale = _isds_utf82locale((char*)code);
9275 char *message_locale = _isds_utf82locale((char*)message);
9276 isds_log(ILF_ISDS, ILL_DEBUG,
9277 _("Server did not accept message for %s on CreateMessage "
9278 "request (code=%s, message=%s)\n"),
9279 box_id_locale, code_locale, message_locale);
9280 isds_log_message(context, message_locale);
9281 free(box_id_locale);
9282 free(code_locale);
9283 free(message_locale);
9284 err = IE_ISDS;
9285 goto leave;
9289 /* Extract data */
9290 xpath_ctx = xmlXPathNewContext(response);
9291 if (!xpath_ctx) {
9292 err = IE_ERROR;
9293 goto leave;
9295 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9296 err = IE_ERROR;
9297 goto leave;
9299 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
9300 xpath_ctx);
9301 if (!result) {
9302 err = IE_ERROR;
9303 goto leave;
9305 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9306 isds_log_message(context, _("Missing CreateMessageResponse element"));
9307 err = IE_ISDS;
9308 goto leave;
9310 if (result->nodesetval->nodeNr > 1) {
9311 isds_log_message(context, _("Multiple CreateMessageResponse element"));
9312 err = IE_ISDS;
9313 goto leave;
9315 xpath_ctx->node = result->nodesetval->nodeTab[0];
9316 xmlXPathFreeObject(result); result = NULL;
9318 if (outgoing_message->envelope->dmID) {
9319 free(outgoing_message->envelope->dmID);
9320 outgoing_message->envelope->dmID = NULL;
9322 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
9323 if (!outgoing_message->envelope->dmID) {
9324 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
9325 "but did not return assigned message ID\n"));
9328 leave:
9329 /* TODO: Serialize message into structure member raw */
9330 /* XXX: Each web service transport message in different format.
9331 * Therefore it's not possible to save them directly.
9332 * To save them, one must figure out common format.
9333 * We can leave it on application, or we can implement the ESS format. */
9334 /*if (message_is_complete) {
9335 if (outgoing_message->envelope->dmID) {
9337 /* Add assigned message ID as first child*/
9338 /*xmlNodePtr dmid_text = xmlNewText(
9339 (xmlChar *) outgoing_message->envelope->dmID);
9340 if (!dmid_text) goto serialization_failed;
9342 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
9343 BAD_CAST "dmID");
9344 if (!dmid_element) {
9345 xmlFreeNode(dmid_text);
9346 goto serialization_failed;
9349 xmlNodePtr dmid_element_with_text =
9350 xmlAddChild(dmid_element, dmid_text);
9351 if (!dmid_element_with_text) {
9352 xmlFreeNode(dmid_element);
9353 xmlFreeNode(dmid_text);
9354 goto serialization_failed;
9357 node = xmlAddPrevSibling(envelope->childern,
9358 dmid_element_with_text);
9359 if (!node) {
9360 xmlFreeNodeList(dmid_element_with_text);
9361 goto serialization_failed;
9365 /* Serialize message with ID into raw */
9366 /*buffer = serialize_element(envelope)*/
9367 /* }
9369 serialization_failed:
9373 /* Clean up */
9374 xmlXPathFreeObject(result);
9375 xmlXPathFreeContext(xpath_ctx);
9377 free(code);
9378 free(message);
9379 xmlFreeDoc(response);
9380 xmlFreeNode(request);
9382 if (!err)
9383 isds_log(ILF_ISDS, ILL_DEBUG,
9384 _("CreateMessage request processed by server "
9385 "successfully.\n"));
9386 #else /* not HAVE_LIBCURL */
9387 err = IE_NOTSUP;
9388 #endif
9390 return err;
9394 /* Send a message via ISDS to a multiple recipients
9395 * @context is session context
9396 * @outgoing_message is message to send; Some members are mandatory,
9397 * some are optional and some are irrelevant (especially data
9398 * about sender). Data about recipient will be substituted by ISDS from
9399 * @copies. Included pointer to isds_list documents must
9400 * contain at least one document of FILEMETATYPE_MAIN.
9401 * @copies is list of isds_message_copy structures addressing all desired
9402 * recipients. This is read-write structure, some members will be filled with
9403 * valid data from ISDS (message IDs, error codes, error descriptions).
9404 * @return
9405 * ISDS_SUCCESS if all messages have been sent
9406 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
9407 * succeeded messages can be identified by copies->data->error),
9408 * or other error code if something other goes wrong. */
9409 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
9410 const struct isds_message *outgoing_message,
9411 struct isds_list *copies) {
9413 isds_error err = IE_SUCCESS;
9414 #if HAVE_LIBCURL
9415 isds_error append_err;
9416 xmlNsPtr isds_ns = NULL;
9417 xmlNodePtr request = NULL, recipients, recipient, node;
9418 struct isds_list *item;
9419 struct isds_message_copy *copy;
9420 xmlDocPtr response = NULL;
9421 xmlChar *code = NULL, *message = NULL;
9422 xmlXPathContextPtr xpath_ctx = NULL;
9423 xmlXPathObjectPtr result = NULL;
9424 xmlChar *string = NULL;
9425 int i;
9426 #endif
9428 if (!context) return IE_INVALID_CONTEXT;
9429 zfree(context->long_message);
9430 if (!outgoing_message || !copies) return IE_INVAL;
9432 #if HAVE_LIBCURL
9433 /* Check if connection is established
9434 * TODO: This check should be done downstairs. */
9435 if (!context->curl) return IE_CONNECTION_CLOSED;
9438 /* Build CreateMultipleMessage request */
9439 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
9440 if (!request) {
9441 isds_log_message(context,
9442 _("Could not build CreateMultipleMessage request"));
9443 return IE_ERROR;
9445 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9446 if(!isds_ns) {
9447 isds_log_message(context, _("Could not create ISDS name space"));
9448 xmlFreeNode(request);
9449 return IE_ERROR;
9451 xmlSetNs(request, isds_ns);
9454 /* Build recipients */
9455 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
9456 if (!recipients) {
9457 isds_log_message(context, _("Could not add dmRecipients child to "
9458 "CreateMultipleMessage element"));
9459 xmlFreeNode(request);
9460 return IE_ERROR;
9463 /* Insert each recipient */
9464 for (item = copies; item; item = item->next) {
9465 copy = (struct isds_message_copy *) item->data;
9466 if (!copy) {
9467 isds_log_message(context,
9468 _("`copies' list item contains empty data"));
9469 err = IE_INVAL;
9470 goto leave;
9473 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
9474 if (!recipient) {
9475 isds_log_message(context, _("Could not add dmRecipient child to "
9476 "dmRecipients element"));
9477 err = IE_ERROR;
9478 goto leave;
9481 if (!copy->dbIDRecipient) {
9482 isds_log_message(context,
9483 _("Message copy is missing recipient box identifier"));
9484 err = IE_INVAL;
9485 goto leave;
9487 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
9488 INSERT_STRING(recipient, "dmRecipientOrgUnit",
9489 copy->dmRecipientOrgUnit);
9490 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
9491 copy->dmRecipientOrgUnitNum, string);
9492 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
9495 /* Append envelope and files */
9496 err = insert_envelope_files(context, outgoing_message, request, 0);
9497 if (err) goto leave;
9500 isds_log(ILF_ISDS, ILL_DEBUG,
9501 _("Sending CreateMultipleMessage request to ISDS\n"));
9503 /* Sent request */
9504 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9505 if (err) {
9506 isds_log(ILF_ISDS, ILL_DEBUG,
9507 _("Processing ISDS response on CreateMultipleMessage "
9508 "request failed\n"));
9509 goto leave;
9512 /* Check for response status */
9513 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9514 &code, &message, NULL);
9515 if (err) {
9516 isds_log(ILF_ISDS, ILL_DEBUG,
9517 _("ISDS response on CreateMultipleMessage request "
9518 "is missing status\n"));
9519 goto leave;
9522 /* Request processed, but some copies failed */
9523 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9524 char *box_id_locale =
9525 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9526 char *code_locale = _isds_utf82locale((char*)code);
9527 char *message_locale = _isds_utf82locale((char*)message);
9528 isds_log(ILF_ISDS, ILL_DEBUG,
9529 _("Server did accept message for multiple recipients "
9530 "on CreateMultipleMessage request but delivery to "
9531 "some of them failed (code=%s, message=%s)\n"),
9532 box_id_locale, code_locale, message_locale);
9533 isds_log_message(context, message_locale);
9534 free(box_id_locale);
9535 free(code_locale);
9536 free(message_locale);
9537 err = IE_PARTIAL_SUCCESS;
9540 /* Request refused by server as whole */
9541 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9542 char *box_id_locale =
9543 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9544 char *code_locale = _isds_utf82locale((char*)code);
9545 char *message_locale = _isds_utf82locale((char*)message);
9546 isds_log(ILF_ISDS, ILL_DEBUG,
9547 _("Server did not accept message for multiple recipients "
9548 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9549 box_id_locale, code_locale, message_locale);
9550 isds_log_message(context, message_locale);
9551 free(box_id_locale);
9552 free(code_locale);
9553 free(message_locale);
9554 err = IE_ISDS;
9555 goto leave;
9559 /* Extract data */
9560 xpath_ctx = xmlXPathNewContext(response);
9561 if (!xpath_ctx) {
9562 err = IE_ERROR;
9563 goto leave;
9565 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9566 err = IE_ERROR;
9567 goto leave;
9569 result = xmlXPathEvalExpression(
9570 BAD_CAST "/isds:CreateMultipleMessageResponse"
9571 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9572 xpath_ctx);
9573 if (!result) {
9574 err = IE_ERROR;
9575 goto leave;
9577 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9578 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9579 err = IE_ISDS;
9580 goto leave;
9583 /* Extract message ID and delivery status for each copy */
9584 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9585 item = item->next, i++) {
9586 copy = (struct isds_message_copy *) item->data;
9587 xpath_ctx->node = result->nodesetval->nodeTab[i];
9589 append_err = append_TMStatus(context, copy, xpath_ctx);
9590 if (append_err) {
9591 err = append_err;
9592 goto leave;
9595 if (item || i < result->nodesetval->nodeNr) {
9596 isds_printf_message(context, _("ISDS returned unexpected number of "
9597 "message copy delivery states: %d"),
9598 result->nodesetval->nodeNr);
9599 err = IE_ISDS;
9600 goto leave;
9604 leave:
9605 /* Clean up */
9606 free(string);
9607 xmlXPathFreeObject(result);
9608 xmlXPathFreeContext(xpath_ctx);
9610 free(code);
9611 free(message);
9612 xmlFreeDoc(response);
9613 xmlFreeNode(request);
9615 if (!err)
9616 isds_log(ILF_ISDS, ILL_DEBUG,
9617 _("CreateMultipleMessageResponse request processed by server "
9618 "successfully.\n"));
9619 #else /* not HAVE_LIBCURL */
9620 err = IE_NOTSUP;
9621 #endif
9623 return err;
9627 /* Get list of messages. This is common core for getting sent or received
9628 * messages.
9629 * Any criterion argument can be NULL, if you don't care about it.
9630 * @context is session context. Must not be NULL.
9631 * @outgoing_direction is true if you want list of outgoing messages,
9632 * it's false if you want incoming messages.
9633 * @from_time is minimal time and date of message sending inclusive.
9634 * @to_time is maximal time and date of message sending inclusive
9635 * @organization_unit_number is number of sender/recipient respectively.
9636 * @status_filter is bit field of isds_message_status values. Use special
9637 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9638 * all values, you can use bit-wise arithmetic if you want.)
9639 * @offset is index of first message we are interested in. First message is 1.
9640 * Set to 0 (or 1) if you don't care.
9641 * @number is maximal length of list you want to get as input value, outputs
9642 * number of messages matching these criteria. Can be NULL if you don't care
9643 * (applies to output value either).
9644 * @messages is automatically reallocated list of isds_message's. Be ware that
9645 * it returns only brief overview (envelope and some other fields) about each
9646 * message, not the complete message. FIXME: Specify exact fields.
9647 * The list is sorted by delivery time in ascending order.
9648 * Use NULL if you don't care about don't need the data (useful if you want to
9649 * know only the @number). If you provide &NULL, list will be allocated on
9650 * heap, if you provide pointer to non-NULL, list will be freed automatically
9651 * at first. Also in case of error the list will be NULLed.
9652 * @return IE_SUCCESS or appropriate error code. */
9653 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9654 _Bool outgoing_direction,
9655 const struct timeval *from_time, const struct timeval *to_time,
9656 const long int *organization_unit_number,
9657 const unsigned int status_filter,
9658 const unsigned long int offset, unsigned long int *number,
9659 struct isds_list **messages) {
9661 isds_error err = IE_SUCCESS;
9662 #if HAVE_LIBCURL
9663 xmlNsPtr isds_ns = NULL;
9664 xmlNodePtr request = NULL, node;
9665 xmlDocPtr response = NULL;
9666 xmlChar *code = NULL, *message = NULL;
9667 xmlXPathContextPtr xpath_ctx = NULL;
9668 xmlXPathObjectPtr result = NULL;
9669 xmlChar *string = NULL;
9670 int count = 0;
9671 #endif
9673 if (!context) return IE_INVALID_CONTEXT;
9674 zfree(context->long_message);
9676 /* Free former message list if any */
9677 if (messages) isds_list_free(messages);
9679 #if HAVE_LIBCURL
9680 /* Check if connection is established
9681 * TODO: This check should be done downstairs. */
9682 if (!context->curl) return IE_CONNECTION_CLOSED;
9684 /* Build GetListOf*Messages request */
9685 request = xmlNewNode(NULL,
9686 (outgoing_direction) ?
9687 BAD_CAST "GetListOfSentMessages" :
9688 BAD_CAST "GetListOfReceivedMessages"
9690 if (!request) {
9691 isds_log_message(context,
9692 (outgoing_direction) ?
9693 _("Could not build GetListOfSentMessages request") :
9694 _("Could not build GetListOfReceivedMessages request")
9696 return IE_ERROR;
9698 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9699 if(!isds_ns) {
9700 isds_log_message(context, _("Could not create ISDS name space"));
9701 xmlFreeNode(request);
9702 return IE_ERROR;
9704 xmlSetNs(request, isds_ns);
9707 if (from_time) {
9708 err = timeval2timestring(from_time, &string);
9709 if (err) goto leave;
9711 INSERT_STRING(request, "dmFromTime", string);
9712 free(string); string = NULL;
9714 if (to_time) {
9715 err = timeval2timestring(to_time, &string);
9716 if (err) goto leave;
9718 INSERT_STRING(request, "dmToTime", string);
9719 free(string); string = NULL;
9721 if (outgoing_direction) {
9722 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9723 organization_unit_number, string);
9724 } else {
9725 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9726 organization_unit_number, string);
9729 if (status_filter > MESSAGESTATE_ANY) {
9730 isds_printf_message(context,
9731 _("Invalid message state filter value: %ld"), status_filter);
9732 err = IE_INVAL;
9733 goto leave;
9735 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9737 if (offset > 0 ) {
9738 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9739 } else {
9740 INSERT_STRING(request, "dmOffset", "1");
9743 /* number 0 means no limit */
9744 if (number && *number == 0) {
9745 INSERT_STRING(request, "dmLimit", NULL);
9746 } else {
9747 INSERT_ULONGINT(request, "dmLimit", number, string);
9751 isds_log(ILF_ISDS, ILL_DEBUG,
9752 (outgoing_direction) ?
9753 _("Sending GetListOfSentMessages request to ISDS\n") :
9754 _("Sending GetListOfReceivedMessages request to ISDS\n")
9757 /* Sent request */
9758 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9759 xmlFreeNode(request); request = NULL;
9761 if (err) {
9762 isds_log(ILF_ISDS, ILL_DEBUG,
9763 (outgoing_direction) ?
9764 _("Processing ISDS response on GetListOfSentMessages "
9765 "request failed\n") :
9766 _("Processing ISDS response on GetListOfReceivedMessages "
9767 "request failed\n")
9769 goto leave;
9772 /* Check for response status */
9773 err = isds_response_status(context, SERVICE_DM_INFO, response,
9774 &code, &message, NULL);
9775 if (err) {
9776 isds_log(ILF_ISDS, ILL_DEBUG,
9777 (outgoing_direction) ?
9778 _("ISDS response on GetListOfSentMessages request "
9779 "is missing status\n") :
9780 _("ISDS response on GetListOfReceivedMessages request "
9781 "is missing status\n")
9783 goto leave;
9786 /* Request processed, but nothing found */
9787 if (xmlStrcmp(code, BAD_CAST "0000")) {
9788 char *code_locale = _isds_utf82locale((char*)code);
9789 char *message_locale = _isds_utf82locale((char*)message);
9790 isds_log(ILF_ISDS, ILL_DEBUG,
9791 (outgoing_direction) ?
9792 _("Server refused GetListOfSentMessages request "
9793 "(code=%s, message=%s)\n") :
9794 _("Server refused GetListOfReceivedMessages request "
9795 "(code=%s, message=%s)\n"),
9796 code_locale, message_locale);
9797 isds_log_message(context, message_locale);
9798 free(code_locale);
9799 free(message_locale);
9800 err = IE_ISDS;
9801 goto leave;
9805 /* Extract data */
9806 xpath_ctx = xmlXPathNewContext(response);
9807 if (!xpath_ctx) {
9808 err = IE_ERROR;
9809 goto leave;
9811 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9812 err = IE_ERROR;
9813 goto leave;
9815 result = xmlXPathEvalExpression(
9816 (outgoing_direction) ?
9817 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9818 "isds:dmRecords/isds:dmRecord" :
9819 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9820 "isds:dmRecords/isds:dmRecord",
9821 xpath_ctx);
9822 if (!result) {
9823 err = IE_ERROR;
9824 goto leave;
9827 /* Fill output arguments in */
9828 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9829 struct isds_envelope *envelope;
9830 struct isds_list *item = NULL, *last_item = NULL;
9832 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9833 /* Create new message */
9834 item = calloc(1, sizeof(*item));
9835 if (!item) {
9836 err = IE_NOMEM;
9837 goto leave;
9839 item->destructor = (void(*)(void**)) &isds_message_free;
9840 item->data = calloc(1, sizeof(struct isds_message));
9841 if (!item->data) {
9842 isds_list_free(&item);
9843 err = IE_NOMEM;
9844 goto leave;
9847 /* Extract envelope data */
9848 xpath_ctx->node = result->nodesetval->nodeTab[count];
9849 envelope = NULL;
9850 err = extract_DmRecord(context, &envelope, xpath_ctx);
9851 if (err) {
9852 isds_list_free(&item);
9853 goto leave;
9856 /* Attach extracted envelope */
9857 ((struct isds_message *) item->data)->envelope = envelope;
9859 /* Append new message into the list */
9860 if (!*messages) {
9861 *messages = last_item = item;
9862 } else {
9863 last_item->next = item;
9864 last_item = item;
9868 if (number) *number = count;
9870 leave:
9871 if (err) {
9872 isds_list_free(messages);
9875 free(string);
9876 xmlXPathFreeObject(result);
9877 xmlXPathFreeContext(xpath_ctx);
9879 free(code);
9880 free(message);
9881 xmlFreeDoc(response);
9882 xmlFreeNode(request);
9884 if (!err)
9885 isds_log(ILF_ISDS, ILL_DEBUG,
9886 (outgoing_direction) ?
9887 _("GetListOfSentMessages request processed by server "
9888 "successfully.\n") :
9889 _("GetListOfReceivedMessages request processed by server "
9890 "successfully.\n")
9892 #else /* not HAVE_LIBCURL */
9893 err = IE_NOTSUP;
9894 #endif
9895 return err;
9899 /* Get list of outgoing (already sent) messages.
9900 * Any criterion argument can be NULL, if you don't care about it.
9901 * @context is session context. Must not be NULL.
9902 * @from_time is minimal time and date of message sending inclusive.
9903 * @to_time is maximal time and date of message sending inclusive
9904 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9905 * @status_filter is bit field of isds_message_status values. Use special
9906 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9907 * all values, you can use bit-wise arithmetic if you want.)
9908 * @offset is index of first message we are interested in. First message is 1.
9909 * Set to 0 (or 1) if you don't care.
9910 * @number is maximal length of list you want to get as input value, outputs
9911 * number of messages matching these criteria. Can be NULL if you don't care
9912 * (applies to output value either).
9913 * @messages is automatically reallocated list of isds_message's. Be ware that
9914 * it returns only brief overview (envelope and some other fields) about each
9915 * message, not the complete message. FIXME: Specify exact fields.
9916 * The list is sorted by delivery time in ascending order.
9917 * Use NULL if you don't care about the meta data (useful if you want to know
9918 * only the @number). If you provide &NULL, list will be allocated on heap,
9919 * if you provide pointer to non-NULL, list will be freed automatically at
9920 * first. Also in case of error the list will be NULLed.
9921 * @return IE_SUCCESS or appropriate error code. */
9922 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9923 const struct timeval *from_time, const struct timeval *to_time,
9924 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9925 const unsigned long int offset, unsigned long int *number,
9926 struct isds_list **messages) {
9928 return isds_get_list_of_messages(
9929 context, 1,
9930 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9931 offset, number,
9932 messages);
9936 /* Get list of incoming (addressed to you) messages.
9937 * Any criterion argument can be NULL, if you don't care about it.
9938 * @context is session context. Must not be NULL.
9939 * @from_time is minimal time and date of message sending inclusive.
9940 * @to_time is maximal time and date of message sending inclusive
9941 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9942 * @status_filter is bit field of isds_message_status values. Use special
9943 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9944 * all values, you can use bit-wise arithmetic if you want.)
9945 * @offset is index of first message we are interested in. First message is 1.
9946 * Set to 0 (or 1) if you don't care.
9947 * @number is maximal length of list you want to get as input value, outputs
9948 * number of messages matching these criteria. Can be NULL if you don't care
9949 * (applies to output value either).
9950 * @messages is automatically reallocated list of isds_message's. Be ware that
9951 * it returns only brief overview (envelope and some other fields) about each
9952 * message, not the complete message. FIXME: Specify exact fields.
9953 * Use NULL if you don't care about the meta data (useful if you want to know
9954 * only the @number). If you provide &NULL, list will be allocated on heap,
9955 * if you provide pointer to non-NULL, list will be freed automatically at
9956 * first. Also in case of error the list will be NULLed.
9957 * @return IE_SUCCESS or appropriate error code. */
9958 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9959 const struct timeval *from_time, const struct timeval *to_time,
9960 const long int *dmRecipientOrgUnitNum,
9961 const unsigned int status_filter,
9962 const unsigned long int offset, unsigned long int *number,
9963 struct isds_list **messages) {
9965 return isds_get_list_of_messages(
9966 context, 0,
9967 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9968 offset, number,
9969 messages);
9973 /* Get list of sent message state changes.
9974 * Any criterion argument can be NULL, if you don't care about it.
9975 * @context is session context. Must not be NULL.
9976 * @from_time is minimal time and date of status changes inclusive
9977 * @to_time is maximal time and date of status changes inclusive
9978 * @changed_states is automatically reallocated list of
9979 * isds_message_status_change's. If you provide &NULL, list will be allocated
9980 * on heap, if you provide pointer to non-NULL, list will be freed
9981 * automatically at first. Also in case of error the list will be NULLed.
9982 * XXX: The list item ordering is not specified.
9983 * XXX: Server provides only `recent' changes.
9984 * @return IE_SUCCESS or appropriate error code. */
9985 isds_error isds_get_list_of_sent_message_state_changes(
9986 struct isds_ctx *context,
9987 const struct timeval *from_time, const struct timeval *to_time,
9988 struct isds_list **changed_states) {
9990 isds_error err = IE_SUCCESS;
9991 #if HAVE_LIBCURL
9992 xmlNsPtr isds_ns = NULL;
9993 xmlNodePtr request = NULL, node;
9994 xmlDocPtr response = NULL;
9995 xmlXPathContextPtr xpath_ctx = NULL;
9996 xmlXPathObjectPtr result = NULL;
9997 xmlChar *string = NULL;
9998 int count = 0;
9999 #endif
10001 if (!context) return IE_INVALID_CONTEXT;
10002 zfree(context->long_message);
10004 /* Free former message list if any */
10005 isds_list_free(changed_states);
10007 #if HAVE_LIBCURL
10008 /* Check if connection is established
10009 * TODO: This check should be done downstairs. */
10010 if (!context->curl) return IE_CONNECTION_CLOSED;
10012 /* Build GetMessageStateChanges request */
10013 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
10014 if (!request) {
10015 isds_log_message(context,
10016 _("Could not build GetMessageStateChanges request"));
10017 return IE_ERROR;
10019 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10020 if(!isds_ns) {
10021 isds_log_message(context, _("Could not create ISDS name space"));
10022 xmlFreeNode(request);
10023 return IE_ERROR;
10025 xmlSetNs(request, isds_ns);
10028 if (from_time) {
10029 err = timeval2timestring(from_time, &string);
10030 if (err) goto leave;
10032 INSERT_STRING(request, "dmFromTime", string);
10033 zfree(string);
10035 if (to_time) {
10036 err = timeval2timestring(to_time, &string);
10037 if (err) goto leave;
10039 INSERT_STRING(request, "dmToTime", string);
10040 zfree(string);
10043 /* Sent request */
10044 err = send_destroy_request_check_response(context,
10045 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
10046 &response, NULL, NULL);
10047 if (err) goto leave;
10050 /* Extract data */
10051 xpath_ctx = xmlXPathNewContext(response);
10052 if (!xpath_ctx) {
10053 err = IE_ERROR;
10054 goto leave;
10056 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10057 err = IE_ERROR;
10058 goto leave;
10060 result = xmlXPathEvalExpression(
10061 BAD_CAST "/isds:GetMessageStateChangesResponse/"
10062 "isds:dmRecords/isds:dmRecord", xpath_ctx);
10063 if (!result) {
10064 err = IE_ERROR;
10065 goto leave;
10068 /* Fill output arguments in */
10069 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10070 struct isds_list *item = NULL, *last_item = NULL;
10072 for (count = 0; count < result->nodesetval->nodeNr; count++) {
10073 /* Create new status change */
10074 item = calloc(1, sizeof(*item));
10075 if (!item) {
10076 err = IE_NOMEM;
10077 goto leave;
10079 item->destructor =
10080 (void(*)(void**)) &isds_message_status_change_free;
10082 /* Extract message status change */
10083 xpath_ctx->node = result->nodesetval->nodeTab[count];
10084 err = extract_StateChangesRecord(context,
10085 (struct isds_message_status_change **) &item->data,
10086 xpath_ctx);
10087 if (err) {
10088 isds_list_free(&item);
10089 goto leave;
10092 /* Append new message status change into the list */
10093 if (!*changed_states) {
10094 *changed_states = last_item = item;
10095 } else {
10096 last_item->next = item;
10097 last_item = item;
10102 leave:
10103 if (err) {
10104 isds_list_free(changed_states);
10107 free(string);
10108 xmlXPathFreeObject(result);
10109 xmlXPathFreeContext(xpath_ctx);
10110 xmlFreeDoc(response);
10111 xmlFreeNode(request);
10113 if (!err)
10114 isds_log(ILF_ISDS, ILL_DEBUG,
10115 _("GetMessageStateChanges request processed by server "
10116 "successfully.\n"));
10117 #else /* not HAVE_LIBCURL */
10118 err = IE_NOTSUP;
10119 #endif
10120 return err;
10124 #if HAVE_LIBCURL
10125 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
10126 * code
10127 * @context is session context
10128 * @service is ISDS WS service handler
10129 * @service_name is name of SERVICE_DM_OPERATIONS
10130 * @message_id is message ID to send as service argument to ISDS
10131 * @response is reallocated server SOAP body response as XML document
10132 * @raw_response is reallocated bit stream with response body. Use
10133 * NULL if you don't care
10134 * @raw_response_length is size of @raw_response in bytes
10135 * @code is reallocated ISDS status code
10136 * @status_message is reallocated ISDS status message
10137 * @return error coded from lower layer, context message will be set up
10138 * appropriately. */
10139 static isds_error build_send_check_message_request(struct isds_ctx *context,
10140 const isds_service service, const xmlChar *service_name,
10141 const char *message_id,
10142 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
10143 xmlChar **code, xmlChar **status_message) {
10145 isds_error err = IE_SUCCESS;
10146 char *service_name_locale = NULL, *message_id_locale = NULL;
10147 xmlNodePtr request = NULL, node;
10148 xmlNsPtr isds_ns = NULL;
10150 if (!context) return IE_INVALID_CONTEXT;
10151 if (!service_name || !message_id) return IE_INVAL;
10152 if (!response || !code || !status_message) return IE_INVAL;
10153 if (!raw_response_length && raw_response) return IE_INVAL;
10155 /* Free output argument */
10156 xmlFreeDoc(*response); *response = NULL;
10157 if (raw_response) zfree(*raw_response);
10158 zfree(*code);
10159 zfree(*status_message);
10162 /* Check if connection is established
10163 * TODO: This check should be done downstairs. */
10164 if (!context->curl) return IE_CONNECTION_CLOSED;
10166 service_name_locale = _isds_utf82locale((char*)service_name);
10167 message_id_locale = _isds_utf82locale(message_id);
10168 if (!service_name_locale || !message_id_locale) {
10169 err = IE_NOMEM;
10170 goto leave;
10173 /* Build request */
10174 request = xmlNewNode(NULL, service_name);
10175 if (!request) {
10176 isds_printf_message(context,
10177 _("Could not build %s request for %s message ID"),
10178 service_name_locale, message_id_locale);
10179 err = IE_ERROR;
10180 goto leave;
10182 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10183 if(!isds_ns) {
10184 isds_log_message(context, _("Could not create ISDS name space"));
10185 err = IE_ERROR;
10186 goto leave;
10188 xmlSetNs(request, isds_ns);
10191 /* Add requested ID */
10192 err = validate_message_id_length(context, (xmlChar *) message_id);
10193 if (err) goto leave;
10194 INSERT_STRING(request, "dmID", message_id);
10197 isds_log(ILF_ISDS, ILL_DEBUG,
10198 _("Sending %s request for %s message ID to ISDS\n"),
10199 service_name_locale, message_id_locale);
10201 /* Send request */
10202 err = _isds(context, service, request, response,
10203 raw_response, raw_response_length);
10204 xmlFreeNode(request); request = NULL;
10206 if (err) {
10207 isds_log(ILF_ISDS, ILL_DEBUG,
10208 _("Processing ISDS response on %s request failed\n"),
10209 service_name_locale);
10210 goto leave;
10213 /* Check for response status */
10214 err = isds_response_status(context, service, *response,
10215 code, status_message, NULL);
10216 if (err) {
10217 isds_log(ILF_ISDS, ILL_DEBUG,
10218 _("ISDS response on %s request is missing status\n"),
10219 service_name_locale);
10220 goto leave;
10223 /* Request processed, but nothing found */
10224 if (xmlStrcmp(*code, BAD_CAST "0000")) {
10225 char *code_locale = _isds_utf82locale((char*) *code);
10226 char *status_message_locale = _isds_utf82locale((char*) *status_message);
10227 isds_log(ILF_ISDS, ILL_DEBUG,
10228 _("Server refused %s request for %s message ID "
10229 "(code=%s, message=%s)\n"),
10230 service_name_locale, message_id_locale,
10231 code_locale, status_message_locale);
10232 isds_log_message(context, status_message_locale);
10233 free(code_locale);
10234 free(status_message_locale);
10235 err = IE_ISDS;
10236 goto leave;
10239 leave:
10240 free(message_id_locale);
10241 free(service_name_locale);
10242 xmlFreeNode(request);
10243 return err;
10247 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
10248 * signed data and free ISDS response.
10249 * @context is session context
10250 * @message_id is UTF-8 encoded message ID for logging purpose
10251 * @response is parsed XML document. It will be freed and NULLed in the middle
10252 * of function run to save memory. This is not guaranteed in case of error.
10253 * @request_name is name of ISDS request used to construct response root
10254 * element name and for logging purpose.
10255 * @raw is reallocated output buffer with DER encoded CMS data
10256 * @raw_length is size of @raw buffer in bytes
10257 * @returns standard error codes, in case of error, @raw will be freed and
10258 * NULLed, @response sometimes. */
10259 static isds_error find_extract_signed_data_free_response(
10260 struct isds_ctx *context, const xmlChar *message_id,
10261 xmlDocPtr *response, const xmlChar *request_name,
10262 void **raw, size_t *raw_length) {
10264 isds_error err = IE_SUCCESS;
10265 char *xpath_expression = NULL;
10266 xmlXPathContextPtr xpath_ctx = NULL;
10267 xmlXPathObjectPtr result = NULL;
10268 char *encoded_structure = NULL;
10270 if (!context) return IE_INVALID_CONTEXT;
10271 if (!raw) return IE_INVAL;
10272 zfree(*raw);
10273 if (!message_id || !response || !*response || !request_name || !raw_length)
10274 return IE_INVAL;
10276 /* Build XPath expression */
10277 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
10278 "Response/isds:dmSignature");
10279 if (!xpath_expression) return IE_NOMEM;
10281 /* Extract data */
10282 xpath_ctx = xmlXPathNewContext(*response);
10283 if (!xpath_ctx) {
10284 err = IE_ERROR;
10285 goto leave;
10287 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10288 err = IE_ERROR;
10289 goto leave;
10291 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
10292 if (!result) {
10293 err = IE_ERROR;
10294 goto leave;
10296 /* Empty response */
10297 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10298 char *message_id_locale = _isds_utf82locale((char*) message_id);
10299 isds_printf_message(context,
10300 _("Server did not return any signed data for message ID `%s' "
10301 "on %s request"),
10302 message_id_locale, request_name);
10303 free(message_id_locale);
10304 err = IE_ISDS;
10305 goto leave;
10307 /* More responses */
10308 if (result->nodesetval->nodeNr > 1) {
10309 char *message_id_locale = _isds_utf82locale((char*) message_id);
10310 isds_printf_message(context,
10311 _("Server did return more signed data for message ID `%s' "
10312 "on %s request"),
10313 message_id_locale, request_name);
10314 free(message_id_locale);
10315 err = IE_ISDS;
10316 goto leave;
10318 /* One response */
10319 xpath_ctx->node = result->nodesetval->nodeTab[0];
10321 /* Extract PKCS#7 structure */
10322 EXTRACT_STRING(".", encoded_structure);
10323 if (!encoded_structure) {
10324 isds_log_message(context, _("dmSignature element is empty"));
10327 /* Here we have delivery info as standalone CMS in encoded_structure.
10328 * We don't need any other data, free them: */
10329 xmlXPathFreeObject(result); result = NULL;
10330 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
10331 xmlFreeDoc(*response); *response = NULL;
10334 /* Decode PKCS#7 to DER format */
10335 *raw_length = _isds_b64decode(encoded_structure, raw);
10336 if (*raw_length == (size_t) -1) {
10337 isds_log_message(context,
10338 _("Error while Base64-decoding PKCS#7 structure"));
10339 err = IE_ERROR;
10340 goto leave;
10343 leave:
10344 if (err) {
10345 zfree(*raw);
10346 raw_length = 0;
10349 free(encoded_structure);
10350 xmlXPathFreeObject(result);
10351 xmlXPathFreeContext(xpath_ctx);
10352 free(xpath_expression);
10354 return err;
10356 #endif /* HAVE_LIBCURL */
10359 /* Download incoming message envelope identified by ID.
10360 * @context is session context
10361 * @message_id is message identifier (you can get them from
10362 * isds_get_list_of_received_messages())
10363 * @message is automatically reallocated message retrieved from ISDS.
10364 * It will miss documents per se. Use isds_get_received_message(), if you are
10365 * interested in documents (content) too.
10366 * Returned hash and timestamp require documents to be verifiable. */
10367 isds_error isds_get_received_envelope(struct isds_ctx *context,
10368 const char *message_id, struct isds_message **message) {
10370 isds_error err = IE_SUCCESS;
10371 #if HAVE_LIBCURL
10372 xmlDocPtr response = NULL;
10373 xmlChar *code = NULL, *status_message = NULL;
10374 xmlXPathContextPtr xpath_ctx = NULL;
10375 xmlXPathObjectPtr result = NULL;
10376 #endif
10378 if (!context) return IE_INVALID_CONTEXT;
10379 zfree(context->long_message);
10381 /* Free former message if any */
10382 if (!message) return IE_INVAL;
10383 isds_message_free(message);
10385 #if HAVE_LIBCURL
10386 /* Do request and check for success */
10387 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10388 BAD_CAST "MessageEnvelopeDownload", message_id,
10389 &response, NULL, NULL, &code, &status_message);
10390 if (err) goto leave;
10392 /* Extract data */
10393 xpath_ctx = xmlXPathNewContext(response);
10394 if (!xpath_ctx) {
10395 err = IE_ERROR;
10396 goto leave;
10398 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10399 err = IE_ERROR;
10400 goto leave;
10402 result = xmlXPathEvalExpression(
10403 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
10404 "isds:dmReturnedMessageEnvelope",
10405 xpath_ctx);
10406 if (!result) {
10407 err = IE_ERROR;
10408 goto leave;
10410 /* Empty response */
10411 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10412 char *message_id_locale = _isds_utf82locale((char*) message_id);
10413 isds_printf_message(context,
10414 _("Server did not return any envelope for ID `%s' "
10415 "on MessageEnvelopeDownload request"), message_id_locale);
10416 free(message_id_locale);
10417 err = IE_ISDS;
10418 goto leave;
10420 /* More envelops */
10421 if (result->nodesetval->nodeNr > 1) {
10422 char *message_id_locale = _isds_utf82locale((char*) message_id);
10423 isds_printf_message(context,
10424 _("Server did return more envelopes for ID `%s' "
10425 "on MessageEnvelopeDownload request"), message_id_locale);
10426 free(message_id_locale);
10427 err = IE_ISDS;
10428 goto leave;
10430 /* One message */
10431 xpath_ctx->node = result->nodesetval->nodeTab[0];
10433 /* Extract the envelope (= message without documents, hence 0) */
10434 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10435 if (err) goto leave;
10437 /* Save XML blob */
10438 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10439 &(*message)->raw_length);
10441 leave:
10442 if (err) {
10443 isds_message_free(message);
10446 xmlXPathFreeObject(result);
10447 xmlXPathFreeContext(xpath_ctx);
10449 free(code);
10450 free(status_message);
10451 if (!*message || !(*message)->xml) {
10452 xmlFreeDoc(response);
10455 if (!err)
10456 isds_log(ILF_ISDS, ILL_DEBUG,
10457 _("MessageEnvelopeDownload request processed by server "
10458 "successfully.\n")
10460 #else /* not HAVE_LIBCURL */
10461 err = IE_NOTSUP;
10462 #endif
10463 return err;
10467 /* Load delivery info of any format from buffer.
10468 * @context is session context
10469 * @raw_type advertises format of @buffer content. Only delivery info types
10470 * are accepted.
10471 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10472 * retrieve such data from message->raw after calling
10473 * isds_get_signed_delivery_info().
10474 * @length is length of buffer in bytes.
10475 * @message is automatically reallocated message parsed from @buffer.
10476 * @strategy selects how buffer will be attached into raw isds_message member.
10477 * */
10478 isds_error isds_load_delivery_info(struct isds_ctx *context,
10479 const isds_raw_type raw_type,
10480 const void *buffer, const size_t length,
10481 struct isds_message **message, const isds_buffer_strategy strategy) {
10483 isds_error err = IE_SUCCESS;
10484 message_ns_type message_ns;
10485 xmlDocPtr message_doc = NULL;
10486 xmlXPathContextPtr xpath_ctx = NULL;
10487 xmlXPathObjectPtr result = NULL;
10488 void *xml_stream = NULL;
10489 size_t xml_stream_length = 0;
10491 if (!context) return IE_INVALID_CONTEXT;
10492 zfree(context->long_message);
10493 if (!message) return IE_INVAL;
10494 isds_message_free(message);
10495 if (!buffer) return IE_INVAL;
10498 /* Select buffer format and extract XML from CMS*/
10499 switch (raw_type) {
10500 case RAWTYPE_DELIVERYINFO:
10501 message_ns = MESSAGE_NS_UNSIGNED;
10502 xml_stream = (void *) buffer;
10503 xml_stream_length = length;
10504 break;
10506 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
10507 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10508 xml_stream = (void *) buffer;
10509 xml_stream_length = length;
10510 break;
10512 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10513 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10514 err = _isds_extract_cms_data(context, buffer, length,
10515 &xml_stream, &xml_stream_length);
10516 if (err) goto leave;
10517 break;
10519 default:
10520 isds_log_message(context, _("Bad raw delivery representation type"));
10521 return IE_INVAL;
10522 break;
10525 if (_isds_sizet2int(xml_stream_length) >= 0) {
10526 isds_log(ILF_ISDS, ILL_DEBUG,
10527 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10528 _isds_sizet2int(xml_stream_length), xml_stream);
10531 /* Convert delivery info XML stream into XPath context */
10532 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10533 if (!message_doc) {
10534 err = IE_XML;
10535 goto leave;
10537 xpath_ctx = xmlXPathNewContext(message_doc);
10538 if (!xpath_ctx) {
10539 err = IE_ERROR;
10540 goto leave;
10542 /* XXX: Name spaces mangled for signed delivery info:
10543 * http://isds.czechpoint.cz/v20/delivery:
10545 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10546 * <q:dmDelivery>
10547 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10548 * <p:dmID>170272</p:dmID>
10549 * ...
10550 * </p:dmDm>
10551 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10552 * ...
10553 * </q:dmEvents>...</q:dmEvents>
10554 * </q:dmDelivery>
10555 * </q:GetDeliveryInfoResponse>
10556 * */
10557 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10558 err = IE_ERROR;
10559 goto leave;
10561 result = xmlXPathEvalExpression(
10562 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10563 xpath_ctx);
10564 if (!result) {
10565 err = IE_ERROR;
10566 goto leave;
10568 /* Empty delivery info */
10569 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10570 isds_printf_message(context,
10571 _("XML document is not sisds:dmDelivery document"));
10572 err = IE_ISDS;
10573 goto leave;
10575 /* More delivery info's */
10576 if (result->nodesetval->nodeNr > 1) {
10577 isds_printf_message(context,
10578 _("XML document has more sisds:dmDelivery elements"));
10579 err = IE_ISDS;
10580 goto leave;
10582 /* One delivery info */
10583 xpath_ctx->node = result->nodesetval->nodeTab[0];
10585 /* Extract the envelope (= message without documents, hence 0).
10586 * XXX: extract_TReturnedMessage() can obtain attachments size,
10587 * but delivery info carries none. It's coded as option elements,
10588 * so it should work. */
10589 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10590 if (err) goto leave;
10592 /* Extract events */
10593 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10594 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10595 if (err) { err = IE_ERROR; goto leave; }
10596 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10597 if (err) goto leave;
10599 /* Append raw CMS structure into message */
10600 (*message)->raw_type = raw_type;
10601 switch (strategy) {
10602 case BUFFER_DONT_STORE:
10603 break;
10604 case BUFFER_COPY:
10605 (*message)->raw = malloc(length);
10606 if (!(*message)->raw) {
10607 err = IE_NOMEM;
10608 goto leave;
10610 memcpy((*message)->raw, buffer, length);
10611 (*message)->raw_length = length;
10612 break;
10613 case BUFFER_MOVE:
10614 (*message)->raw = (void *) buffer;
10615 (*message)->raw_length = length;
10616 break;
10617 default:
10618 err = IE_ENUM;
10619 goto leave;
10622 leave:
10623 if (err) {
10624 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10625 isds_message_free(message);
10628 xmlXPathFreeObject(result);
10629 xmlXPathFreeContext(xpath_ctx);
10630 if (!*message || !(*message)->xml) {
10631 xmlFreeDoc(message_doc);
10633 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10635 if (!err)
10636 isds_log(ILF_ISDS, ILL_DEBUG,
10637 _("Delivery info loaded successfully.\n"));
10638 return err;
10642 /* Download signed delivery info-sheet of given message identified by ID.
10643 * @context is session context
10644 * @message_id is message identifier (you can get them from
10645 * isds_get_list_of_{sent,received}_messages())
10646 * @message is automatically reallocated message retrieved from ISDS.
10647 * It will miss documents per se. Use isds_get_signed_received_message(),
10648 * if you are interested in documents (content). OTOH, only this function
10649 * can get list events message has gone through. */
10650 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10651 const char *message_id, struct isds_message **message) {
10653 isds_error err = IE_SUCCESS;
10654 #if HAVE_LIBCURL
10655 xmlDocPtr response = NULL;
10656 xmlChar *code = NULL, *status_message = NULL;
10657 void *raw = NULL;
10658 size_t raw_length = 0;
10659 #endif
10661 if (!context) return IE_INVALID_CONTEXT;
10662 zfree(context->long_message);
10664 /* Free former message if any */
10665 if (!message) return IE_INVAL;
10666 isds_message_free(message);
10668 #if HAVE_LIBCURL
10669 /* Do request and check for success */
10670 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10671 BAD_CAST "GetSignedDeliveryInfo", message_id,
10672 &response, NULL, NULL, &code, &status_message);
10673 if (err) goto leave;
10675 /* Find signed delivery info, extract it into raw and maybe free
10676 * response */
10677 err = find_extract_signed_data_free_response(context,
10678 (xmlChar *)message_id, &response,
10679 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10680 if (err) goto leave;
10682 /* Parse delivery info */
10683 err = isds_load_delivery_info(context,
10684 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10685 message, BUFFER_MOVE);
10686 if (err) goto leave;
10688 raw = NULL;
10690 leave:
10691 if (err) {
10692 isds_message_free(message);
10695 free(raw);
10696 free(code);
10697 free(status_message);
10698 xmlFreeDoc(response);
10700 if (!err)
10701 isds_log(ILF_ISDS, ILL_DEBUG,
10702 _("GetSignedDeliveryInfo request processed by server "
10703 "successfully.\n")
10705 #else /* not HAVE_LIBCURL */
10706 err = IE_NOTSUP;
10707 #endif
10708 return err;
10712 /* Download delivery info-sheet of given message identified by ID.
10713 * @context is session context
10714 * @message_id is message identifier (you can get them from
10715 * isds_get_list_of_{sent,received}_messages())
10716 * @message is automatically reallocated message retrieved from ISDS.
10717 * It will miss documents per se. Use isds_get_received_message(), if you are
10718 * interested in documents (content). OTOH, only this function can get list
10719 * of events message has gone through. */
10720 isds_error isds_get_delivery_info(struct isds_ctx *context,
10721 const char *message_id, struct isds_message **message) {
10723 isds_error err = IE_SUCCESS;
10724 #if HAVE_LIBCURL
10725 xmlDocPtr response = NULL;
10726 xmlChar *code = NULL, *status_message = NULL;
10727 xmlNodePtr delivery_node = NULL;
10728 void *raw = NULL;
10729 size_t raw_length = 0;
10730 #endif
10732 if (!context) return IE_INVALID_CONTEXT;
10733 zfree(context->long_message);
10735 /* Free former message if any */
10736 if (!message) return IE_INVAL;
10737 isds_message_free(message);
10739 #if HAVE_LIBCURL
10740 /* Do request and check for success */
10741 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10742 BAD_CAST "GetDeliveryInfo", message_id,
10743 &response, NULL, NULL, &code, &status_message);
10744 if (err) goto leave;
10747 /* Serialize delivery info */
10748 delivery_node = xmlDocGetRootElement(response);
10749 if (!delivery_node) {
10750 char *message_id_locale = _isds_utf82locale((char*) message_id);
10751 isds_printf_message(context,
10752 _("Server did not return any delivery info for ID `%s' "
10753 "on GetDeliveryInfo request"), message_id_locale);
10754 free(message_id_locale);
10755 err = IE_ISDS;
10756 goto leave;
10758 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10759 if (err) goto leave;
10761 /* Parse delivery info */
10762 /* TODO: Here we parse the response second time. We could single delivery
10763 * parser from isds_load_delivery_info() to make things faster. */
10764 err = isds_load_delivery_info(context,
10765 RAWTYPE_DELIVERYINFO, raw, raw_length,
10766 message, BUFFER_MOVE);
10767 if (err) goto leave;
10769 raw = NULL;
10772 leave:
10773 if (err) {
10774 isds_message_free(message);
10777 free(raw);
10778 free(code);
10779 free(status_message);
10780 xmlFreeDoc(response);
10782 if (!err)
10783 isds_log(ILF_ISDS, ILL_DEBUG,
10784 _("GetDeliveryInfo request processed by server "
10785 "successfully.\n")
10787 #else /* not HAVE_LIBCURL */
10788 err = IE_NOTSUP;
10789 #endif
10790 return err;
10794 /* Download incoming message identified by ID.
10795 * @context is session context
10796 * @message_id is message identifier (you can get them from
10797 * isds_get_list_of_received_messages())
10798 * @message is automatically reallocated message retrieved from ISDS */
10799 isds_error isds_get_received_message(struct isds_ctx *context,
10800 const char *message_id, struct isds_message **message) {
10802 isds_error err = IE_SUCCESS;
10803 #if HAVE_LIBCURL
10804 xmlDocPtr response = NULL;
10805 void *xml_stream = NULL;
10806 size_t xml_stream_length;
10807 xmlChar *code = NULL, *status_message = NULL;
10808 xmlXPathContextPtr xpath_ctx = NULL;
10809 xmlXPathObjectPtr result = NULL;
10810 char *phys_path = NULL;
10811 size_t phys_start, phys_end;
10812 #endif
10814 if (!context) return IE_INVALID_CONTEXT;
10815 zfree(context->long_message);
10817 /* Free former message if any */
10818 if (NULL == message) return IE_INVAL;
10819 if (message) isds_message_free(message);
10821 #if HAVE_LIBCURL
10822 /* Do request and check for success */
10823 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10824 BAD_CAST "MessageDownload", message_id,
10825 &response, &xml_stream, &xml_stream_length,
10826 &code, &status_message);
10827 if (err) goto leave;
10829 /* Extract data */
10830 xpath_ctx = xmlXPathNewContext(response);
10831 if (!xpath_ctx) {
10832 err = IE_ERROR;
10833 goto leave;
10835 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10836 err = IE_ERROR;
10837 goto leave;
10839 result = xmlXPathEvalExpression(
10840 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10841 xpath_ctx);
10842 if (!result) {
10843 err = IE_ERROR;
10844 goto leave;
10846 /* Empty response */
10847 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10848 char *message_id_locale = _isds_utf82locale((char*) message_id);
10849 isds_printf_message(context,
10850 _("Server did not return any message for ID `%s' "
10851 "on MessageDownload request"), message_id_locale);
10852 free(message_id_locale);
10853 err = IE_ISDS;
10854 goto leave;
10856 /* More messages */
10857 if (result->nodesetval->nodeNr > 1) {
10858 char *message_id_locale = _isds_utf82locale((char*) message_id);
10859 isds_printf_message(context,
10860 _("Server did return more messages for ID `%s' "
10861 "on MessageDownload request"), message_id_locale);
10862 free(message_id_locale);
10863 err = IE_ISDS;
10864 goto leave;
10866 /* One message */
10867 xpath_ctx->node = result->nodesetval->nodeTab[0];
10869 /* Extract the message */
10870 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10871 if (err) goto leave;
10873 /* Locate raw XML blob */
10874 phys_path = strdup(
10875 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10876 PHYSXML_ELEMENT_SEPARATOR
10877 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10878 PHYSXML_ELEMENT_SEPARATOR
10879 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10881 if (!phys_path) {
10882 err = IE_NOMEM;
10883 goto leave;
10885 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10886 phys_path, &phys_start, &phys_end);
10887 zfree(phys_path);
10888 if (err) {
10889 isds_log_message(context,
10890 _("Substring with isds:MessageDownloadResponse element "
10891 "could not be located in raw SOAP message"));
10892 goto leave;
10894 /* Save XML blob */
10895 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10896 &(*message)->raw_length);*/
10897 /* TODO: Store name space declarations from ancestors */
10898 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10899 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10900 (*message)->raw_length = phys_end - phys_start + 1;
10901 (*message)->raw = malloc((*message)->raw_length);
10902 if (!(*message)->raw) {
10903 err = IE_NOMEM;
10904 goto leave;
10906 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10909 leave:
10910 if (err) {
10911 isds_message_free(message);
10914 free(phys_path);
10916 xmlXPathFreeObject(result);
10917 xmlXPathFreeContext(xpath_ctx);
10919 free(code);
10920 free(status_message);
10921 free(xml_stream);
10922 if (!*message || !(*message)->xml) {
10923 xmlFreeDoc(response);
10926 if (!err)
10927 isds_log(ILF_ISDS, ILL_DEBUG,
10928 _("MessageDownload request processed by server "
10929 "successfully.\n")
10931 #else /* not HAVE_LIBCURL */
10932 err = IE_NOTSUP;
10933 #endif
10934 return err;
10938 /* Load message of any type from buffer.
10939 * @context is session context
10940 * @raw_type defines content type of @buffer. Only message types are allowed.
10941 * @buffer is message raw representation. Format (CMS, plain signed,
10942 * message direction) is defined in @raw_type. You can retrieve such data
10943 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10944 * @length is length of buffer in bytes.
10945 * @message is automatically reallocated message parsed from @buffer.
10946 * @strategy selects how buffer will be attached into raw isds_message member.
10947 * */
10948 isds_error isds_load_message(struct isds_ctx *context,
10949 const isds_raw_type raw_type, const void *buffer, const size_t length,
10950 struct isds_message **message, const isds_buffer_strategy strategy) {
10952 isds_error err = IE_SUCCESS;
10953 void *xml_stream = NULL;
10954 size_t xml_stream_length = 0;
10955 message_ns_type message_ns;
10956 xmlDocPtr message_doc = NULL;
10957 xmlXPathContextPtr xpath_ctx = NULL;
10958 xmlXPathObjectPtr result = NULL;
10960 if (!context) return IE_INVALID_CONTEXT;
10961 zfree(context->long_message);
10962 if (!message) return IE_INVAL;
10963 isds_message_free(message);
10964 if (!buffer) return IE_INVAL;
10967 /* Select buffer format and extract XML from CMS*/
10968 switch (raw_type) {
10969 case RAWTYPE_INCOMING_MESSAGE:
10970 message_ns = MESSAGE_NS_UNSIGNED;
10971 xml_stream = (void *) buffer;
10972 xml_stream_length = length;
10973 break;
10975 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10976 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10977 xml_stream = (void *) buffer;
10978 xml_stream_length = length;
10979 break;
10981 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10982 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10983 err = _isds_extract_cms_data(context, buffer, length,
10984 &xml_stream, &xml_stream_length);
10985 if (err) goto leave;
10986 break;
10988 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10989 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10990 xml_stream = (void *) buffer;
10991 xml_stream_length = length;
10992 break;
10994 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10995 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10996 err = _isds_extract_cms_data(context, buffer, length,
10997 &xml_stream, &xml_stream_length);
10998 if (err) goto leave;
10999 break;
11001 default:
11002 isds_log_message(context, _("Bad raw message representation type"));
11003 return IE_INVAL;
11004 break;
11007 if (_isds_sizet2int(xml_stream_length) >= 0) {
11008 isds_log(ILF_ISDS, ILL_DEBUG,
11009 _("Loading message:\n%.*s\nEnd of message\n"),
11010 _isds_sizet2int(xml_stream_length), xml_stream);
11013 /* Convert messages XML stream into XPath context */
11014 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
11015 if (!message_doc) {
11016 err = IE_XML;
11017 goto leave;
11019 xpath_ctx = xmlXPathNewContext(message_doc);
11020 if (!xpath_ctx) {
11021 err = IE_ERROR;
11022 goto leave;
11024 /* XXX: Standard name space for unsigned incoming direction:
11025 * http://isds.czechpoint.cz/v20/
11027 * XXX: Name spaces mangled for signed outgoing direction:
11028 * http://isds.czechpoint.cz/v20/SentMessage:
11030 * <q:MessageDownloadResponse
11031 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
11032 * <q:dmReturnedMessage>
11033 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11034 * <p:dmID>151916</p:dmID>
11035 * ...
11036 * </p:dmDm>
11037 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11038 * ...
11039 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11040 * </q:dmReturnedMessage>
11041 * </q:MessageDownloadResponse>
11043 * XXX: Name spaces mangled for signed incoming direction:
11044 * http://isds.czechpoint.cz/v20/message:
11046 * <q:MessageDownloadResponse
11047 * xmlns:q="http://isds.czechpoint.cz/v20/message">
11048 * <q:dmReturnedMessage>
11049 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11050 * <p:dmID>151916</p:dmID>
11051 * ...
11052 * </p:dmDm>
11053 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11054 * ...
11055 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11056 * </q:dmReturnedMessage>
11057 * </q:MessageDownloadResponse>
11059 * Stupidity of ISDS developers is unlimited */
11060 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
11061 err = IE_ERROR;
11062 goto leave;
11064 result = xmlXPathEvalExpression(
11065 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
11066 xpath_ctx);
11067 if (!result) {
11068 err = IE_ERROR;
11069 goto leave;
11071 /* Empty message */
11072 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11073 isds_printf_message(context,
11074 _("XML document does not contain "
11075 "sisds:dmReturnedMessage element"));
11076 err = IE_ISDS;
11077 goto leave;
11079 /* More messages */
11080 if (result->nodesetval->nodeNr > 1) {
11081 isds_printf_message(context,
11082 _("XML document has more sisds:dmReturnedMessage elements"));
11083 err = IE_ISDS;
11084 goto leave;
11086 /* One message */
11087 xpath_ctx->node = result->nodesetval->nodeTab[0];
11089 /* Extract the message */
11090 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
11091 if (err) goto leave;
11093 /* Append raw buffer into message */
11094 (*message)->raw_type = raw_type;
11095 switch (strategy) {
11096 case BUFFER_DONT_STORE:
11097 break;
11098 case BUFFER_COPY:
11099 (*message)->raw = malloc(length);
11100 if (!(*message)->raw) {
11101 err = IE_NOMEM;
11102 goto leave;
11104 memcpy((*message)->raw, buffer, length);
11105 (*message)->raw_length = length;
11106 break;
11107 case BUFFER_MOVE:
11108 (*message)->raw = (void *) buffer;
11109 (*message)->raw_length = length;
11110 break;
11111 default:
11112 err = IE_ENUM;
11113 goto leave;
11117 leave:
11118 if (err) {
11119 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
11120 isds_message_free(message);
11123 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
11124 xmlXPathFreeObject(result);
11125 xmlXPathFreeContext(xpath_ctx);
11126 if (!*message || !(*message)->xml) {
11127 xmlFreeDoc(message_doc);
11130 if (!err)
11131 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
11132 return err;
11136 /* Determine type of raw message or delivery info according some heuristics.
11137 * It does not validate the raw blob.
11138 * @context is session context
11139 * @raw_type returns content type of @buffer. Valid only if exit code of this
11140 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
11141 * reallocated memory.
11142 * @buffer is message raw representation.
11143 * @length is length of buffer in bytes. */
11144 isds_error isds_guess_raw_type(struct isds_ctx *context,
11145 isds_raw_type *raw_type, const void *buffer, const size_t length) {
11146 isds_error err;
11147 void *xml_stream = NULL;
11148 size_t xml_stream_length = 0;
11149 xmlDocPtr document = NULL;
11150 xmlNodePtr root = NULL;
11152 if (!context) return IE_INVALID_CONTEXT;
11153 zfree(context->long_message);
11154 if (length == 0 || !buffer) return IE_INVAL;
11155 if (!raw_type) return IE_INVAL;
11157 /* Try CMS */
11158 err = _isds_extract_cms_data(context, buffer, length,
11159 &xml_stream, &xml_stream_length);
11160 if (err) {
11161 xml_stream = (void *) buffer;
11162 xml_stream_length = (size_t) length;
11163 err = IE_SUCCESS;
11166 /* Try XML */
11167 document = xmlParseMemory(xml_stream, xml_stream_length);
11168 if (!document) {
11169 isds_printf_message(context,
11170 _("Could not parse data as XML document"));
11171 err = IE_NOTSUP;
11172 goto leave;
11175 /* Get root element */
11176 root = xmlDocGetRootElement(document);
11177 if (!root) {
11178 isds_printf_message(context,
11179 _("XML document is missing root element"));
11180 err = IE_XML;
11181 goto leave;
11184 if (!root->ns || !root->ns->href) {
11185 isds_printf_message(context,
11186 _("Root element does not belong to any name space"));
11187 err = IE_NOTSUP;
11188 goto leave;
11191 /* Test name space */
11192 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
11193 if (xml_stream == buffer)
11194 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
11195 else
11196 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
11197 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
11198 if (xml_stream == buffer)
11199 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
11200 else
11201 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
11202 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
11203 if (xml_stream == buffer)
11204 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
11205 else
11206 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
11207 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
11208 if (xml_stream != buffer) {
11209 isds_printf_message(context,
11210 _("Document in ISDS name space is encapsulated into CMS" ));
11211 err = IE_NOTSUP;
11212 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
11213 *raw_type = RAWTYPE_INCOMING_MESSAGE;
11214 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
11215 *raw_type = RAWTYPE_DELIVERYINFO;
11216 else {
11217 isds_printf_message(context,
11218 _("Unknown root element in ISDS name space"));
11219 err = IE_NOTSUP;
11221 } else {
11222 isds_printf_message(context,
11223 _("Unknown name space"));
11224 err = IE_NOTSUP;
11227 leave:
11228 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
11229 xmlFreeDoc(document);
11230 return err;
11234 /* Download signed incoming/outgoing message identified by ID.
11235 * @context is session context
11236 * @output is true for outgoing message, false for incoming message
11237 * @message_id is message identifier (you can get them from
11238 * isds_get_list_of_{sent,received}_messages())
11239 * @message is automatically reallocated message retrieved from ISDS. The raw
11240 * member will be filled with PKCS#7 structure in DER format. */
11241 static isds_error isds_get_signed_message(struct isds_ctx *context,
11242 const _Bool outgoing, const char *message_id,
11243 struct isds_message **message) {
11245 isds_error err = IE_SUCCESS;
11246 #if HAVE_LIBCURL
11247 xmlDocPtr response = NULL;
11248 xmlChar *code = NULL, *status_message = NULL;
11249 xmlXPathContextPtr xpath_ctx = NULL;
11250 xmlXPathObjectPtr result = NULL;
11251 char *encoded_structure = NULL;
11252 void *raw = NULL;
11253 size_t raw_length = 0;
11254 #endif
11256 if (!context) return IE_INVALID_CONTEXT;
11257 zfree(context->long_message);
11258 if (!message) return IE_INVAL;
11259 isds_message_free(message);
11261 #if HAVE_LIBCURL
11262 /* Do request and check for success */
11263 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
11264 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
11265 BAD_CAST "SignedMessageDownload",
11266 message_id, &response, NULL, NULL, &code, &status_message);
11267 if (err) goto leave;
11269 /* Find signed message, extract it into raw and maybe free
11270 * response */
11271 err = find_extract_signed_data_free_response(context,
11272 (xmlChar *)message_id, &response,
11273 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
11274 BAD_CAST "SignedMessageDownload",
11275 &raw, &raw_length);
11276 if (err) goto leave;
11278 /* Parse message */
11279 err = isds_load_message(context,
11280 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
11281 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
11282 raw, raw_length, message, BUFFER_MOVE);
11283 if (err) goto leave;
11285 raw = NULL;
11287 leave:
11288 if (err) {
11289 isds_message_free(message);
11292 free(encoded_structure);
11293 xmlXPathFreeObject(result);
11294 xmlXPathFreeContext(xpath_ctx);
11295 free(raw);
11297 free(code);
11298 free(status_message);
11299 xmlFreeDoc(response);
11301 if (!err)
11302 isds_log(ILF_ISDS, ILL_DEBUG,
11303 (outgoing) ?
11304 _("SignedSentMessageDownload request processed by server "
11305 "successfully.\n") :
11306 _("SignedMessageDownload request processed by server "
11307 "successfully.\n")
11309 #else /* not HAVE_LIBCURL */
11310 err = IE_NOTSUP;
11311 #endif
11312 return err;
11316 /* Download signed incoming message identified by ID.
11317 * @context is session context
11318 * @message_id is message identifier (you can get them from
11319 * isds_get_list_of_received_messages())
11320 * @message is automatically reallocated message retrieved from ISDS. The raw
11321 * member will be filled with PKCS#7 structure in DER format. */
11322 isds_error isds_get_signed_received_message(struct isds_ctx *context,
11323 const char *message_id, struct isds_message **message) {
11324 return isds_get_signed_message(context, 0, message_id, message);
11328 /* Download signed outgoing message identified by ID.
11329 * @context is session context
11330 * @message_id is message identifier (you can get them from
11331 * isds_get_list_of_sent_messages())
11332 * @message is automatically reallocated message retrieved from ISDS. The raw
11333 * member will be filled with PKCS#7 structure in DER format. */
11334 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
11335 const char *message_id, struct isds_message **message) {
11336 return isds_get_signed_message(context, 1, message_id, message);
11340 /* Get type and name of user who sent a message identified by ID.
11341 * @context is session context
11342 * @message_id is message identifier
11343 * @sender_type is pointer to automatically allocated type of sender detected
11344 * from @raw_sender_type string. If @raw_sender_type is unknown to this
11345 * library or to the server, NULL will be returned. Pass NULL if you don't
11346 * care about it.
11347 * @raw_sender_type is automatically reallocated UTF-8 string describing
11348 * sender type or NULL if not known to server. Pass NULL if you don't care.
11349 * @sender_name is automatically reallocated UTF-8 name of user who sent the
11350 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
11351 isds_error isds_get_message_sender(struct isds_ctx *context,
11352 const char *message_id, isds_sender_type **sender_type,
11353 char **raw_sender_type, char **sender_name) {
11354 isds_error err = IE_SUCCESS;
11355 #if HAVE_LIBCURL
11356 xmlDocPtr response = NULL;
11357 xmlChar *code = NULL, *status_message = NULL;
11358 xmlXPathContextPtr xpath_ctx = NULL;
11359 xmlXPathObjectPtr result = NULL;
11360 char *type_string = NULL;
11361 #endif
11363 if (!context) return IE_INVALID_CONTEXT;
11364 zfree(context->long_message);
11365 if (sender_type) zfree(*sender_type);
11366 if (raw_sender_type) zfree(*raw_sender_type);
11367 if (sender_name) zfree(*sender_name);
11368 if (!message_id) return IE_INVAL;
11370 #if HAVE_LIBCURL
11371 /* Do request and check for success */
11372 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11373 BAD_CAST "GetMessageAuthor",
11374 message_id, &response, NULL, NULL, &code, &status_message);
11375 if (err) goto leave;
11377 /* Extract data */
11378 xpath_ctx = xmlXPathNewContext(response);
11379 if (!xpath_ctx) {
11380 err = IE_ERROR;
11381 goto leave;
11383 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11384 err = IE_ERROR;
11385 goto leave;
11387 result = xmlXPathEvalExpression(
11388 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
11389 if (!result) {
11390 err = IE_ERROR;
11391 goto leave;
11393 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11394 isds_log_message(context,
11395 _("Missing GetMessageAuthorResponse element"));
11396 err = IE_ISDS;
11397 goto leave;
11399 if (result->nodesetval->nodeNr > 1) {
11400 isds_log_message(context,
11401 _("Multiple GetMessageAuthorResponse element"));
11402 err = IE_ISDS;
11403 goto leave;
11405 xpath_ctx->node = result->nodesetval->nodeTab[0];
11406 xmlXPathFreeObject(result); result = NULL;
11408 /* Fill output arguments in */
11409 EXTRACT_STRING("isds:userType", type_string);
11410 if (NULL != type_string) {
11411 if (NULL != sender_type) {
11412 *sender_type = calloc(1, sizeof(**sender_type));
11413 if (NULL == *sender_type) {
11414 err = IE_NOMEM;
11415 goto leave;
11418 err = string2isds_sender_type((xmlChar *)type_string,
11419 *sender_type);
11420 if (err) {
11421 zfree(*sender_type);
11422 if (err == IE_ENUM) {
11423 err = IE_SUCCESS;
11424 char *type_string_locale = _isds_utf82locale(type_string);
11425 isds_log(ILF_ISDS, ILL_WARNING,
11426 _("Unknown isds:userType value: %s"),
11427 type_string_locale);
11428 free(type_string_locale);
11433 if (NULL != sender_name)
11434 EXTRACT_STRING("isds:authorName", *sender_name);
11436 leave:
11437 if (err) {
11438 if (NULL != sender_type) zfree(*sender_type);
11439 zfree(type_string);
11440 if (NULL != sender_name) zfree(*sender_name);
11442 if (NULL != raw_sender_type) *raw_sender_type = type_string;
11444 xmlXPathFreeObject(result);
11445 xmlXPathFreeContext(xpath_ctx);
11447 free(code);
11448 free(status_message);
11449 xmlFreeDoc(response);
11451 if (!err)
11452 isds_log(ILF_ISDS, ILL_DEBUG,
11453 _("GetMessageAuthor request processed by server "
11454 "successfully.\n"));
11455 #else /* not HAVE_LIBCURL */
11456 err = IE_NOTSUP;
11457 #endif
11458 return err;
11462 /* Retrieve hash of message identified by ID stored in ISDS.
11463 * @context is session context
11464 * @message_id is message identifier
11465 * @hash is automatically reallocated message hash downloaded from ISDS.
11466 * Message must exist in system and must not be deleted. */
11467 isds_error isds_download_message_hash(struct isds_ctx *context,
11468 const char *message_id, struct isds_hash **hash) {
11470 isds_error err = IE_SUCCESS;
11471 #if HAVE_LIBCURL
11472 xmlDocPtr response = NULL;
11473 xmlChar *code = NULL, *status_message = NULL;
11474 xmlXPathContextPtr xpath_ctx = NULL;
11475 xmlXPathObjectPtr result = NULL;
11476 #endif
11478 if (!context) return IE_INVALID_CONTEXT;
11479 zfree(context->long_message);
11481 isds_hash_free(hash);
11483 #if HAVE_LIBCURL
11484 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11485 BAD_CAST "VerifyMessage", message_id,
11486 &response, NULL, NULL, &code, &status_message);
11487 if (err) goto leave;
11490 /* Extract data */
11491 xpath_ctx = xmlXPathNewContext(response);
11492 if (!xpath_ctx) {
11493 err = IE_ERROR;
11494 goto leave;
11496 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11497 err = IE_ERROR;
11498 goto leave;
11500 result = xmlXPathEvalExpression(
11501 BAD_CAST "/isds:VerifyMessageResponse",
11502 xpath_ctx);
11503 if (!result) {
11504 err = IE_ERROR;
11505 goto leave;
11507 /* Empty response */
11508 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11509 char *message_id_locale = _isds_utf82locale((char*) message_id);
11510 isds_printf_message(context,
11511 _("Server did not return any response for ID `%s' "
11512 "on VerifyMessage request"), message_id_locale);
11513 free(message_id_locale);
11514 err = IE_ISDS;
11515 goto leave;
11517 /* More responses */
11518 if (result->nodesetval->nodeNr > 1) {
11519 char *message_id_locale = _isds_utf82locale((char*) message_id);
11520 isds_printf_message(context,
11521 _("Server did return more responses for ID `%s' "
11522 "on VerifyMessage request"), message_id_locale);
11523 free(message_id_locale);
11524 err = IE_ISDS;
11525 goto leave;
11527 /* One response */
11528 xpath_ctx->node = result->nodesetval->nodeTab[0];
11530 /* Extract the hash */
11531 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11533 leave:
11534 if (err) {
11535 isds_hash_free(hash);
11538 xmlXPathFreeObject(result);
11539 xmlXPathFreeContext(xpath_ctx);
11541 free(code);
11542 free(status_message);
11543 xmlFreeDoc(response);
11545 if (!err)
11546 isds_log(ILF_ISDS, ILL_DEBUG,
11547 _("VerifyMessage request processed by server "
11548 "successfully.\n")
11550 #else /* not HAVE_LIBCURL */
11551 err = IE_NOTSUP;
11552 #endif
11553 return err;
11557 /* Erase message specified by @message_id from long term storage. Other
11558 * message cannot be erased on user request.
11559 * @context is session context
11560 * @message_id is message identifier.
11561 * @incoming is true for incoming message, false for outgoing message.
11562 * @return
11563 * IE_SUCCESS if message has ben removed
11564 * IE_INVAL if message does not exist in long term storage or message
11565 * belongs to different box
11566 * TODO: IE_NOEPRM if user has no permission to erase a message */
11567 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11568 const char *message_id, _Bool incoming) {
11569 isds_error err = IE_SUCCESS;
11570 #if HAVE_LIBCURL
11571 xmlNodePtr request = NULL, node;
11572 xmlNsPtr isds_ns = NULL;
11573 xmlDocPtr response = NULL;
11574 xmlChar *code = NULL, *status_message = NULL;
11575 #endif
11577 if (!context) return IE_INVALID_CONTEXT;
11578 zfree(context->long_message);
11579 if (NULL == message_id) return IE_INVAL;
11581 #if HAVE_LIBCURL
11582 /* Check if connection is established
11583 * TODO: This check should be done downstairs. */
11584 if (!context->curl) return IE_CONNECTION_CLOSED;
11586 /* Build request */
11587 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11588 if (!request) {
11589 isds_log_message(context,
11590 _("Could build EraseMessage request"));
11591 return IE_ERROR;
11593 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11594 if(!isds_ns) {
11595 isds_log_message(context, _("Could not create ISDS name space"));
11596 xmlFreeNode(request);
11597 return IE_ERROR;
11599 xmlSetNs(request, isds_ns);
11601 err = validate_message_id_length(context, (xmlChar *) message_id);
11602 if (err) goto leave;
11603 INSERT_STRING(request, "dmID", message_id);
11605 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11608 /* Send request */
11609 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11610 "message ID %s to ISDS\n"), message_id);
11611 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11612 xmlFreeNode(request); request = NULL;
11614 if (err) {
11615 isds_log(ILF_ISDS, ILL_DEBUG,
11616 _("Processing ISDS response on EraseMessage request "
11617 "failed\n"));
11618 goto leave;
11621 /* Check for response status */
11622 err = isds_response_status(context, SERVICE_DM_INFO, response,
11623 &code, &status_message, NULL);
11624 if (err) {
11625 isds_log(ILF_ISDS, ILL_DEBUG,
11626 _("ISDS response on EraseMessage request is missing "
11627 "status\n"));
11628 goto leave;
11631 /* Check server status code */
11632 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11633 isds_log_message(context, _("Message to erase belongs to other box"));
11634 err = IE_INVAL;
11635 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11636 isds_log_message(context, _("Message to erase is not saved in "
11637 "long term storage or the direction does not match"));
11638 err = IE_INVAL;
11639 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11640 char *code_locale = _isds_utf82locale((char*) code);
11641 char *message_locale = _isds_utf82locale((char*) status_message);
11642 isds_log(ILF_ISDS, ILL_DEBUG,
11643 _("Server refused EraseMessage request "
11644 "(code=%s, message=%s)\n"),
11645 code_locale, message_locale);
11646 isds_log_message(context, message_locale);
11647 free(code_locale);
11648 free(message_locale);
11649 err = IE_ISDS;
11650 goto leave;
11653 leave:
11654 free(code);
11655 free(status_message);
11656 xmlFreeDoc(response);
11657 xmlFreeNode(request);
11659 if (!err)
11660 isds_log(ILF_ISDS, ILL_DEBUG,
11661 _("EraseMessage request processed by server "
11662 "successfully.\n")
11664 #else /* not HAVE_LIBCURL */
11665 err = IE_NOTSUP;
11666 #endif
11667 return err;
11671 /* Mark message as read. This is a transactional commit function to acknowledge
11672 * to ISDS the message has been downloaded and processed by client properly.
11673 * @context is session context
11674 * @message_id is message identifier. */
11675 isds_error isds_mark_message_read(struct isds_ctx *context,
11676 const char *message_id) {
11678 isds_error err = IE_SUCCESS;
11679 #if HAVE_LIBCURL
11680 xmlDocPtr response = NULL;
11681 xmlChar *code = NULL, *status_message = NULL;
11682 #endif
11684 if (!context) return IE_INVALID_CONTEXT;
11685 zfree(context->long_message);
11687 #if HAVE_LIBCURL
11688 /* Do request and check for success */
11689 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11690 BAD_CAST "MarkMessageAsDownloaded", message_id,
11691 &response, NULL, NULL, &code, &status_message);
11693 free(code);
11694 free(status_message);
11695 xmlFreeDoc(response);
11697 if (!err)
11698 isds_log(ILF_ISDS, ILL_DEBUG,
11699 _("MarkMessageAsDownloaded request processed by server "
11700 "successfully.\n")
11702 #else /* not HAVE_LIBCURL */
11703 err = IE_NOTSUP;
11704 #endif
11705 return err;
11709 /* Mark message as received by recipient. This is applicable only to
11710 * commercial message. Use envelope->dmType message member to distinguish
11711 * commercial message from government message. Government message is
11712 * received automatically (by law), commercial message on recipient request.
11713 * @context is session context
11714 * @message_id is message identifier. */
11715 isds_error isds_mark_message_received(struct isds_ctx *context,
11716 const char *message_id) {
11718 isds_error err = IE_SUCCESS;
11719 #if HAVE_LIBCURL
11720 xmlDocPtr response = NULL;
11721 xmlChar *code = NULL, *status_message = NULL;
11722 #endif
11724 if (!context) return IE_INVALID_CONTEXT;
11725 zfree(context->long_message);
11727 #if HAVE_LIBCURL
11728 /* Do request and check for success */
11729 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11730 BAD_CAST "ConfirmDelivery", message_id,
11731 &response, NULL, NULL, &code, &status_message);
11733 free(code);
11734 free(status_message);
11735 xmlFreeDoc(response);
11737 if (!err)
11738 isds_log(ILF_ISDS, ILL_DEBUG,
11739 _("ConfirmDelivery request processed by server "
11740 "successfully.\n")
11742 #else /* not HAVE_LIBCURL */
11743 err = IE_NOTSUP;
11744 #endif
11745 return err;
11749 /* Send document for authorized conversion into Czech POINT system.
11750 * This is public anonymous service, no log-in necessary. Special context is
11751 * used to reuse keep-a-live HTTPS connection.
11752 * @context is Czech POINT session context. DO NOT use context connected to
11753 * ISDS server. Use new context or context used by this function previously.
11754 * @document is document to convert. Only data, data_length, dmFileDescr and
11755 * is_xml members are significant. Be ware that not all document formats can be
11756 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11757 * @id is reallocated identifier assigned by Czech POINT system to
11758 * your document on submit. Use is to tell it to Czech POINT officer.
11759 * @date is reallocated document submit date (submitted documents
11760 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11761 * value. */
11762 isds_error czp_convert_document(struct isds_ctx *context,
11763 const struct isds_document *document,
11764 char **id, struct tm **date) {
11765 isds_error err = IE_SUCCESS;
11766 #if HAVE_LIBCURL
11767 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11768 xmlNodePtr request = NULL, node;
11769 xmlDocPtr response = NULL;
11771 xmlXPathContextPtr xpath_ctx = NULL;
11772 xmlXPathObjectPtr result = NULL;
11773 long int status = -1;
11774 long int *status_ptr = &status;
11775 char *string = NULL;
11776 #endif
11779 if (!context) return IE_INVALID_CONTEXT;
11780 zfree(context->long_message);
11781 if (!document || !id || !date) return IE_INVAL;
11783 if (document->is_xml) {
11784 isds_log_message(context,
11785 _("XML documents cannot be submitted to conversion"));
11786 return IE_NOTSUP;
11789 /* Free output arguments */
11790 zfree(*id);
11791 zfree(*date);
11793 #if HAVE_LIBCURL
11794 /* Store configuration */
11795 context->type = CTX_TYPE_CZP;
11796 free(context->url);
11797 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11798 if (!(context->url))
11799 return IE_NOMEM;
11801 /* Prepare CURL handle if not yet connected */
11802 if (!context->curl) {
11803 context->curl = curl_easy_init();
11804 if (!(context->curl))
11805 return IE_ERROR;
11808 /* Build conversion request */
11809 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11810 if (!request) {
11811 isds_log_message(context,
11812 _("Could not build Czech POINT conversion request"));
11813 return IE_ERROR;
11815 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11816 if(!deposit_ns) {
11817 isds_log_message(context,
11818 _("Could not create Czech POINT deposit name space"));
11819 xmlFreeNode(request);
11820 return IE_ERROR;
11822 xmlSetNs(request, deposit_ns);
11824 /* Insert children. They are in empty namespace! */
11825 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11826 if(!empty_ns) {
11827 isds_log_message(context, _("Could not create empty name space"));
11828 err = IE_ERROR;
11829 goto leave;
11831 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11832 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11833 document->dmFileDescr);
11835 /* Document encoded in Base64 */
11836 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11837 document->data, document->data_length);
11838 if (err) goto leave;
11840 isds_log(ILF_ISDS, ILL_DEBUG,
11841 _("Submitting document for conversion into Czech POINT deposit"));
11843 /* Send conversion request */
11844 err = _czp_czpdeposit(context, request, &response);
11845 xmlFreeNode(request); request = NULL;
11847 if (err) {
11848 czp_do_close_connection(context);
11849 goto leave;
11853 /* Extract response */
11854 xpath_ctx = xmlXPathNewContext(response);
11855 if (!xpath_ctx) {
11856 err = IE_ERROR;
11857 goto leave;
11859 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11860 err = IE_ERROR;
11861 goto leave;
11863 result = xmlXPathEvalExpression(
11864 BAD_CAST "/deposit:saveDocumentResponse/return",
11865 xpath_ctx);
11866 if (!result) {
11867 err = IE_ERROR;
11868 goto leave;
11870 /* Empty response */
11871 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11872 isds_printf_message(context,
11873 _("Missing `return' element in Czech POINT deposit response"));
11874 err = IE_ISDS;
11875 goto leave;
11877 /* More responses */
11878 if (result->nodesetval->nodeNr > 1) {
11879 isds_printf_message(context,
11880 _("Multiple `return' element in Czech POINT deposit response"));
11881 err = IE_ISDS;
11882 goto leave;
11884 /* One response */
11885 xpath_ctx->node = result->nodesetval->nodeTab[0];
11887 /* Get status */
11888 EXTRACT_LONGINT("status", status_ptr, 1);
11889 if (status) {
11890 EXTRACT_STRING("statusMsg", string);
11891 char *string_locale = _isds_utf82locale(string);
11892 isds_printf_message(context,
11893 _("Czech POINT deposit refused document for conversion "
11894 "(code=%ld, message=%s)"),
11895 status, string_locale);
11896 free(string_locale);
11897 err = IE_ISDS;
11898 goto leave;
11901 /* Get document ID */
11902 EXTRACT_STRING("documentID", *id);
11904 /* Get submit date */
11905 EXTRACT_STRING("dateInserted", string);
11906 if (string) {
11907 *date = calloc(1, sizeof(**date));
11908 if (!*date) {
11909 err = IE_NOMEM;
11910 goto leave;
11912 err = _isds_datestring2tm((xmlChar *)string, *date);
11913 if (err) {
11914 if (err == IE_NOTSUP) {
11915 err = IE_ISDS;
11916 char *string_locale = _isds_utf82locale(string);
11917 isds_printf_message(context,
11918 _("Invalid dateInserted value: %s"), string_locale);
11919 free(string_locale);
11921 goto leave;
11925 leave:
11926 free(string);
11927 xmlXPathFreeObject(result);
11928 xmlXPathFreeContext(xpath_ctx);
11930 xmlFreeDoc(response);
11931 xmlFreeNode(request);
11933 if (!err) {
11934 char *id_locale = _isds_utf82locale((char *) *id);
11935 isds_log(ILF_ISDS, ILL_DEBUG,
11936 _("Document %s has been submitted for conversion "
11937 "to server successfully\n"), id_locale);
11938 free(id_locale);
11940 #else /* not HAVE_LIBCURL */
11941 err = IE_NOTSUP;
11942 #endif
11943 return err;
11947 /* Close possibly opened connection to Czech POINT document deposit.
11948 * @context is Czech POINT session context. */
11949 isds_error czp_close_connection(struct isds_ctx *context) {
11950 if (!context) return IE_INVALID_CONTEXT;
11951 zfree(context->long_message);
11952 #if HAVE_LIBCURL
11953 return czp_do_close_connection(context);
11954 #else
11955 return IE_NOTSUP;
11956 #endif
11960 /* Send request for new box creation in testing ISDS instance.
11961 * It's not possible to request for a production box currently, as it
11962 * communicates via e-mail.
11963 * XXX: This function does not work either. Server complains about invalid
11964 * e-mail address.
11965 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11966 * this function
11967 * @context is special session context for box creation request. DO NOT use
11968 * standard context as it could reveal your password. Use fresh new context or
11969 * context previously used by this function.
11970 * @box is box description to create including single primary user (in case of
11971 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
11972 * ignored. It outputs box ID assigned by ISDS in dbID element.
11973 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11974 * box, or contact address of PFO box owner). The email member is mandatory as
11975 * it will be used to deliver credentials.
11976 * @former_names is former name of box owner. Pass NULL if you don't care.
11977 * @approval is optional external approval of box manipulation
11978 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11979 * NULL, if you don't care.*/
11980 isds_error isds_request_new_testing_box(struct isds_ctx *context,
11981 struct isds_DbOwnerInfo *box, const struct isds_list *users,
11982 const char *former_names, const struct isds_approval *approval,
11983 char **refnumber) {
11984 isds_error err = IE_SUCCESS;
11985 #if HAVE_LIBCURL
11986 xmlNodePtr request = NULL;
11987 xmlDocPtr response = NULL;
11988 xmlXPathContextPtr xpath_ctx = NULL;
11989 xmlXPathObjectPtr result = NULL;
11990 #endif
11993 if (!context) return IE_INVALID_CONTEXT;
11994 zfree(context->long_message);
11995 if (!box) return IE_INVAL;
11997 #if HAVE_LIBCURL
11998 if (!box->email || box->email[0] == '\0') {
11999 isds_log_message(context, _("E-mail field is mandatory"));
12000 return IE_INVAL;
12003 /* Scratch box ID */
12004 zfree(box->dbID);
12006 /* Store configuration */
12007 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
12008 free(context->url);
12009 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
12010 if (!(context->url))
12011 return IE_NOMEM;
12013 /* Prepare CURL handle if not yet connected */
12014 if (!context->curl) {
12015 context->curl = curl_easy_init();
12016 if (!(context->curl))
12017 return IE_ERROR;
12020 /* Build CreateDataBox request */
12021 err = build_CreateDBInput_request(context,
12022 &request, BAD_CAST "CreateDataBox",
12023 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
12024 if (err) goto leave;
12026 /* Send it to server and process response */
12027 err = send_destroy_request_check_response(context,
12028 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
12029 &response, (xmlChar **) refnumber, NULL);
12030 if (err) goto leave;
12032 /* Extract box ID */
12033 xpath_ctx = xmlXPathNewContext(response);
12034 if (!xpath_ctx) {
12035 err = IE_ERROR;
12036 goto leave;
12038 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
12039 err = IE_ERROR;
12040 goto leave;
12042 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
12044 leave:
12045 xmlXPathFreeObject(result);
12046 xmlXPathFreeContext(xpath_ctx);
12047 xmlFreeDoc(response);
12048 xmlFreeNode(request);
12050 if (!err) {
12051 isds_log(ILF_ISDS, ILL_DEBUG,
12052 _("CreateDataBox request processed by server successfully.\n"));
12054 #else /* not HAVE_LIBCURL */
12055 err = IE_NOTSUP;
12056 #endif
12058 return err;
12062 /* Submit CMS signed message to ISDS to verify its originality. This is
12063 * stronger form of isds_verify_message_hash() because ISDS does more checks
12064 * than simple one (potentialy old weak) hash comparison.
12065 * @context is session context
12066 * @message is memory with raw CMS signed message bit stream
12067 * @length is @message size in bytes
12068 * @return
12069 * IE_SUCCESS if message originates in ISDS
12070 * IE_NOTEQUAL if message is unknown to ISDS
12071 * other code for other errors */
12072 isds_error isds_authenticate_message(struct isds_ctx *context,
12073 const void *message, size_t length) {
12074 isds_error err = IE_SUCCESS;
12075 #if HAVE_LIBCURL
12076 xmlNsPtr isds_ns = NULL;
12077 xmlNodePtr request = NULL;
12078 xmlDocPtr response = NULL;
12079 xmlXPathContextPtr xpath_ctx = NULL;
12080 xmlXPathObjectPtr result = NULL;
12081 _Bool *authentic = NULL;
12082 #endif
12084 if (!context) return IE_INVALID_CONTEXT;
12085 zfree(context->long_message);
12086 if (!message || length == 0) return IE_INVAL;
12088 #if HAVE_LIBCURL
12089 /* Check if connection is established
12090 * TODO: This check should be done downstairs. */
12091 if (!context->curl) return IE_CONNECTION_CLOSED;
12094 /* Build AuthenticateMessage request */
12095 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
12096 if (!request) {
12097 isds_log_message(context,
12098 _("Could not build AuthenticateMessage request"));
12099 return IE_ERROR;
12101 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
12102 if(!isds_ns) {
12103 isds_log_message(context, _("Could not create ISDS name space"));
12104 xmlFreeNode(request);
12105 return IE_ERROR;
12107 xmlSetNs(request, isds_ns);
12109 /* Insert Base64 encoded message */
12110 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
12111 message, length);
12112 if (err) goto leave;
12114 /* Send request to server and process response */
12115 err = send_destroy_request_check_response(context,
12116 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
12117 &response, NULL, NULL);
12118 if (err) goto leave;
12121 /* ISDS has decided */
12122 xpath_ctx = xmlXPathNewContext(response);
12123 if (!xpath_ctx) {
12124 err = IE_ERROR;
12125 goto leave;
12127 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
12128 err = IE_ERROR;
12129 goto leave;
12132 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
12134 if (!authentic) {
12135 isds_log_message(context,
12136 _("Server did not return any response on "
12137 "AuthenticateMessage request"));
12138 err = IE_ISDS;
12139 goto leave;
12141 if (*authentic) {
12142 isds_log(ILF_ISDS, ILL_DEBUG,
12143 _("ISDS authenticated the message successfully\n"));
12144 } else {
12145 isds_log_message(context, _("ISDS does not know the message"));
12146 err = IE_NOTEQUAL;
12150 leave:
12151 free(authentic);
12152 xmlXPathFreeObject(result);
12153 xmlXPathFreeContext(xpath_ctx);
12155 xmlFreeDoc(response);
12156 xmlFreeNode(request);
12157 #else /* not HAVE_LIBCURL */
12158 err = IE_NOTSUP;
12159 #endif
12161 return err;
12165 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
12166 * including adding new CMS time stamp. Only CMS blobs without time stamp can
12167 * be re-signed.
12168 * @context is session context
12169 * @input_data is memory with raw CMS signed message or delivery info bit
12170 * stream to re-sign
12171 * @input_length is @input_data size in bytes
12172 * @output_data is pointer to auto-allocated memory where to store re-signed
12173 * input data blob. Caller must free it.
12174 * @output_data is pointer where to store @output_data size in bytes
12175 * @valid_to is pointer to auto-allocated date of time stamp expiration.
12176 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
12177 * @return
12178 * IE_SUCCESS if CMS blob has been re-signed successfully
12179 * other code for other errors */
12180 isds_error isds_resign_message(struct isds_ctx *context,
12181 const void *input_data, size_t input_length,
12182 void **output_data, size_t *output_length, struct tm **valid_to) {
12183 isds_error err = IE_SUCCESS;
12184 #if HAVE_LIBCURL
12185 xmlNsPtr isds_ns = NULL;
12186 xmlNodePtr request = NULL;
12187 xmlDocPtr response = NULL;
12188 xmlXPathContextPtr xpath_ctx = NULL;
12189 xmlXPathObjectPtr result = NULL;
12190 char *string = NULL;
12191 const xmlChar *codes[] = {
12192 BAD_CAST "2200",
12193 BAD_CAST "2201",
12194 BAD_CAST "2204",
12195 BAD_CAST "2207",
12196 NULL
12198 const char *meanings[] = {
12199 "Message is bad",
12200 "Message is not original",
12201 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
12202 "Time stamp could not been generated in time"
12204 const isds_error errors[] = {
12205 IE_INVAL,
12206 IE_NOTUNIQ,
12207 IE_INVAL,
12208 IE_ISDS,
12210 struct code_map_isds_error map = {
12211 .codes = codes,
12212 .meanings = meanings,
12213 .errors = errors
12215 #endif
12217 if (NULL != output_data) *output_data = NULL;
12218 if (NULL != output_length) *output_length = 0;
12219 if (NULL != valid_to) *valid_to = NULL;
12221 if (NULL == context) return IE_INVALID_CONTEXT;
12222 zfree(context->long_message);
12223 if (NULL == input_data || 0 == input_length) {
12224 isds_log_message(context, _("Empty CMS blob on input"));
12225 return IE_INVAL;
12227 if (NULL == output_data || NULL == output_length) {
12228 isds_log_message(context,
12229 _("NULL pointer provided for output CMS blob"));
12230 return IE_INVAL;
12233 #if HAVE_LIBCURL
12234 /* Check if connection is established
12235 * TODO: This check should be done downstairs. */
12236 if (!context->curl) return IE_CONNECTION_CLOSED;
12239 /* Build Re-signISDSDocument request */
12240 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
12241 if (!request) {
12242 isds_log_message(context,
12243 _("Could not build Re-signISDSDocument request"));
12244 return IE_ERROR;
12246 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
12247 if(!isds_ns) {
12248 isds_log_message(context, _("Could not create ISDS name space"));
12249 xmlFreeNode(request);
12250 return IE_ERROR;
12252 xmlSetNs(request, isds_ns);
12254 /* Insert Base64 encoded CMS blob */
12255 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
12256 input_data, input_length);
12257 if (err) goto leave;
12259 /* Send request to server and process response */
12260 err = send_destroy_request_check_response(context,
12261 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
12262 &response, NULL, &map);
12263 if (err) goto leave;
12266 /* Extract re-signed data */
12267 xpath_ctx = xmlXPathNewContext(response);
12268 if (!xpath_ctx) {
12269 err = IE_ERROR;
12270 goto leave;
12272 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
12273 err = IE_ERROR;
12274 goto leave;
12276 result = xmlXPathEvalExpression(
12277 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
12278 if (!result) {
12279 err = IE_ERROR;
12280 goto leave;
12282 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
12283 isds_log_message(context,
12284 _("Missing Re-signISDSDocumentResponse element"));
12285 err = IE_ISDS;
12286 goto leave;
12288 if (result->nodesetval->nodeNr > 1) {
12289 isds_log_message(context,
12290 _("Multiple Re-signISDSDocumentResponse element"));
12291 err = IE_ISDS;
12292 goto leave;
12294 xpath_ctx->node = result->nodesetval->nodeTab[0];
12295 xmlXPathFreeObject(result); result = NULL;
12297 EXTRACT_STRING("isds:dmResultDoc", string);
12298 /* Decode non-empty data */
12299 if (NULL != string && string[0] != '\0') {
12300 *output_length = _isds_b64decode(string, output_data);
12301 if (*output_length == (size_t) -1) {
12302 isds_log_message(context,
12303 _("Error while Base64-decoding re-signed data"));
12304 err = IE_ERROR;
12305 goto leave;
12307 } else {
12308 isds_log_message(context, _("Server did not send re-signed data"));
12309 err = IE_ISDS;
12310 goto leave;
12312 zfree(string);
12314 if (NULL != valid_to) {
12315 /* Get time stamp expiration date */
12316 EXTRACT_STRING("isds:dmValidTo", string);
12317 if (NULL != string) {
12318 *valid_to = calloc(1, sizeof(**valid_to));
12319 if (!*valid_to) {
12320 err = IE_NOMEM;
12321 goto leave;
12323 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
12324 if (err) {
12325 if (err == IE_NOTSUP) {
12326 err = IE_ISDS;
12327 char *string_locale = _isds_utf82locale(string);
12328 isds_printf_message(context,
12329 _("Invalid dmValidTo value: %s"), string_locale);
12330 free(string_locale);
12332 goto leave;
12337 leave:
12338 free(string);
12340 xmlXPathFreeObject(result);
12341 xmlXPathFreeContext(xpath_ctx);
12343 xmlFreeDoc(response);
12344 xmlFreeNode(request);
12345 #else /* not HAVE_LIBCURL */
12346 err = IE_NOTSUP;
12347 #endif
12349 return err;
12352 #undef INSERT_ELEMENT
12353 #undef CHECK_FOR_STRING_LENGTH
12354 #undef INSERT_STRING_ATTRIBUTE
12355 #undef INSERT_ULONGINTNOPTR
12356 #undef INSERT_ULONGINT
12357 #undef INSERT_LONGINT
12358 #undef INSERT_BOOLEAN
12359 #undef INSERT_SCALAR_BOOLEAN
12360 #undef INSERT_STRING
12361 #undef INSERT_STRING_WITH_NS
12362 #undef EXTRACT_STRING_ATTRIBUTE
12363 #undef EXTRACT_ULONGINT
12364 #undef EXTRACT_LONGINT
12365 #undef EXTRACT_BOOLEAN
12366 #undef EXTRACT_STRING
12369 /* Compute hash of message from raw representation and store it into envelope.
12370 * Original hash structure will be destroyed in envelope.
12371 * @context is session context
12372 * @message is message carrying raw XML message blob
12373 * @algorithm is desired hash algorithm to use */
12374 isds_error isds_compute_message_hash(struct isds_ctx *context,
12375 struct isds_message *message, const isds_hash_algorithm algorithm) {
12376 isds_error err = IE_SUCCESS;
12377 const char *nsuri;
12378 void *xml_stream = NULL;
12379 size_t xml_stream_length;
12380 size_t phys_start, phys_end;
12381 char *phys_path = NULL;
12382 struct isds_hash *new_hash = NULL;
12385 if (!context) return IE_INVALID_CONTEXT;
12386 zfree(context->long_message);
12387 if (!message) return IE_INVAL;
12389 if (!message->raw) {
12390 isds_log_message(context,
12391 _("Message does not carry raw representation"));
12392 return IE_INVAL;
12395 switch (message->raw_type) {
12396 case RAWTYPE_INCOMING_MESSAGE:
12397 nsuri = ISDS_NS;
12398 xml_stream = message->raw;
12399 xml_stream_length = message->raw_length;
12400 break;
12402 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
12403 nsuri = SISDS_INCOMING_NS;
12404 xml_stream = message->raw;
12405 xml_stream_length = message->raw_length;
12406 break;
12408 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
12409 nsuri = SISDS_INCOMING_NS;
12410 err = _isds_extract_cms_data(context,
12411 message->raw, message->raw_length,
12412 &xml_stream, &xml_stream_length);
12413 if (err) goto leave;
12414 break;
12416 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
12417 nsuri = SISDS_OUTGOING_NS;
12418 xml_stream = message->raw;
12419 xml_stream_length = message->raw_length;
12420 break;
12422 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
12423 nsuri = SISDS_OUTGOING_NS;
12424 err = _isds_extract_cms_data(context,
12425 message->raw, message->raw_length,
12426 &xml_stream, &xml_stream_length);
12427 if (err) goto leave;
12428 break;
12430 default:
12431 isds_log_message(context, _("Bad raw representation type"));
12432 return IE_INVAL;
12433 break;
12437 /* XXX: Hash is computed from original string representing isds:dmDm
12438 * subtree. That means no encoding, white space, xmlns attributes changes.
12439 * In other words, input for hash can be invalid XML stream. */
12440 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
12441 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
12442 PHYSXML_ELEMENT_SEPARATOR,
12443 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
12444 PHYSXML_ELEMENT_SEPARATOR
12445 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
12446 err = IE_NOMEM;
12447 goto leave;
12449 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
12450 phys_path, &phys_start, &phys_end);
12451 zfree(phys_path);
12452 if (err) {
12453 isds_log_message(context,
12454 _("Substring with isds:dmDM element could not be located "
12455 "in raw message"));
12456 goto leave;
12460 /* Compute hash */
12461 new_hash = calloc(1, sizeof(*new_hash));
12462 if (!new_hash) {
12463 err = IE_NOMEM;
12464 goto leave;
12466 new_hash->algorithm = algorithm;
12467 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
12468 new_hash);
12469 if (err) {
12470 isds_log_message(context, _("Could not compute message hash"));
12471 goto leave;
12474 /* Save computed hash */
12475 if (!message->envelope) {
12476 message->envelope = calloc(1, sizeof(*message->envelope));
12477 if (!message->envelope) {
12478 err = IE_NOMEM;
12479 goto leave;
12482 isds_hash_free(&message->envelope->hash);
12483 message->envelope->hash = new_hash;
12485 leave:
12486 if (err) {
12487 isds_hash_free(&new_hash);
12490 free(phys_path);
12491 if (xml_stream != message->raw) free(xml_stream);
12492 return err;
12496 /* Compare two hashes.
12497 * @h1 is first hash
12498 * @h2 is another hash
12499 * @return
12500 * IE_SUCCESS if hashes equal
12501 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12502 * IE_ENUM if not comparable, but both structures defined
12503 * IE_INVAL if some of the structures are undefined (NULL)
12504 * IE_ERROR if internal error occurs */
12505 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
12506 if (h1 == NULL || h2 == NULL) return IE_INVAL;
12507 if (h1->algorithm != h2->algorithm) return IE_ENUM;
12508 if (h1->length != h2->length) return IE_ERROR;
12509 if (h1->length > 0 && !h1->value) return IE_ERROR;
12510 if (h2->length > 0 && !h2->value) return IE_ERROR;
12512 for (size_t i = 0; i < h1->length; i++) {
12513 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12514 return IE_NOTEQUAL;
12516 return IE_SUCCESS;
12520 /* Check message has gone through ISDS by comparing message hash stored in
12521 * ISDS and locally computed hash. You must provide message with valid raw
12522 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12523 * This is convenient wrapper for isds_download_message_hash(),
12524 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12525 * @context is session context
12526 * @message is message with valid raw and envelope member; envelope->hash
12527 * member will be changed during function run. Use envelope on heap only.
12528 * @return
12529 * IE_SUCCESS if message originates in ISDS
12530 * IE_NOTEQUAL if message is unknown to ISDS
12531 * other code for other errors */
12532 isds_error isds_verify_message_hash(struct isds_ctx *context,
12533 struct isds_message *message) {
12534 isds_error err = IE_SUCCESS;
12535 struct isds_hash *downloaded_hash = NULL;
12537 if (!context) return IE_INVALID_CONTEXT;
12538 zfree(context->long_message);
12539 if (!message) return IE_INVAL;
12541 if (!message->envelope) {
12542 isds_log_message(context,
12543 _("Given message structure is missing envelope"));
12544 return IE_INVAL;
12546 if (!message->raw) {
12547 isds_log_message(context,
12548 _("Given message structure is missing raw representation"));
12549 return IE_INVAL;
12552 err = isds_download_message_hash(context, message->envelope->dmID,
12553 &downloaded_hash);
12554 if (err) goto leave;
12556 err = isds_compute_message_hash(context, message,
12557 downloaded_hash->algorithm);
12558 if (err) goto leave;
12560 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12562 leave:
12563 isds_hash_free(&downloaded_hash);
12564 return err;
12568 /* Search for document by document ID in list of documents. IDs are compared
12569 * as UTF-8 string.
12570 * @documents is list of isds_documents
12571 * @id is document identifier
12572 * @return first matching document or NULL. */
12573 const struct isds_document *isds_find_document_by_id(
12574 const struct isds_list *documents, const char *id) {
12575 const struct isds_list *item;
12576 const struct isds_document *document;
12578 for (item = documents; item; item = item->next) {
12579 document = (struct isds_document *) item->data;
12580 if (!document) continue;
12582 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12583 return document;
12586 return NULL;
12590 /* Normalize @mime_type to be proper MIME type.
12591 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12592 * guess regular MIME type (e.g. "application/pdf").
12593 * @mime_type is UTF-8 encoded MIME type to fix
12594 * @return original @mime_type if no better interpretation exists, or
12595 * constant static UTF-8 encoded string with proper MIME type. */
12596 const char *isds_normalize_mime_type(const char *mime_type) {
12597 if (!mime_type) return NULL;
12599 for (size_t offset = 0;
12600 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12601 offset += 2) {
12602 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12603 extension_map_mime[offset]))
12604 return (const char *) extension_map_mime[offset + 1];
12607 return mime_type;
12611 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12612 struct isds_message **message);
12613 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12614 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12615 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12616 struct isds_address **address);
12618 int isds_message_free(struct isds_message **message);
12619 int isds_address_free(struct isds_address **address);
12623 /* Makes known all relevant namespaces to given XPath context
12624 * @xpath_ctx is XPath context
12625 * @message_ns selects proper message name space. Unsigned and signed
12626 * messages and delivery info's differ in prefix and URI. */
12627 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12628 const message_ns_type message_ns) {
12629 const xmlChar *message_namespace = NULL;
12631 if (!xpath_ctx) return IE_ERROR;
12633 switch(message_ns) {
12634 case MESSAGE_NS_1:
12635 message_namespace = BAD_CAST ISDS1_NS; break;
12636 case MESSAGE_NS_UNSIGNED:
12637 message_namespace = BAD_CAST ISDS_NS; break;
12638 case MESSAGE_NS_SIGNED_INCOMING:
12639 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12640 case MESSAGE_NS_SIGNED_OUTGOING:
12641 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12642 case MESSAGE_NS_SIGNED_DELIVERY:
12643 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12644 default:
12645 return IE_ENUM;
12648 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12649 return IE_ERROR;
12650 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12651 return IE_ERROR;
12652 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12653 return IE_ERROR;
12654 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12655 return IE_ERROR;
12656 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12657 return IE_ERROR;
12658 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12659 return IE_ERROR;
12660 return IE_SUCCESS;